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
动词(用于对象),显示,显示或显示,显示。 1.引起或允许被看到... - http://dictionary.reference.com/browse/shown
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:
So, how can I know _when all the things (controls) have been fully loaded and displayed?
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
Shown事件实际上是与引发的初始化相关的最后一个事件。但请注意,Windows(以及其他平台)上的UI对象的实际渲染(在屏幕上绘制)将被延迟。 UI对象的创建仅分配所有必要的资源并“使对象的可视区域无效”。然后平台会调度渲染事件(在非托管Windows中,这是WM_PAINT,在Winforms API中,这将是Control实例的Paint事件)。
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
await keywords in C#. You could instead use the older
BackgroundWorker object to accomplish the same, if you prefer.