I have an application which updates my datagrid each time a log file that I'm watching gets updated (Appended with new text) in the following manner:
private void DGAddRow(string name, FunctionType ft) { ASCIIEncoding ascii = new ASCIIEncoding(); CommDGDataSource ds = new CommDGDataSource(); int position = 0; string[] data_split = ft.Data.Split(' '); foreach (AttributeType at in ft.Types) { if (at.IsAddress) { ds.Source = HexString2Ascii(data_split[position]); ds.Destination = HexString2Ascii(data_split[position+1]); break; } else { position += at.Size; } } ds.Protocol = name; ds.Number = rowCount; ds.Data = ft.Data; ds.Time = ft.Time; dataGridRows.Add(ds); rowCount++; } ... private void FileSystemWatcher() { FileSystemWatcher watcher = new FileSystemWatcher(Environment.CurrentDirectory); watcher.Filter = syslogPath; watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName; watcher.Changed += new FileSystemEventHandler(watcher_Changed); watcher.EnableRaisingEvents = true; } private void watcher_Changed(object sender, FileSystemEventArgs e) { if (File.Exists(syslogPath)) { string line = GetLine(syslogPath,currentLine); foreach (CommRuleParser crp in crpList) { FunctionType ft = new FunctionType(); if (crp.ParseLine(line, out ft)) { DGAddRow(crp.Protocol, ft); } } currentLine++; } else MessageBox.Show(UIConstant.COMM_SYSLOG_NON_EXIST_WARNING); }
When the event is raised for the FileWatcher, because it creates a separate thread, when I try to run dataGridRows.Add(ds); to add the new row, the program just crashes without any warning given during debug mode.
In Winforms, this was easily solved by utilizing the Invoke function but I am not sure how to go about this in WPF.
The UI thread queues work items inside an object called a Dispatcher. The Dispatcher selects work items on a priority basis and runs each one to completion. Every UI thread must have at least one Dispatcher, and each Dispatcher can execute work items in exactly one thread.
WPF supports a single-threaded apartment model that has the following rules: One thread runs in the entire application and owns all the WPF objects. WPF elements have thread affinity, in other words other threads can't interact with each other.
So thread affinity means that the thread, in this case the UI thread that instantiates an object is the only thread that can access its members. So for example, dependency object in WPF has thread affinity.
WPF applications start their lives with two threads: one for rendering and another for the UI. The rendering thread runs hidden in the background while the UI thread receives input, handles events, paints the screen, and runs application code. The UI thread queues work items inside an object called a Dispatcher.
You can use
Dispatcher.Invoke(Delegate, object[])
on the Application
's (or any UIElement
's) dispatcher.
You can use it for example like this:
Application.Current.Dispatcher.Invoke(new Action(() => { /* Your code here */ }));
or
someControl.Dispatcher.Invoke(new Action(() => { /* Your code here */ }));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With