Is there a way to find a control's owner thread? - c#

I am having some threading issues with a large app I am working on (getting cross-thread exceptions). Is there a way to find the thread name/id that a particular control was created on?
The error occurs when I try to add a new control to my control's control collection. I can't really create a small, reproducible sample so I will describe it as best as I can.
I have a main control that sits on a form, call it _mainControl. In its constructor I instantiate an instance of another control, something like
ChildControl _childControl = new ChildControl();
Now _childControl exists but I do not add it to _mainControls collection yet.
Eventually, _mainControl receives an event notification that I should add the control. In the event handler I check if this.InvokeRequired and if it is, I Invoke the handler, something like the following:
AddControlEventHander(...)
{
if(InvokeRequired)
{
BeginInvoke(new MethodInvoker(AddControlEventHander);
return;
}
Controls.Add(_childControl);
}
The exception is always thrown at Controls.Add ("Cross-thread operation not valid: Control '_item' accessed from a thread other than the thread it was created on").
Now, what I don't understand is how this is possible. I created _childControl on the same thread that _mainControl was created on. When I look at the threads window while I am debugging, the current thread name/id is the same when I call Control.Add as it was when the _childControl was added. However, the thing that confuses me the most are the following calls from _mainControl:
InvokeReuqired == false;
_childControl.InvokeRequired == false;
_childControl._item.InvokeRequired == true; //I made _item public just to try this and it returns true!
How is that possible? Is it possible for _childControl to be created on one thread while its children are created somehow on another? All of _childControl's children are created during initialization, as is normally done.
If anyone has any tips/suggestions as to what might be going on please let me know.
Thanks.
Edit:
In case anyone is interested, I found out what was happening. I was curious as to how a control can be created on one thread and it's children created on a another thread even though the InitializeComponent was all done on the same thread. So, I found out which thread the child was being created on using code similar to what Charles suggested below. Once I knew that, I at least knew which thread to focus on. Then I broke on the OnHandleCreated event of the child control and found the issue.
One thing I didn't know was that the handle of a control is created when the control is first made visible, not when it is created. So a thread that didn't own the control was trying to set it's visibility to true. So I added a check to see if InvokeRequired and thought that would do the trick. However, something I really didn't expect is that calling InvokeRequired will create the handle of the control if it is not created yet! This in effect causes the control to be created on the wrong thread and always return false for InvokeRequired. I got around this by touching the Handle property of the control so that it is created before InvokeRequired is called.
Thanks for the help guys :)

To get the owner thread for a control, try this:
private Thread GetControlOwnerThread(Control ctrl)
{
if (ctrl.InvokeRequired)
ctrl.BeginInvoke(
new Action<Control>(GetControlOwnerThread),
new object[] {ctrl});
else
return System.Threading.Thread.CurrentThread;
}
Can child controls be on a different thread from the parent (container control)? Yes, it all depends on what thread was running when the control was constructed (new'ed up)
You always have to check InvokeRequired... Because you never know what thread might be calling into the method you are coding... Whether you need to check InvokeRequired separately for each child control, depends on how sure you are that all the controls were created on the same thread or not. If all controls are created when the form is created, in the same initialization routine, then you are probably safe to assume they were all created on the same thread

Related

InvalidOperationException when using Invoke before the form was opened

The exception is this:
System.InvalidOperationException: Invoke or BeginInvoke cannot be
called on a control until the window handle has been created.
First I'll explain the relations in my app.
There's a form named MainForm and another form named AssetsForm. The MainForm is creating an instance of the AssetsForm in the MainForm's constrcutor but doesn't AssetsForm.Show() it yet.
There is class named AssetsSource which implements the IObservable and sends data for display to the AssetsForm which implements the IObserver. When AssetsForm receives data to display, it creates a BackgroundWorker which handles the data and update a TreeView.
I've implemented the following wrong code to handle the UI updates from the BackgroundWorker:
private void Invoke(Control control, Action action)
{
if (control.InvokeRequired)
{
control.BeginInvoke(action);
}
else
{
control.Invoke(action);
}
}
It's wrong because instead of Invoke(action) I should have written action(); But I will refer to this later. Anyway, an InvalidOperationException was thrown from the Invoke(action) line of code. I can infer that the InvokeRequired evaluated to FALSE, although I update the TreeView from a BackgroundWorker !!
In MSDN it is written about Control.Invoke:
The Invoke method searches up the control's parent chain until it
finds a control or form that has a window handle if the current
control's underlying window handle does not exist yet. If no
appropriate handle can be found, the Invoke method will throw an
exception.
What is the parent chain and what is the window handle ? When the window handle is created ? I guess all this had to do with the fact that the AssetsForm is closed.
When I removed that line and uses only action(); as it should be, the program doesn't crash.
When the AssetsForm is opened before the AssetsSource sends updates to AssetsForm, by debugging I can see that InvokeRequired is evaluated to TRUE and BeginInvoke of the TreeView updates itself.
To sum everything up, I don't understand why when the AssetsForm is closed, then the InvokeRequired is false and the UI update (TreeView) is allowed to be from the thread that didn't create the TreeView.
As long as the window is not shown, Winforms doesn't need to stick on the UI-thread mechanismn. Therefore InvokeRequired returns false.
If you call Show() the window is opened and all UI activities need to be run through the event loop and therefore through the UI-thread.
Background: The restriction to handle UI activities only through the main thread is due to the fact that only one (windows) event loop is handling all UI related activities. To ensure that all activities are running in the correct order, all actions need to be run through one thread (at least in winforms). As long as the form is not shown, no events are triggered and therefore there's no need to enforce that all actions are run through the main thread.
EDIT: Add some background description.

c++/cli c# winforms cross-thread operation sometimes possible

I am using c++/cli (should also apply to c#) and creating a winforms program.
I have some lines of code that edit data in a DataGridView. The code is executed by an FileSystemWatcher event, but it could be executed by a background worker or a simple thread. However, it is NOT executed by the UI thread.
The DGV is placed on a tab of a TabControl. When the code is executed while the DGV-containing tab has the focus, the code fails with the well-known and expected exception "Cross-thread operation not valid".
But when another tab has the focus, the code executes smoothly. I assume that the DGV is not updated when it's not shown, causing the code to work well in this case. But this means that the sending of messages like WM_PAINT to the message queue depends on the visibility (shown or not) of the DGV, and in case it is not visible, those messages must be sent when the DGV gets shown again.
Is this correct?
What are the differences in code exection when the DGV is (not) shown?
Your code is fundamentally wrong, but that does not mean that you are guaranteed to be reminded about it. It won't bomb when the control is not visible, no need to update it so no need to do anything that's thread-unsafe so no exception.
You must fix the underlying problem. Very easy to do with the FileSystemWatcher::SynchronizingObject property. Just set it to this in the form constructor. Now the event is automatically raised on the UI thread and you can party on the control properties without being rapped on the knuckles. Fix:
MyForm(void)
{
InitializeComponent();
fileSystemWatcher1->SynchronizingObject = this;
}
With the assumption that you dropped the FSW from the toolbox onto your form. Tweak as needed.

Executing Code behind before Xaml Rendering - How to influence the sequence

I'm working on a Windows Phone App.
I have a very performance intensive method which takes several seconds until the operation is done.
When the method is called I want to show an animated loading symbol which is defined in the xaml of my view. When the operation is finished it should disappear. I set the loading symbol to visible in the first line of this method.In the last line I set the visibility to collapsed.
The problem is that at first the whole code behind will be executed. Unfortunately nothing is to be seen, because the the visibiliy is set to visible after the code behind operations are executed and in the same moment its set to collapsed.
Has anybody an idea how to solve this problem? Thanks so much in advance.
The problem you have is that you're calling your method on the main (UI) thread. This means that your method blocks the UI from refreshing, and also means that (as you noted) by the time the UI does refresh, you've already hidden the icon again.
What you need to do instead is call your method on a background thread (there are a number of ways to handle this). You will need to push the UI update to the UI thread (using Dispatcher.Invoke), but the rest of your method will run on a separate thread.
You'll also need to use a callback of some kind - maybe a custom event - so that your UI thread knows when the background thread is completed.
Without seeing the code its hard to say for sure but if you use the dispatcher to run you intensive code after the busy indicator is set this would allow the ui thread time to change before running the code.
An example
//This assumes you are binding in xaml to the isbusy and it implements INotifyPropertyChanged
IsBusy = true;
Dispatcher.BeginInvoke(()=>{ //...performance intense here
});
That being Said Dan Puzey is right. You should only run this logic on the UI thread if for some reason your need to. even then be wary of this as it makes for a poor ui experience.
One way you could accomplish this and still have your dispatcher fire off when you need would be to pass a copy of the dispatcher into the background.
ThreadPool.QueueUserWorkItem (d => {
//...performance intense here
Dispatcher dispatcher = d as Dispatcher;
if(dispatcher != null){
dispatcher.BeginInvoke()()=>{//...ui updates here }
}
}, Dispatcher.CurrentDispatcher);//make sure this is called from your UI thread or you may not end up with the correct dispatcher

When Something Occurs in a BackgroundWorker, Trigger Code on a Different Thread?

I have a background worker that runs and looks for stuff, and when it finds stuff, I want to update my main WinForm. The issue that I'm having is that when I try to update my WinForm from my background worker, I get errors that tell me I can't modify things that were made outside of my background worker (in other words, everything in my form).
Can someone provide a simple code example of how I can get my code to work the way I want it to? Thanks!
I believe you're looking for the OnProgressChanged Event. More info with example here: http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.onprogresschanged.aspx
If I understand correctly, you want to make a change on the form itself, however you cannot change a control on a form from a thread other than the thread the form was created on. To get around this I use the Form.Invoke() method like so:
public void DoSomething(string myArg)
{
if(InvokeRequired)
{
Invoke(new Action<string>(DoSomething), myArg);
}
else
{
// Do something here
}
}
The InvokeRequired property checks the calling thread to determine if it is the proper thread to make changes to the form, if no the Invoke method moves the call onto the form's window thread.

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