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.
Related
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.
Is there any way I can pause all UI Update commands in Winforms?
Or I have a slight feeling I'm trying to go about this the completely wrong way, so is there another way around my problem: I basically load a saved state of a control, which loads new controls all over it. However I do some of this in the UI thread, and some of the data loading from another thread, which then fills the UI.
So the effect I have when it is loading is that the user can see a few of the controls appearing in one place, then moving to another place on the form, changing values, etc.
I'd like to get a loading screen instead of this and load the controls in the background. It's quite a large application and its not THAT important so redesigning my code isn't really an option.
Can I simply stop all Update() commands on a control while a method is executing?
You can use the SuspendLayout and ResumeLayout methods to wrap the setup of UI in one operation (without the update of the rendering).
Basically (assuming SomeMethod is in the form class):
private void SomeMethod()
{
this.SuspendLayout();
// all UI setup
this.ResumeLayout();
}
it really depends on your form logic, in general you should not overload the Load or Show method with too much things so that the form can be shown and drawn quickly and always look responsive.
in some cases it could help to use the SuspendLayout and ResumeLayout methods, see here:
Control.SuspendLayout Method
I have a WinForm set up and a process that loops until a button is pressed on the form.
When I try to run my code, the form does not even display. I suspect this is because the code gets stuck in the loop and doesn't get far enough to display the WinForm. How can I get the form to display and the loop to run after that point?
If you're looping because you need to do something with the GUI periodically while waiting for input, I suggest using a Timer control and its Tick event.
If you want to do non-GUI things while waiting, a more traditional timer is better suited to the task,
You should probably run the loop in a background thread. The BackgroundWorker class makes this pretty easy to do.
Don't do that.
Windows Forms (like most modern user interface development toolkits) is an event-driven framework. You should never use a loop that "waits" for something to happen; instead you want to use an event that triggers something to happen.
Essentially what's happening is this: WinForms has a loop running a message pump that listens for events from Windows and triggers C# events in response to them. Your code is executing on the same thread as that message pump (it has to, since in WinForms only one thread is allowed to touch any given control). So if you put that thread into a loop, the WinForms code that should be pumping messages isn't, and your user interface appears to hang, since it isn't responding to any messages from Windows. (If you keep clicking it, you will fill up the message queue and get a dialog box that says "This application has stopped responding, do you want to terminate?" or something like that.)
The correct solution is to do one of the following:
Use a Timer
Use a BackgroundWorker
Use a ThreadPool
Another solution that would work, but is not a good idea is:
Use Application.DoEvents() -- but please don't actually do this
Your form loading is freezing because the UI of a windows form runs in a single thread. And the logic that you put on the Load event of this form is running on that thread.
You can run your loop on a separate thread easily by using a BackgroundWorker component on your windows form. On the event DoWork of your background worker, you place the code that has the loop that should run without block your UI. On the Form.Load event, you can start the background worker component by calling the method RunWorkerAsync. On the event handler of your button, you place a code to stop the background worker by calling CancelAsync method.
The article How to: Implement a Form That Uses a Background Operation shows exactly how to accomplish it.
About your comment on not being able to update the Text of a TextBox from a your background worker component. It happens because it is not allowed to modify the state of a windows forms control from a different thread (your background worker code is running on a separated thread) MSDN documentation says:
Access to Windows Forms controls is not inherently thread safe. If you have two or more threads manipulating the state of a control, it is possible to force the control into an inconsistent state. Other thread-related bugs are possible, such as race conditions and deadlocks. It is important to make sure that access to your controls is performed in a thread-safe way.
A sample of how you can update the state of your windows forms controls from your background thread will be similar to the one below (assuming that the new value is already stored on a String variable named text):
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.textBox1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
this.textBox1.Text = text;
}
I borrowed this code snipped from How to: Make Thread-Safe Calls to Windows Forms Controls article. It can provide you more information about how to deal with multi-threaded windows forms.
You can use the form load event to trigger the start of the loop.
So it would handle the event Me.Load
However is it necessary for your loop to be happening inside of the UI?
This happens because your loop is keeping the window function from processing messages, such as those that tell it to repaint itself. Place a call to Application.DoEvents() inside of your loop to allow the UI to continue to function.
However, you need to ask yourself why you're looping like this in the first place. If you're, say, copying a bunch of files, this might make sense. For most tasks, though, responding to a timer tick should do the trick and won't block the UI.
You should run your loop in a background thread using the BackgroundWorker component.
Remember that the background thread cannot directly interact with the UI controls.
To report the progress on the UI, you should call the BackgroundWorker's ReportProgress method in the background thread, and handle the ProgressChanged event to update the UI.
You can call the CancelAsync method when the Button is clicked, and loop until the CancellationPending property is true.
This is a very similar problem to This one, sadly that one was never answered either.
I have a MDI Main forum that hosts several children forms. One of them does a long calculation and throws an exception if an error occurs (all work is done on the same thread). I then try to inform the user of an error with an messagebox, however it doesn't appear (but steals focus from the MDI Main, so the application is completely unresponsive).
The beheviour changes slightly if I call Application.DoEvents() (evil I know, but this is a last resort thing). Then the forms remain completely active and the messagebox only appears after I change active application (Alt+Tab) to something else and then back again.
What can I do to make sure the messagebox will be visible? I have already tried passing both, active child and MDI Main as parameter to the MessageBox.Show method. It doesn't change the behaviour.
To clarify: the messagebox is a part of the child form, however at this point I am willing to show it in any way that doesn't break the application. The messagebox should be modal, but it should be visible so it can be acknowledged by the user.
I had the same issue. When pressed ALT the popup showed.
It turned out to be a LinkedLabel that had the AutoSize property to true. The LinkedLabel was inside a FlowLayoutPanel. When I set the LinkedLabel.Text property to String.Empty. The LinkedLabel constantly tried to resize, which was causing the GUI to be constantly busy.
When I turned off the AutoSize property and the GUI no longer had to recalculate the positions. The GUI was no free. And the popup showed.
There could be other controls that are behaving the same.
See also:
https://connect.microsoft.com/VisualStudio/feedback/details/116884
Is the MessageBox shown in the MainForm or as part of the ChildForms? If the MessageBox is in the child Forms maybe you could pass an event back to the MainForm and open the MessageBox there.
The problem is that messageboxes tend to be modal.
In this instance I think that you'd do far better to use a delegate or an event with a handler in your main MDI code. That way your main application displays the message boxes. You can easily redefined an EventArgsType if you wish to pass whatever information that you require.
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