WPF Pass TreeView to a backgroundworker's DoWork method - c#

I'm trying to access the header properties of a TreeView control in a background worker's DoWork method.
I have tried the following:
var worker = new BackgroundWorker();
worker.DoWork += DoWork;
worker.RunWorkerAsync(MyTreeView);
private void DoWork(object sender, DoWorkEventArgs e)
{
var x = (e.Argument as TreeView);
var item1 = x.Items[0] as TreeViewItem;
//error here..
var headerItem1 = item1.Header;
}
The error that is thrown says that the property I want to access is owned by another thread (the UI thread in my case).
I have had this problem only with the TreeView control so far. Passing and then accessing less 'complex' controls like Labels or TextBlocks has worked fine.
Thanks for every answer.

The rule is: only access the GUI elements (controls) on the GUI thread.
In a BackgroundWorker, the DoWork event handler will be called on a background thread. You are not allowed to access the GUI elements on that thread. Accessing means reading or writing properties (or indexers) or calling methods.
If you need to do something with a control on a background thread, use Dispatcher.Invoke method. But be warned, that using the Invoke/BeginInvoke methods might impact the overall performance (e.g. when used in a tight loop).
You have to redesign your logic in such a manner that you don't need to access the GUI elements on a background thread. This would be the best solution.
By the way, I would recommend you to move from the BackgroundWorker to the modern asynchronous patterns (async/await & Tasks).

Related

WPF framework using Dispatcher.CurrentDispatcher in background thread, causing memory leak

I'm using a WPF CollectionView and I'm setting the Filter in a background thread, because it takes a long time to apply this filter.
Setting this Filter triggers the method ScheduleMapCleanup() of CollectionView (so WPF framework code I can't change). In this method, Dispatcher.CurrentDispatcher.BeginInvoke is used.
However, because this is executed in a background thread, this Action is never executed (the Dispatcher of this thread is never started), causing a memory leak: The Dispatcher keeps a reference to the CollectionView.
How could I work around this problem? Setting the Filter in the UI thread is not an option.
Could I start the Dispatcher myself? If so, how do I do this (Dispatcher.Run halts everything)?
I use this when I need to update some controls and binding on my UI thread from my background tasks:
Application.Current.Dispatcher.Invoke(
DispatcherPriority.Loaded,
new Action(() => {
// Code here
})
);
If it's not this can you be more specific on what you want to do on your UI thread
Accessing the current dispatcher from a background thread does not give you the UI dispatcher, it gives you a new one for the background thread.
Either call CurrentDispatcher from the foreground thread and pass the result to the background thread, or call DependencyObject.Dispatcher to get the dispatcher for a window or other control.
Edit: I just read the question more closely. Since you do not control the code calling CurrentDispatcher, the only way that it will work is to call that code from the UI thread.
To be clear: I don't use Dispatcher.CurrentDispatcher in my code. This is used in the WPF framework code, so I can't change this.
This code is executed in a background thread because I'm setting the Filter in a background thread. I'm setting this property in a background thread because it can take up to several minutes. Setting it in a background thread keeps the UI responsive and lets me show a loading indication to the user.
I fixed the memory leak (caused by the not-running background Dispatcher keeping a reference to the CollectionView) by adding a Shutdown to the Dispatcher and starting the dispatcher in the background thread:
//All code below is executed on a background thread
//Line below causes WPF framework to add something to Dispatcher.CurrentDispatcher queue.
view.Filter = new Predicate<Object>(actionTarget.FilterCallback);
if (Thread.CurrentThread.IsBackground && Dispatcher.CurrentDispatcher != Application.Current.Dispatcher)
{
Dispatcher.CurrentDispatcher.BeginInvokeShutdown(DispatcherPriority.Background);
Dispatcher.Run();
}
If the background thread is reused later (for example because it's a thread pool thread, started by a BackgroundWorker) you can't use BeginInvokeShutdown like in the code above: a shut down dispatcher can not be started again. In that case, use this instead of the BeginInvokeShutdown:
Dispatcher.CurrentDispatcher.BeginInvoke((Action) delegate() { Dispatcher.ExitAllFrames(); }, DispatcherPriority.Background);
This will make sure the Run() method returns, but the dispatcher can be started again later on.
Edit: As Mitch mentioned in comment below, be carefull when multiple threads can be executing the Run() at the same time. If necessary add a lock around the Run().

How resource efficient is Data Binding vs BeginInvoke (for the purpose of GUI manipulation)

I did a search and this is the closest thing to the question I have in mind
How do I refresh visual control properties (TextBlock.text) set inside a loop?
The example included in that URL is exactly like in my situation, except that I am reading in a constant changing stream of data, and want the changes in values to be reflected in the Windows interface.
I am trying to make the program as efficient as possible, so should I use
(INotifyPropertyChanged + Data Binding)
or following would be better?
Application.Current.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Background,
new Action () => Label1.Content = some_content))
Assuming I perform a lot of buffer/checksum operations using system timers.
WPF, like most other UI frameworks, requires that you modify UI controls from the same thread on which they were created. This thread, often referred to as the "UI thread", is usually the application's main thread, i.e. the one where program execution started.
If you happen to subscribe to the SerialPort.DataReceived event, you are facing a problem, because that event might be triggered on any (background) thread:
The DataReceived event is raised on a secondary thread when data is received from the SerialPort object. Because this event is raised on a secondary thread, and not the main thread, attempting to modify some elements in the main thread, such as UI elements, could raise a threading exception. If it is necessary to modify elements in the main Form or Control, post change requests back using Invoke, which will do the work on the proper thread.
Meaning that inside your event handler, you may not directly manipulate any UI controls.
That is where WPF's Dispatcher.BeginInvoke comes in: It helps you to get some code back on the UI thread. You pass it a delegate, and that delegate will be invoked on the UI thread. Therefore you are allowed to manipulate the UI inside that delegate.
Data binding is no real alternative to BeginInvoke: Data binding also manipulates the UI, based on changes to some data object (or vice versa), but it does not automatically switch to the UI thread. This means that if you're using data binding, you must change the data context only if you're on the UI thread... so you need BeginInvoke anyway.
That all being said, if the use of data binding makes your code simpler and easier to understand, by all means use it! But you might still need Dispatcher.BeginInvoke:
private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
Application.Current.Dispatcher.BeginInvoke(..., () => dataContext.SomeProperty = serialPort...;
}

UI Container in thread?

brothers in code.
I'm trying to make my WinForms app multi threaded. In DoWork of my background worker I've got a method which changes few controls using MethodInvoker delegate. My question is if I have to invoke every control every time I want to change it from another thread or maybe there is some kind of container of controls which I can invoke to avoid multiple invoking certain controls?
Invoking means scheduling your code to run on the thread that owns the controls, which in all straightforward cases would be the very same thread for all of your controls. So while you do have to invoke every time you want to interact with a control, you can in practice "pool" as many interactions as you wish together and only invoke once for the whole piece (doing so will be more performant).
If you want to "hide" the invocations you 'd have to write a class that, when triggered, would detect changes to its properties and use Invoke on code that interacts with your controls in a manner dependent on these properties. So the workflow would be:
Your worker modifies the "controller"'s properties, without invoking. This does not have any immediate effect.
At some point, the controller is "triggered" (perhaps periodically by the worker?).
The controller detects (or already knows) what changes were made to its properties and how these translate to invoking code on controls. It invokes a block of code that interact with the controls accordingly.
My question is if I have to invoke every control every time I want to change it from another
thread or maybe there is some kind of container of controls which I can invoke to avoid multiple
invoking certain controls?
You have to invoke every time yiou want to change the UI. The invoke operation can do more than changing one peroeprty -it can be a complete function udpating 100 controls.
Minimizing invokes is good for performance.
No, there is no predefined container. You are assumed to be an able program an invoke, for example for an anonymous code block, yourself.
Let's say you want to change the text on two Labels. Assuming they belong to the same Form, you can do this either by individual calls to Invoke...
void buttonInvoke_Click(object sender, EventArgs e) {
Invoke((Action)(() => label1.Text = "A1"));
Invoke((Action)(() => label2.Text = "A2"));
}
...or by grouping then in a single Invoke, to save some typing and increase performance.
private void buttonInvoke_Click(object sender, EventArgs e) {
Invoke(
(Action)(() => {
label1.Text = "B1";
label2.Text = "B2";
})
);
}
Technically all of your GUI is created on the main thread, so If you invoke say a main panel on the GUI then within that invocation method you can alter other controls on the GUI all within that method
Plus if your background worker was created on main thread then you can call report progress event back on main thread... Which means invocation not required. Main purpose of background workers really.
public void UpdateControl<T>(T control, Action<T> action) where T : Control
{
if(control.InvokeRequired)
control.Invoke(() => action(control));
else
action(control);
}

Run a modal dialog on a non-UI thread

I'm writing a simple data UI using standard .Net databinding to a typed DataSet from SQL Server.
I have a reload button which calls Fill on all of the DataAdapters to get new data from the database (in case another user changed the data).
This takes some time, during which the UI is frozen. It must be run on the UI thread or the databinding event handlers throw cross-thread exceptions.
I'd like to show a modal "Please Wait" dialog on a background thread (so that it can be animated) while the UI thread connects to the database.
How can I show a modal dialog box on the non-UI thread?
EDIT: I'm aware that best practice is to run the operation in the background, but I can't do that because of the databinding events.
You should do the opposite. Run your long-running process on a background thread and leave the UI thread free to respond to the user actions.
If you want to block any user actions while it is processing you have a number of options, including modal dialogs. Once the background thread completes processing you can inform the main thread about the outcome
The code running in the databinding events need to be decoupled from the UI, probably using some kind of data transfer object.
Then you can run the query operation in a separate thread or a BackgroundWorker, and leave the UI thread as it was.
Edit: The really quick way to fix this is to get the events to run in their own delegate using InvokeRequired and .Invoke. That will give the methods UI context. My co-worker does this like it's going out of style and it annoys me to no end because it's rarely a good idea to do it this way... but if you want a fast solution this will work. (I'm not at work so I don't have a sample with me; I'll try to come up with something.)
Edit 2: I'm not sure what you're asking for is possible. I made a sample app that created a modal dialog in another thread, and it ends up being modeless. Instead of using a modal dialog, could you use some other control or set of controls to indicate progress change, most likely directly on the same form?
using( var frmDialog = new MyPleasWaitDialog() ) {
// data loading is started after the form is shown
frmDialog.Load += (_sender, _e) {
// load data in separate thread
ThreadPool.QueueWorkItem( (_state)=> {
myAdapter.Fill( myDataSet );
// refresh UI components in correct (UI) thread
frmDialog.Invoke( (Action)myDataControl.Refresh );
// close dialog
frmDialog.Invoke( (Action)frmDialog.Close() );
}
}
// shows dialog
frmDialog.ShowDialog( this );
}
Here is an example of using BackgroundWorker to do the loading of data and running a user friendly form to show 'Loading records' or similar...
public void Run()
{
bgWorkrFillDS = new BackgroundWorker();
bgWorkrFillDS.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgWorkrFillDS_RunWorkerCompleted);
bgWorkrFillDS.DoWork += new DoWorkEventHandler(bgWorkrFillDS_DoWork);
bgWorkrFillDS.RunWorkerAsync();
}
void bgWorkrFillDS_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker bgWrkrFillDS = (BackgroundWorker)sender as BackgroundWorker;
if (bgWrkrFillDS != null)
{
// Load up the form that shows a 'Loading....'
// Here we fill in the DS
// someDataSetAdapter.Fill(myDataSet);
}
}
void bgWorkrFillDS_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// Hide or unload the form when the work is done
}
Hope this helps...
Take care,
Tom.
I solved this problem by creating a new DataSet, loading in in the background, then calling DataSet.Merge on the UI thread. Thanks everyone for your advice, which led to this solution.
As an added bonus, this runs much faster than it used to (calling Fill in the background, which only worked with no grids open). Does anyone know why?

Delay loading of combobox when form loads

I've got a Windows Forms (C#) project with multiple comboboxes/listboxes etc that are populated when the form loads.
The problem is that the loading of the comboboxes/listboxes is slow, and since the loading is done when the form is trying to display the entire form isn't shown until all the controls have been populated. This can in some circumstances be 20+ seconds.
Had there been a Form_finished_loaded type of event I could have put my code in there, but I can't find an event that is fired after the form is done drawing the basic controls.
I have one requirement though - the loading has to be done in the main thread (since I get the items from a non-threading friendly COM-application).
I have found one potential solution, but perhaps there is a better way?
I can create a System.Timer.Timer when creating the form, and have the first Tick be called about 1 second later, and then populate the lists from that tick. That gives the form enough time to be displayed before it starts filling the lists.
Does anyone have any other tips on how to delay the loading of the controls?
There is the Shown event that "occurs whenever the form is first displayed.". Also you may want to use the BeginUpdate and EndUpdate functions to make the populating of your combobox faster.
It has that certain smell of workaround, but this approach should fulfil your needs:
private bool _hasInitialized = false;
private void Form1_Shown(object sender, EventArgs e)
{
if (!_hasInitialized)
{
ThreadPool.QueueUserWorkItem(state =>
{
Thread.Sleep(200); // brief sleep to allow the main thread
// to paint the form nicely
this.Invoke((Action)delegate { LoadData(); });
});
}
}
private void LoadData()
{
// do the data loading
_hasInitialized = true;
}
What it does is that it reacts when the form is shown, checks if it has already been initialized before, and if not it spawns a thread that will wait for a brief moment before calling the LoadData method on the main thread. This will allow for the form to get painted properly. The samething could perhaps be achieve by simply calling this.Refresh() but I like the idea of letting the system decide how to do the work.
I would still try to push the data loading onto a worker thread, invoking back on the main thread for populating the UI (if it is at all possible with the COM component).
Can you get your data from a web service that calls the COM component?
That way, you can display empty controls on a Locked form at the start, make Asynchronous calls to get the data, and on return populate the respective combos, and once all of them are loaded, you can unlock the form for the user to use.
You could listen for the VisibleChanged event and the first time it's value is true you put your initialization code.
Isn't FormShown the event you're looking for?
When you say that you cannot use a background thread because of COM what do you mean? I am using many COM components within my apps and running them on background threads.
If you create a new thread as an STAThread you can probably load the ComboBox/ListBox on a Non-UI thread. IIRC the ThreadPool allocates worker threads as MTAThread so you'll need to actually create a thread manually instead of using ThreadPool.QueueUserWorkItem.

Categories

Resources