I've got a very strange issue while parsing an external XAML file. The pre-history is that I want to load an external XAML file with content to process. But I want to load as many different files as I want. That happens by unloading the old and loading the new one.
My issue is:
When I load a xaml the first time, everything is good, all as it should be.
But when I load the same xaml the second time, every entry of the object im Loading is there twice. If I run this again, every object is there three times and so on...
To debug the project yourself, download it here. The function starts at line 137 in the file "Control Panel.xaml.cs". I realy don't know what this is. Is it my fault or simply a bug? If yes, is there a workaround?
/// <summary>
/// Load a xaml file and parse it
/// </summary>
public void LoadPresentation()
{
this.Title = "Control Panel - " + System.IO.Path.GetFileName(global.file);
System.IO.FileStream XAML_file = new System.IO.FileStream(global.file, System.IO.FileMode.Open);
try
{
System.IO.StreamReader reader = new System.IO.StreamReader(XAML_file);
string dump = reader.ReadToEnd(); //This is only for debugging purposes because of the strange issue...
XAML_file.Seek(0, System.IO.SeekOrigin.Begin);
presentation = (ResourceDictionary)XamlReader.Load(XAML_file);
//Keys the resourceDictionary must have to be valid
if (presentation["INDEX"] == null || presentation["MAIN_GRID"] == null || presentation["CONTAINER"] == null || presentation["LAYOUTLIST"] == null)
{
throw new Exception();
}
//When this list is loaded, every item in it is there twice or three times or four... Why????
TopicList Index = null;
Index = (TopicList)presentation["INDEX"];
for (int i = 0; i < topics.Count; )
{
topics.RemoveAt(i);
}
foreach (TopicListItem item in Index.Topics)
{
topics.Insert(item.TopicIndex, (Topic)presentation[item.ResourceKey]);
}
lv_topics.SelectedIndex = 0;
selectedIndex = 0;
}
catch
{
System.Windows.Forms.MessageBox.Show("Failed to load XAML file \"" + global.file + "\"", "Parsing Error", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error);
presentation = null;
}
finally
{
XAML_file.Close();
}
}
Edit:
I have tried to serialize the object that was read from the XamlReader and in the output was nowhere any childelement... But if I pull the object out of the dictionary, the children are all there (duplicated and triplicated, but there).
I have already tried to clear the list over
topics.Clear();
and
topics=new ObservableCollection<TopicListItem>();
lv_topics.ItemsSource=topics;
Try Index.Topics.Clear() after loading the Topics into your topics object. That appears to get rid of the duplication.
//When this list is loaded, every item in it is there twice or three times or four... Why????
TopicList Index = null;
Index = (TopicList)presentation["INDEX"];
topics.Clear();
foreach (TopicListItem item in Index.Topics)
{
topics.Insert(item.TopicIndex, (Topic)presentation[item.ResourceKey]);
}
Index.Topics.Clear(); //Adding this will prevent the duplication
lv_topics.SelectedIndex = 0;
selectedIndex = 0;
In the code post topics is not declared in LoadPresentation() so naturally it will have any prior values.
I know you said you tried topics=new ObservableCollection(); but please try again. And put that IN LoadPresentation()
public void LoadPresentation()
{
ObservableCollection<TopicListItem> topics = new ObservableCollection<TopicListItem>()
I would pass filename
public void LoadPresentation(string fileName)
I get you may need to use topics outside LoadPresentation but this is debugging. If you need topics outside the return it.
public ObservableCollection<TopicListItem> LoadPresentation(string fileName)
If that does not fix it I would put a try catch block on the XAML_file.Close(); to see if something weird is not going on.
Related
I am trying to search through a folder with Event Logs in them, eventpath has the path of the specific Event Log I want to access. I want to use a specified RecordID to find it's correlated FormatDescription and display it in a MessageBox. I want to be able to use the eventpath to access each Event Log since I am using 6 separate .evtx files and need to use this method on all of them.
I found this solution, but I get an error when I'm trying to Query. I've tried to find a fix, but it seems as if it's just not going to work for what I need. I commented where exactly it is occurring in the code.
This is the exception: System.Diagnostics.Eventing.Reader.EventLogException: The specified path is invalid.
I can't find a fix for this code, but if anyone knows a fix or another way to approach searching through Event Logs by RecordID and giving the corresponding FormatDescription, it would be greatly appreciated.
I am using C# in Windows Presentation Foundation.
public void getDesc(string recordid)
{
string eventpath = getEventPath();
//takes off the .evtx of the path
string result = eventpath.Substring(0, eventpath.Length - 5);
//result1 is going to be similar to this:
//C:\Users\MyName\AppData\Local\Temp\randomTempDirectory\additional_files\DiagnosticInfo\WindowsEventLogs\Application
string sQuery = "*[System/EventRecordID=" + recordid + "]";
var elQuery = new EventLogQuery(result, PathType.LogName, sQuery);
//this is where it errors out
//error: Specified Channel Path is invalid
using (var elReader = new System.Diagnostics.Eventing.Reader.EventLogReader(elQuery))
{
List<EventRecord> eventList = new List<EventRecord>();
EventRecord eventInstance = elReader.ReadEvent();
try
{
while ((eventInstance = elReader.ReadEvent()) != null)
{
//Access event properties here:
string formatDescription = eventInstance.FormatDescription();
MessageBox.Show(formatDescription);
}
}
finally
{
if (eventInstance != null)
eventInstance.Dispose();
}
}
}
I've created *.txt file with data like this:
deviceid,model,availability,renterID,renterName,reneterSurname,OS
0001,iPhone_5S,false,002,John,Dowland,iOS-7.1.2
0002,GalaxyS3,false,002,Amadeus,Mozart,Android-4.3.2
In my C# app (WPF application) got this function to operate on the file:
private void rentSaveButton_Click(object sender, RoutedEventArgs e)
{
StreamWriter sw = new StreamWriter(#"D:\deviceLib.txt", true);
var currentUser = _list.Where(u => u.Id == int.Parse(userIDTextBox.Text)).FirstOrDefault();
var currentDevice = _list2.Where(i => i.deviceId == int.Parse(deviceIDTextBox.Text)).FirstOrDefault();
if (currentDevice != null && currentUser != null)
{
currentDevice.Availability = false;
currentDevice.rentId = currentUser.Id;
currentDevice.rentName = currentUser.Name;
currentDevice.rentSurname = currentUser.Surname;
dataGridDeviceList.Items.Refresh();
sw.WriteLine();
sw.Close();
MessageBox.Show("Rent done. Thanks!");
tabControl.SelectedItem = mainTab;
}
else
{
MessageBox.Show("We don't have such device. Sorry :( ");
userIDTextBox.Clear();
deviceIDTextBox.Clear();
}
}
Now, the problem is that I can normally work on this file during program work, it changes values, can read it etc but when I turn off app (by X button) nothing happen. TXT file is untouched nor any changes are saved.
You aren't writing anything to the file.
sw.WriteLine(); // you need to pass a string in to this function
It seems to me that you must have invented a theory that any changes you make to currentDevice should, somehow, affect what's written to sw. What you're seeing here is what Karl Popper used to call "disconfirmation": Your theory predicts behavior that can be observed; you observe; the behavior is not happening. The first explanation for that is that your theory is wrong.
And that is in fact the correct explanation for what you're (not) seeing. There is absolutely no connection whatsoever between currentDevice and sw. None. It's like you're pressing buttons on the microwave in an effort to start your car. You'll be walking to work if you don't switch to another approach.
currentDevice.Availability = false;
currentDevice.rentId = currentUser.Id;
currentDevice.rentName = currentUser.Name;
currentDevice.rentSurname = currentUser.Surname;
dataGridDeviceList.Items.Refresh();
sw.WriteLine();
sw.Close();
All you do there is change a bunch of properties on a random object, refresh your data grid, and then write a newline, a newline and nothing else, to the output stream. Why does sw.WriteLine();, with no arguments do that? Well, it does that because that's what the designers decided it would do. There's nothing else it sanely can do, because you're not giving it anything to write. And that behavior is documented, which you would know if you had spent ten seconds reading the documentation.
If you want to write a non-empty line to a file, use one of the many, many overloads of WriteLine which are documented in the documentation. If you want to write just part of a line, use one of the many overloads of Write, which are also documented.
Something like this would be fine (I'm guessing at your field names; if you don't know what they are, maybe somebody else on StackOverflow knows what that part of your code looks like and can help you):
currentDevice.Availability = false;
currentDevice.rentId = currentUser.Id;
currentDevice.rentName = currentUser.Name;
currentDevice.rentSurname = currentUser.Surname;
// Write fields in desired order of appearance in the file.
sw.Write(currentDevice.deviceID);
// There are many far superior ways to write a comma to the file,
// and I'll hear about all of them in comments, but we're keeping
// it as simple as possible for the moment.
sw.Write(",");
sw.Write(currentDevice.model);
sw.Write(",");
// Properties you just set
sw.Write(currentDevice.Availability);
sw.Write(",");
sw.Write(currentDevice.rentID);
sw.Write(",");
sw.Write(currentDevice.rentName);
sw.Write(",");
sw.Write(currentDevice.rentSurname);
sw.Write(",");
sw.Write(currentDevice.OS);
// NOW write a newline.
sw.WriteLine();
But that's ugly. So we'll roll it into a method to hide it. This is called "refactoring".
public void WriteDeviceStateToStream(StreamWriter stream, WhateverTheDeviceClassIs device)
{
// Write fields in desired order of appearance in the file.
stream.Write(device.deviceID);
// There are many far superior ways to write a comma to the file,
// and I'll hear about all of them in comments, but we're keeping
// it as simple as possible for the moment.
stream.Write(",");
stream.Write(device.model);
stream.Write(",");
// Properties you just set
stream.Write(device.Availability);
stream.Write(",");
stream.Write(device.rentID);
stream.Write(",");
stream.Write(device.rentName);
stream.Write(",");
stream.Write(device.rentSurname);
stream.Write(",");
stream.Write(device.OS);
// NOW write a newline.
stream.WriteLine();
// DO NOT close the stream here. The stream belongs to the caller; make no
// assumptions about what he plans to do with it next.
}
...and in your event handler, call that method. Also note using statement for the StreamWriter. That disposes of it properly, which closes the file and so on. That's important.
private void rentSaveButton_Click(object sender, RoutedEventArgs e)
{
using (StreamWriter sw = new StreamWriter(#"D:\deviceLib.txt", true))
{
var currentUser = _list.Where(u => u.Id == int.Parse(userIDTextBox.Text)).FirstOrDefault();
var currentDevice = _list2.Where(i => i.deviceId == int.Parse(deviceIDTextBox.Text)).FirstOrDefault();
if (currentDevice != null && currentUser != null)
{
currentDevice.Availability = false;
currentDevice.rentId = currentUser.Id;
currentDevice.rentName = currentUser.Name;
currentDevice.rentSurname = currentUser.Surname;
dataGridDeviceList.Items.Refresh();
// Call write method
WriteDeviceStateToStream(sw, currentDevice);
// The user's going to get real tired of this messagebox real fast.
MessageBox.Show("Rent done. Thanks!");
tabControl.SelectedItem = mainTab;
}
else
{
MessageBox.Show("We don't have such device. Sorry :( ");
userIDTextBox.Clear();
deviceIDTextBox.Clear();
}
}
}
I am trying to loop through all shapes in a document and check their "Alternate Text" which has had the source filename for that image recorded as it's alternate text. I need to read specific source images and convert them to a different image format.
I am able to get to the point of reading the AlternateText of the shape but it throws an exception:
'((Microsoft.Office.Interop.Word.Shape)(s)).AlternativeText' threw an exception of type 'System.Runtime.InteropServices.COMException'
When I set a breakpoint and view the "s" object, the majority of properties are throwing this exception, however some are not, for example I can read the LinkFormat property and a few others without issue, but the majority of properties throw an error.
Here is the code I am using:
Word.Application WordApp = new Word.Application();
d = WordApp.Documents.Open(#strFilename, ReadOnly: true, Visible: false);
int iReplacements = 0;
int iReplacementNoLink = 0;
foreach (Word.Shape s in d.Shapes)
{
Application.DoEvents();
try
{
if (s.LinkFormat.SourceName.ToString().Contains(".eps") || s.LinkFormat.SourceName.ToString().Contains(".png"))
{
iReplacements++;
}
if (s.AlternativeText != "")
{
iReplacementNoLink++;
}
}
catch (Exception fff)
{
Console.Write(fff);
}
}
The if statement checking the s.AlternateText always ends up in the catch.
I am using Visual Studio 2013 and I have Office 2007, I am not sure if that is relevant or not.
Can anyone tell me what I need to do to be able to read the Alternate Text of the shapes? If I am going about it in the wrong way or need to include a library or if I need to upgrade VS or Office? It seems like it should be really straight forward.
Thank you for any assistance you can provide.
I am unsure why this worked, but I was able to resolve this issue by using the "Select" method of the shape. Once the shape is selected the majority of the properties that previously were throwing errors are populated. There are still approximately 20 properties that thow the error, but I am now able to access things like "AlternativeText","Name","Callout" which were previously throwing the error.
Word.Application WordApp = new Word.Application();
d = WordApp.Documents.Open(#strFilename, ReadOnly: true, Visible: false);
int iReplacements = 0;
int iReplacementNoLink = 0;
foreach (Word.Shape s in d.Shapes)
{
Application.DoEvents();
try
{
//if (s.Type == Microsoft.Office.Core.MsoShapeType.msoLinkedPicture)
if (s.LinkFormat.SourceName.ToString().Contains(".eps") || s.LinkFormat.SourceName.ToString().Contains(".png"))
{
iReplacements++;
}
s.Select();
if (s.AlternativeText != "" && s.AlternativeText != null)
{
iReplacementNoLink++;
}
}
catch (Exception fff)
{
Console.Write(fff);
}
}
I can't sort this weird issue out and I have tried anything and everything I can think of.
I got 5 pages, everyone of them passing variables with navigation this way:
Pass:
NavigationSerice.Navigate(new Uri("/myPage.xaml?key=" + myVariable, UriKind.Relative));
Retrieve:
If (NavigationContext.QueryString.ContainsKey(myKey))
{
String retrievedVariable = NavigationContext.QueryString["myKey"].toString();
}
I open a list on many pages and one of the pages automatically deletes an item from the list actualProject (actualProject is a variable for a string list). Then, when I go so far back that I reach a specific page - the app throws an exception. Why? I have no idea.
The code that deletes the item:
// Remove the active subject from the availible subjects
unlinkedSubjects.Remove(actualSubject);
unlinkedsubjectsListBox.ItemsSource = null;
unlinkedsubjectsListBox.ItemsSource = unlinkedSubjects;
Then the page that throws the exception's OnNavigatedTo event:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (NavigationContext.QueryString.ContainsKey("key"))
{
actualProject = NavigationContext.QueryString["key"];
try
{
//Read subjectList from IsolatedStorage
subjectList = readSetting(actualProject) != null ? (List<String>)readSetting(actualProject) : new List<String>();
//Put the subjectList into the subjectListBox
subjectListBox.ItemsSource = subjectList;
//Set the subjectsPageTitle to the "actualProject" value, to display the name of the current open project at the top of the screen
subjectsPageTitle.Text = actualProject;
}
catch (Exception)
{
if (language.Equals("en."))
{
// Language is set to english
MessageBox.Show("Couldn't open the project, please try again or please report the error to Accelerated Code - details on the about page");
}
else if (language.Equals("no."))
{
// Language is set to norwegian
MessageBox.Show("Kunne ikke åpne prosjektet, vennligst prøv igjen eller rapporter problemet til Accelerated Code - du finner detaljer på om-siden");
}
}
}
}
Exception:
_exception {System.ArgumentException: Value does not fall within the expected range.} System.Exception {System.ArgumentException}
My theory:
The app kind of loads the currently opened and modified List. Is that possible? No idea.
So there are a number of ways to pass data between pages.
The way you have chosen is the least suggested.
You can use the PhoneApplicationService.Current dictionary but this is messy also if you have a ton of variables, doesn't persist after app shut down and could be simplified.
I wrote a free DLL that kept this exact scenario in mind called EZ_iso.
You can find it here
Basically what you would do to use it is this.
[DataContractAttribute]
public class YourPageVars{
[DataMember]
public Boolean Value1 = false;
[DataMember]
public String Value2 = "And so on";
[DataMember]
public List<String> MultipleValues;
}
Once you have your class setup you can pass it easily between pages
YourPageVars vars = new YourPageVars { /*Set all your values*/ };
//Now we save it
EZ_iso.IsolatedStorageAccess.SaveFile("PageVars",vars);
That's it! Now you can navigate and retrieve the file.
YourPageVars vars = (YourPageVars)EZ_iso.IsolatedStorageAccess.GetFile("PageVars",typeof(YorPageVars));
This is nice because you can use it for more than navigation. You can use it for anything that would require Isolated storage. This data is serialized to the device now so even if the app shuts down it will remain. You can of course always delete the file if you choose as well.
Please make sure to refer to the documentation for any exceptions you have. If you still need help feel free to hit me up on twitter #Anth0nyRussell or amr#AnthonyRussell.info
I'm building a console application that have to process a bunch of document.
To stay simple, the process is :
for each year between X and Y, query the DB to get a list of document reference to process
for each of this reference, process a local file
The process method is, I think, independent and should be parallelized as soon as input args are different :
private static bool ProcessDocument(
DocumentsDataset.DocumentsRow d,
string langCode
)
{
try
{
var htmFileName = d.UniqueDocRef.Trim() + langCode + ".htm";
var htmFullPath = Path.Combine("x:\path", htmFileName;
missingHtmlFile = !File.Exists(htmFullPath);
if (!missingHtmlFile)
{
var html = File.ReadAllText(htmFullPath);
// ProcessHtml is quite long : it use a regex search for a list of reference
// which are other documents, then sends the result to a custom WS
ProcessHtml(ref html);
File.WriteAllText(htmFullPath, html);
}
return true;
}
catch (Exception exc)
{
Trace.TraceError("{0,8}Fail processing {1} : {2}","[FATAL]", d.UniqueDocRef, exc.ToString());
return false;
}
}
In order to enumerate my document, I have this method :
private static IEnumerable<DocumentsDataset.DocumentsRow> EnumerateDocuments()
{
return Enumerable.Range(1990, 2020 - 1990).AsParallel().SelectMany(year => {
return Document.FindAll((short)year).Documents;
});
}
Document is a business class that wrap the retrieval of documents. The output of this method is a typed dataset (I'm returning the Documents table). The method is waiting for a year and I'm sure a document can't be returned by more than one year (year is part of the key actually).
Note the use of AsParallel() here, but I never got issue with this one.
Now, my main method is :
var documents = EnumerateDocuments();
var result = documents.Select(d => {
bool success = true;
foreach (var langCode in new string[] { "-e","-f" })
{
success &= ProcessDocument(d, langCode);
}
return new {
d.UniqueDocRef,
success
};
});
using (var sw = File.CreateText("summary.csv"))
{
sw.WriteLine("Level;UniqueDocRef");
foreach (var item in result)
{
string level;
if (!item.success) level = "[ERROR]";
else level = "[OK]";
sw.WriteLine(
"{0};{1}",
level,
item.UniqueDocRef
);
//sw.WriteLine(item);
}
}
This method works as expected under this form. However, if I replace
var documents = EnumerateDocuments();
by
var documents = EnumerateDocuments().AsParrallel();
It stops to work, and I don't understand why.
The error appears exactly here (in my process method):
File.WriteAllText(htmFullPath, html);
It tells me that the file is already opened by another program.
I don't understand what can cause my program not to works as expected. As my documents variable is an IEnumerable returning unique values, why my process method is breaking ?
thx for advises
[Edit] Code for retrieving document :
/// <summary>
/// Get all documents in data store
/// </summary>
public static DocumentsDS FindAll(short? year)
{
Database db = DatabaseFactory.CreateDatabase(connStringName); // MS Entlib
DbCommand cm = db.GetStoredProcCommand("Document_Select");
if (year.HasValue) db.AddInParameter(cm, "Year", DbType.Int16, year.Value);
string[] tableNames = { "Documents", "Years" };
DocumentsDS ds = new DocumentsDS();
db.LoadDataSet(cm, ds, tableNames);
return ds;
}
[Edit2] Possible source of my issue, thanks to mquander. If I wrote :
var test = EnumerateDocuments().AsParallel().Select(d => d.UniqueDocRef);
var testGr = test.GroupBy(d => d).Select(d => new { d.Key, Count = d.Count() }).Where(c=>c.Count>1);
var testLst = testGr.ToList();
Console.WriteLine(testLst.Where(x => x.Count == 1).Count());
Console.WriteLine(testLst.Where(x => x.Count > 1).Count());
I get this result :
0
1758
Removing the AsParallel returns the same output.
Conclusion : my EnumerateDocuments have something wrong and returns twice each documents.
Have to dive here I think
This is probably my source enumeration in cause
I suggest you to have each task put the file data into a global queue and have a parallel thread take writing requests from the queue and do the actual writing.
Anyway, the performance of writing in parallel on a single disk is much worse than writing sequentially, because the disk needs to spin to seek the next writing location, so you are just bouncing the disk around between seeks. It's better to do the writes sequentially.
Is Document.FindAll((short)year).Documents threadsafe? Because the difference between the first and the second version is that in the second (broken) version, this call is running multiple times concurrently. That could plausibly be the cause of the issue.
Sounds like you're trying to write to the same file. Only one thread/program can write to a file at a given time, so you can't use Parallel.
If you're reading from the same file, then you need to open the file with only read permissions as not to put a write lock on it.
The simplest way to fix the issue is to place a lock around your File.WriteAllText, assuming the writing is fast and it's worth parallelizing the rest of the code.