I'm currently working on an application that uses a Windows Form GUI. The main work of the application will be performed on an additional thread - but it is possible that it will be dependent on the state of the form.
Because of this, before I create the thread I must make sure that the form is completely loaded. Also, I need to make sure the thread is terminated before the form begins closing.
Possible solutions for this problem might be overriding the OnShown and OnFormClosing methods.
Does the OnShow method really gets called only after ALL of the assets of the form have been loaded? And what about the OnFormClosing - can I be sure that any code executed in this method will be performed before the form begins close / dispose?
I suggest you read through the WinForms event ordering as posted on MSDN:
http://msdn.microsoft.com/en-us/library/86faxx0d.aspx
Windows Forms events can be tricky, and the order they fire unreliable. For example, the 'Shown' event is meant to fire once and once only, when the Form is first displayed, but just yesterday I found a way to completely prevent that event from firing by manipulating Form.Visible flags at the right time.
So, you need to be quite specific about your needs. For example - when you say 'completely loaded', what do you mean? Controls created, form visible, Form constructor finished executing?
Likewise, the FormClosing event might be circumvented by an unhandled exception in your main aUI thread, which results in the Form being removed without the event firing.
I suggest never assuming anything around these events. Make your worker thread capable of dealing with the Form state being unavailable, or not ready. Without knowing your exact requirements, it's hard to be more specific.
Have you tried to use InitializeComponent on the constructor of your main WinForm and use OnLoad (called whenever everything is loaded)?
Related
What is the real order of events in a Windows Forms application?
What I mean is, when I put code in the Form_Shown event, I expect that code only to run after the form has been Shown:
verb (used with object), showed, shown or showed, showing. 1. to cause
or allow to be seen... - http://dictionary.reference.com/browse/shown
But the Form_Shown event is a little misleading. If I do some heavy stuff inside that event, it seems as though the code gets executed before the Form has finished been shown. Let's say that I have a MainMenu, a small Toolbar and a TextBox on a form.
I want to do some heavy stuff (nevermind threads and workers for now...), so the last event I can use I would think would be Form_Shown. So I put my heavy code in there, but when the Form begins to display, I end up waiting ~ 5 - 6 seconds for the Toolbar and stuff to display (which ends up happening after my heavy code does its thing.
Which leads me to believe that I'm subscribing to the wrong event. I don't want the Form_Shown event at all. What I really need is:
Form_WhenALLTheThingsHaveShownEventHandler event.
So, how can I know _when all the things (controls) have been fully loaded and displayed?
The Shown event is in fact the last event related to initialization that is raised. However, note that the actual rendering (drawing on-screen) of UI objects in Windows (and on other platforms) is deferred. The creation of a UI object merely allocates all the necessary resources and "invalidates" the visual area of the object. The platform then later schedules the rendering event (in unmanaged Windows, this is WM_PAINT, in the Winforms API this would be the Paint event for a Control instance).
The rendering event cannot be dispatched until the UI object's thread is available, and if you have long-running code in the Shown event, that will keep the UI object's thread unavailable for the duration of your code. I.e. nothing gets drawn until your code completes.
There are other events that you could use to more reliably detect when things have "settled down". For example, the Application.Idle event tells you when the main application thread is about to enter the idle state. Alternatively, you could just subscribe to the form's Paint event. In either case, you would want to use BeginInvoke() to dispatch your long-running code, so that you don't block the handling of those events.
Now, all that said: you really should not be performing any long-running work in the UI thread, period. Using either of the above events doesn't solve the underlying problem; it simply delays the problem until after the initial rendering of your UI. The UI will still remain blocked while your long-running work is executing, and frankly the user may actually find it preferable for there to be no UI at all, than for there to be something that looks like they can interact with but which they can't (i.e. is unresponsive to their input).
In the latest version of .NET, there are some very nice mechanisms available for shifting long-running work to background threads, so that the UI can remain responsive. See Task and the async and await keywords in C#. You could instead use the older BackgroundWorker object to accomplish the same, if you prefer.
Which Event is occurred before Form Load event??
Because I have a timer control for which I have written timer.start() method in form load event and current time is displayed in Label Control.
Timer starts when the form Loads but it takes few seconds to show the time, so is there any other way to show the time fast?
Have a function called ShowTheTime. Call it in form load. Also start your timer in form load. Your timer handler also calls ShowTheTime.
Am I missing something here? It seems kind-of trivial.
You're probably using the Timer from the Forms namespace. That component is tightly coupled to the UI. You should take a look at the Threading.Timer class which is not tied to the UI. You will have complete control over it and can start it from the Form's constructor (or even earlier, before the form is created.)
This is a very old article but the concepts in it still hold. (Compares the different Timer classes.) Basically an abbreviated version of the previous article.
Using Winforms,
If I'm on a thread that is not the "main" ui thread can I safetly
Create a Form,
Call ShowDialog on that form
It seems like I would be obeying the rule of:
Winforms controls must be modified on the thread they were created in.
From some of the reading I've done it seems like ShowDialog will create its own message pump so that I don't need to worry about the Form being created on a thread that already has a message pump.
The background of the question is that I get a callback where I'd like to display some Winforms UI, but I don't have access to any other UI that I could use to Invoke to the main thread.
That's roughly correct, albeit that it is pretty important that you call the thread's SetApartmentState() method to switch the thread to STA. Important for many UI operations, including the clipboard, drag and drop and the shell dialogs. And that you usually have a crummy Z-order problem when the form that you create on the thread is not in the foreground and hides behind another window. Or has the nasty habit of actually do move in the foreground when the user doesn't expect it and grab a mouse click or keystroke unexpectedly.
These are hard problems to fix, they do make your app flaky. There's no good reason to not have a reference to invoke to, you can also pass it to the class some way some how. You've always got Application.OpenForms[0] to fall back on, if really necessary.
Yes, you can do that, but if you want the dialog to actually act like a modal dialog (i.e., block the parent Window, which I assume you want since you are calling ShowDialog) then be prepared to be disappointed.
What problem are you actually trying to solve here. It sounds like you want a modal dialog that doesn't block, which is a bit strange. If you explain the problem at hand there may exist a solution you have not yet considered.
I'm using the following code to open a new form (that is for user input) in my function:
Form4 f4 = new Form4();
f4.mainform = this;
f4.get_Text(matchString);
f4.ShowDialog();
It's working perfectly when not threading, but when I try to run the function on two threads I only get 1 form popup. How can I adjust it so that it will support more than one thread?
You can open a form from any thread, you just need to start a message loop on the new thread, such as:
Application.Run(f4)
The Run method will not return until the form is closed. It can cause issues, though, so you need to be careful and know what you are doing. Unless it's a real pain to do, I would sooner consider redesigning your code to be more asynchronous. For instance, you could create a event handler method to listen for the form's closing event.
The WIN32 Message pump runs on one thread -- the GUI thread. As soon as you .ShowDialog(), the c# stops until the user closes the dialog. Even if you use .Show() to avoid a "stop and wait for the user to push ok", you still must handle all GUI events on the GUI thread by .Invoke()ing to the GUI thread.
The way to support multi-threaded-ness is by doing non-GUI work on alternate threads.
ShowDialog does pump messages so it would technically work on a separate thread without needing a dedicated message loop. However, what you currently have looks like a recipe for disaster because that form appears to hold a reference to another form via f4.mainform = this and it is presumably trying to access it. You simply cannot do this without littering (and I mean that literally) your code with a bunch of Invoke or BeginInvoke calls.
Furthermore, running UI forms on a thread other than the main UI thread generally does not work well. There are a few obscure problems you can run into. For example, since there would be two UI threads in play it is possible to have 2 active modal dialog boxes open. One might be hidden behind the other and the end user would not see it. This reason, among others, is why it is not generally recommended to use more than one UI thread.
My advice is to figure out a way to make this work with a single UI thread. Your life will be simplier if you do.
I am trying to update a selected record using this code when closing the form:
interestsTableAdapter.Update(newCityCollectionDataSet);
The record is NOT updating doing this. I use the same code to update datagridviews but I do it while it is validating. Is there a better way to do this with FORMS as opposed to controls?
Why this isn't working could depend on when you're trying to save your record. There's a big difference between executing code before your form's closing and while your form's closing.
Typically, failures while the form's closing (in handlers of a Form's FormClosed event) are caused by the application exiting before the handler gets a chance to do its job.
On the other hand, handlers of a Form's FormClosing event are invoked before the form even starts to close, and can even keep it from closing. These handlers are almost always waited for, as long as the form is the application's "main" or "startup" form (and not a child of it).
Without seeing more code than what you've provided, I'm afraid it's all I can do to advise you to execute your code before the form closes (via its FormClosing event), rather than while it's closing (via its FormClosed event).