Async operation showing wrong file - c#

I am trying to add an item to an ObservableCollection while in an aysnc operation. If I run the application, the collection does not have the correct file. If I step through it, I see the correct file does get added, which obviously shows a timing issue. Trouble is I cannot figure out how to fix it. Besides this, everything else works as I expect.
Can someone explain to me what I am doing wrong and how to fix it so the correct filename is written to the ObservableCollection?
private void ChangeFile(INotificationComplete notification)
{
FileInfo currentFileInfo = null;
var destinationImageFilename = string.Empty;
var imageDestinationFolder = Path.Combine(messageBrokerInstance.GetProgramPath("LevelThreeFilesWebLocation", this.SelectedPlatform), "images");
var fileDestinationFolder = Path.Combine(messageBrokerInstance.GetProgramPath("LevelThreeFilesWebLocation", this.SelectedPlatform));
try
{
Task.Factory.StartNew((Action)delegate
{
string[] files = null;
if (directoryInfo.Exists)
{
files = Directory.GetFiles(directoryInfo.FullName, #"*.htm", SearchOption.TopDirectoryOnly);
}
foreach (string file in files)
{
currentFileInfo = new FileInfo(file);
**// bunch of code
// I've found what I want and now am ready to write the file
// and add the filename to the collection the user sees.**
if (writeFile)
{
var fileDestination = Path.Combine(fileDestinationFolder, currentFileInfo.Name);
File.WriteAllLines(webFileDestination, fileArray);
**// Correct file was written but the wrong filename
// is added to the collection.**
// If I step through this, the correct filename is added.
UIDispatcher.Current.BeginInvoke((Action)delegate
{
this.ChangedFiles.Add(currentFileInfo.Name); // ChangedFiles is an ObservableCollection<string>
});
}
}
WaitAnimationNotification offNotification = new WaitAnimationNotification()
{
IsWaitAnimationOn = false,
WaitAnimationMessage = "Please wait while the operation completes..."
};
WaitAnimationNotification waitNotification = notification as WaitAnimationNotification;
if (waitNotification.IsWaitAnimationOn)
{
this.SendMessage("ToggleWaitAnimation", new NotificationEventArgs<WaitAnimationNotification, INotificationComplete>("ToggleWaitAnimation", offNotification));
}
});
}
}

I dont know what UIDispatcher.Current is, but the correct Dispatcher to use is Application.Current.Dispatcher It's easy to spin up a separate dispatcher on a background thread unintentionally - which will give you the behavior you see. Application.Current.Dispatcher is going to be the correct dispatcher for the main application message pump

Related

MessageBox doesn't show Folder path when two keywords are searched, but it shows the folder path when one keyword is searched

try
{
string[] SetupFolderKeywords = {"Setup", "Installed"};
DirectoryInfo SearchedDirectory = new DirectoryInfo(Game.SelectedPath);
FileSystemInfo[] filesAndDirs = SearchedDirectory.GetFileSystemInfos($"*{SetupFolderKeywords[0]}*|*{SetupFolderKeywords[1]}*"); // <-- This doesn't work
// FileSystemInfo[] filesAndDirs = SearchedDirectory.GetFileSystemInfos("*" + SetupFolderKeywords[0] + "*"); <-- This Works
foreach (FileSystemInfo foundFile in filesAndDirs)
{
string FullName = foundFile.FullName;
MessageBox.Show(FullName);
}
}
catch (IOException ExpMoveFolder)
{
MessageBox.Show(Convert.ToString(ExpMoveFolder));
}
I'm trying to look for a folder that has either the keyword "Setup" or "Installed" inside the Game.SelectedPath directory. (I used a FolderBrowserDialog to select this folder) and make a MessageBox appear with its path.
When I try to search for a folder that matches one keyword, the MessageBox appears with the path of the folder. It works great, but when I try to search for keyword "Setup" or "Installed" MessageBox doesn't show at all.
No error messages or warnings appear in visual studio and no program exception occurs when I try to look for either one of the keywords instead of just one keyword.
You can't search for multiple patterns with a single call. Your attempt at a Boolean expression is just interpreted as a single pattern and, of course, there are no entries that match that pattern. If you want to match multiple patterns then you have to make multiple calls. One option might be like this:
var folder = new DirectoryInfo(Game.SelectedPath);
var entries = folder.EnumerateFileSystemInfos(patterns[0]);
for (var i = 1; i < patterns.Length; i++)
{
entries = entries.Concat(folder.EnumerateFileSystemInfos(patterns[i]));
}
foreach (var entry in entries)
{
// Use entry here.
}
EDIT:
I just created this folder:
I then executed this code:
var patterns = new[] { "123", "789" };
var folder = new DirectoryInfo(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "Test"));
var entries = folder.EnumerateFileSystemInfos($"*{patterns[0]}*");
for (var i = 1; i < patterns.Length; i++)
{
entries = entries.Concat(folder.EnumerateFileSystemInfos($"*{patterns[i]}*"));
}
foreach (var entry in entries)
{
Console.WriteLine(entry.Name);
}
That's basically exactly what I posted above except I added wildcards to the EnumerateFileSystemInfos calls where the original code would have required them to be in strings already. This is the output I got:
File123.txt
Folder123
File789.txt
Folder789
I then changed the filters to this:
var patterns = new[] { "456" };
and ran the code again and got this output:
File456.txt
Folder456
Clearly, the code works exactly as it is supposed to and, if what you did didn't work then you did it wrong. If you can't work out what you did wrong, I suggest that you update your question and add the new relevant information.

Appending chat history and insert it into the same file

I have a case that I need to log the chat history (I am capable of doing this already) and I need it to be logged in a text file (able to log it already).
The problem is the file is being accessed all over again so I need to somewhere store the filename of the file somewhere else, right now I have this code:
public async Task LogAsync(IActivity activity)
{
var conversation = "";
var convActivity = "";
var ctr = 0;
conversation = $"From: {activity.From.Name}\r\n To: {activity.Recipient.Name}\r\n Message: {activity.AsMessageActivity()?.Attachments}\r\n ";
fileName = "test";
await LogActivity(fileName, conversation);
}
The LogActivity is the one handling the append of the file. So what I need is I want the unique fileName to be instantiated once while appending the file all over again or rather while continuously accessing this method.
Or is there a way to log the chat history of bot once like if a Context.Done was called or before it?
Or the inefficient way I am thinking of was making use of .From.Name and .Recipient.Name
So the result will be:
if (activity.From.Name.ToLower().ToString() == "user")
{
name.Value = $"{activity.From.Name.ToString()}";
conversation = $"From: {activity.From.Name}\r\n To: {activity.Recipient.Name}\n Message: {activity.AsMessageActivity()?.Text}\n";
}
else
{
name.Value = $"{activity.Recipient.Name.ToString()}";
conversation = $"From: {activity.From.Name}\r\n To: {activity.Recipient.Name}\r\n Message: {activity.AsMessageActivity()?.Text}\r\n ";
}
await LogActivity(name.Value, conversation);
If I understand correctly, you just want to persist a value throughout a conversation; in this case a filename.
If that's correct, then you can store it in PrivateConversationData which lives in thecontext.
For example:
context.PrivateConversationData.SetValue<string>("log_filename", "log-name-here.txt");
For an example, check here: https://www.robinosborne.co.uk/2016/08/08/persisting-data-within-a-conversation-with-botframeworks-dialogs/
For a full example about persisting the whole conversation, this might also help: https://www.robinosborne.co.uk/2016/11/22/transcribing-messages-in-botframework/
Okay, rposbo's answer also works if you have your own logger (that's what I observed, or maybe if you can implement it the other way around, you can use it, it's up to you) that will persist on each every conversation you have with your bot, see the link he provided on how to persist the whole conversation. As for my end, I used dictionary to store the filename. So down below is what I did
public string _Name { get { return name; } }
string name;
public static Dictionary<string, string> fileName = new Dictionary<string, string>();
public void SetFileName(string _fileName)
{
var isCached = fileName.TryGetValue("filename", out name);
if (!isCached)
{
name = $"{_fileName}_{DateTime.Now.Ticks}";
fileName.Add("filename", name);
}
}
Btw, can I accept two answers? since rposbo's answer also works, but it just doesn't fit for me.

Remove a specific line in text file with c#

I'm building an app for windows 8 desktop, I'm reading in a text file and I want to change one specific line but not sure how so what I have is a text file that says
username|false
username|false
username|false
And I want to remove the middle line when something happens, this is what I have so far;
StorageFolder folder = ApplicationData.Current.LocalFolder;
StorageFile storageFile = await folder.GetFileAsync("students.txt");
var text = await Windows.Storage.FileIO.ReadLinesAsync(storageFile);
var list_false = "";
foreach (var line in text)
{
string name = "" + line.Split('|')[0];
string testTaken = "" + line.Split('|')[1];
if (your_name.Text == name)
{
if (testTaken == "false") {
pageTitle.Text = name;
enter_name_grid.Opacity = 0;
questions_grid.Opacity = 1;
var md = new MessageDialog("Enjoy the test");
await md.ShowAsync();
}
else
{
the_name.Text = "You have already taken the test";
var md1 = new MessageDialog("You have already taken the test");
await md1.ShowAsync();
}
return;
}
else
{
list_false = "You're not on the list";
}
}
if (list_false == "You're not on the list") {
var md2 = new MessageDialog("You're not on the list");
await md2.ShowAsync();
}
Help please, it reads in names perfectly and allows them to take the test, I just need it to remove the correct line. Thanks in advance!!
The important thing to consider is that you are modifying a file. So whatever you choose to change then you need to write it back to the file.
In your case you are opting to read the whole file into memory, this actually works in your favor for something like this as you can just remove any unwanted lines and write back to the file. However, you cannot remove an item while you are iterating through the list using a foreach loop.
The best practice for removing items from an array you are looping is to use a for loop and loop in reverse. It also makes it easier to remove items if we work with a List<string> too, like so:
var list = new List<string>(text);
for(int i = text.Length - 1; i >=0; i--)
{
string line = text[i];
//rest of code
}
text = list.ToArray();
The next part of your task is to remove the line. You can do this in your else statement as this is the part that handles users already having taken the test. For example:
the_name.Text = "You have already taken the test";
list.RemoveAt(i);
Finally, after your loop you need to write the whole thing back to the file:
await Windows.Storage.FileIO.WriteLinesAsync(storageFile, text);
When you read the file, you could store the contents in a list. When your "something happens" you could remove the content at the appropriate index and save (overwrite) the list to the file.

Multithreading with Windows Store Applications

We are currently creating a Windows Store Application which gains information from an RSS feed and inputs this information into an ObservableCollection. The issue we are having is when the information is being gained, the Applications UI becomes unresponsive.
In order to get around this, I thought about creating a new thread and calling the method within this. Though, after some research we realised that this was no longer possible in Windows Store Apps. How can we get around this?
The method that collects the information is below.
public void getFeed()
{
setupImages();
string[] feedUrls = new string[] {
"http://www.igadgetos.co.uk/blog/category/gadget-news/feed/",
"http://www.igadgetos.co.uk/blog/category/gadget-reviews/feed/",
"http://www.igadgetos.co.uk/blog/category/videos/feed/",
"http://www.igadgetos.co.uk/blog/category/gaming/feed/",
"http://www.igadgetos.co.uk/blog/category/jailbreak-2/feed/",
"http://www.igadgetos.co.uk/blog/category/kickstarter/feed/",
"http://www.igadgetos.co.uk/blog/category/cars-2/feed/",
"http://www.igadgetos.co.uk/blog/category/software/feed/",
"http://www.igadgetos.co.uk/blog/category/updates/feed/"
};
{
try
{
XNamespace dc = "http://purl.org/dc/elements/1.1/";
XNamespace content = "http://purl.org/rss/1.0/modules/content/";
foreach (var feedUrl in feedUrls)
{
var doc = XDocument.Load(feedUrl);
var feed = doc.Descendants("item").Select(c => new ArticleItem() //Creates a copy of the ArticleItem Class.
{
Title = c.Element("title").Value,
//There are another 4 of these.
Post = stripTags(c.Element(content + "encoded").Value) }
).OrderByDescending(c => c.PubDate);
this.moveItems = feed.ToList();
foreach (var item in moveItems)
{
item.ID = feedItems.Count;
feedItems.Add(item);
}
}
lastUpdated = DateTime.Now;
}
catch
{
MessageDialog popup = new MessageDialog("An error has occured downloading the feed, please try again later.");
popup.Commands.Add(new UICommand("Okay"));
popup.Title = "ERROR";
popup.ShowAsync();
}
}
}
How would we be able to cause the Application to not freeze as we gain this information, as Threading is not possible within Windows Store Applications.
E.g - We planned to use;
Thread newThread = new Thread(getFeed);
newThread.Start
You need to use the well documented async pattern for your operations that happen on the UI thread. The link given by Paul-Jan in the comments is where you need to start. http://msdn.microsoft.com/en-us/library/windows/apps/hh994635.aspx

strange behavior of XamlReader.Load()?

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.

Categories

Resources