Get rid of WaitCursor
-
Bill, everything has been said, I don't know what to add. Here is the entire class:
using System;
using System.Windows.Forms;namespace LP_Core {
public class LP_WaitCursor : IDisposable {
private Form form;
public LP_WaitCursor(Form form) {
this.form=form;
form.Cursor=Cursors.WaitCursor;
}
public void Dispose() {
form.Cursor=Cursors.Default;
}
}
}It is independent of the actual WinForms application, and needs to be created only once as it belongs in a .NET programmer's toolbox IMO. I use it in every click handler (MenuItem, Button,...) that might take a noticeable amount of time. So there may be hundreds of
using(new LP_WaitCursor(this))
statements in a single app, each one replacing a pair ofCursor=...
statements. :)Luc Pattyn [My Articles] Nil Volentibus Arduum
modified on Saturday, September 3, 2011 2:34 PM
Cool, but why use a
Form
over aControl
? ..an optional parameter to indicate whether to disable the control would be nice too, as well as a call to the old-fashioned[LockWindowUpdate](http://pinvoke.net/default.aspx/user32/LockWindowUpdate.html)[[^](http://pinvoke.net/default.aspx/user32/LockWindowUpdate.html "New Window")]
API. Would those be considered beneficial additions, or a bad idea? OT; it's a basic pattern[^];string temp = someObject.SomeValue;
someObject.SomeValue = "Updating, call again later";
try
{
// do stuff
}
finally
{
someObject.SomeValue = temp;
}Bastard Programmer from Hell :suss:
-
Cool, but why use a
Form
over aControl
? ..an optional parameter to indicate whether to disable the control would be nice too, as well as a call to the old-fashioned[LockWindowUpdate](http://pinvoke.net/default.aspx/user32/LockWindowUpdate.html)[[^](http://pinvoke.net/default.aspx/user32/LockWindowUpdate.html "New Window")]
API. Would those be considered beneficial additions, or a bad idea? OT; it's a basic pattern[^];string temp = someObject.SomeValue;
someObject.SomeValue = "Updating, call again later";
try
{
// do stuff
}
finally
{
someObject.SomeValue = temp;
}Bastard Programmer from Hell :suss:
Eddy Vluggen wrote:
why use a
Form
over aControl
?The way I see it, it is the Form that: - the user is talking to; - is performing some operation, and hence is busy. So I decided to make the parameter a Form, not a more general Control. But of course Control would be fine.
Eddy Vluggen wrote:
an optional parameter to indicate whether to disable the control
I considered that too, but I would probably never use it. Most apps have several ways to get something done (MainMenu.MenuItem, ContextMenu.MenuItem, Button, KeyDown), so there isn't just a single Control to disable, I often have a Command object that sits in between several Controls and a single Action, however that would make my WaitCursor too specialized IMO. Also disabling a synchronous Control isn't all that useful IMO, so I only disable Controls for long, hence asynchronous, operations (which you don't want to run twice by accident).
Eddy Vluggen wrote:
LockWindowUpdate
Never used it. As it is bound to be a short operation, I probably would not care much. Longer operations, I'd do in a BGW, and I make sure the GUI updating is periodic rather than continuous. Summary: KISS, I'd say.
Luc Pattyn [My Articles] Nil Volentibus Arduum
-
Bill, everything has been said, I don't know what to add. Here is the entire class:
using System;
using System.Windows.Forms;namespace LP_Core {
public class LP_WaitCursor : IDisposable {
private Form form;
public LP_WaitCursor(Form form) {
this.form=form;
form.Cursor=Cursors.WaitCursor;
}
public void Dispose() {
form.Cursor=Cursors.Default;
}
}
}It is independent of the actual WinForms application, and needs to be created only once as it belongs in a .NET programmer's toolbox IMO. I use it in every click handler (MenuItem, Button,...) that might take a noticeable amount of time. So there may be hundreds of
using(new LP_WaitCursor(this))
statements in a single app, each one replacing a pair ofCursor=...
statements. :)Luc Pattyn [My Articles] Nil Volentibus Arduum
modified on Saturday, September 3, 2011 2:34 PM
Hi Luc, Yes, I fully understand what you are doing in the class: I created it myself, out of curiousity, before you posted your example here, and tested it, with the significant exception that I changed a Control's (Button, whatever) Cursor to 'WaitCursor, I didn't change the Cursor on the entire Form. Do you have any evidence whatsoever that changing the visual appearance of a Cursor on a Control or a Form is a cause of a memory leak, or consumes resources in a way that is not garbage collected ? And, that the solution you present here in any way prevents a memory leak ? I repeat that there is no valid reason for use of such a technique for so simple a task as changing the visual appearance of a Cursor, and, for those not-common, and well-documented cases, where .NET tells us we should bracket certain objects in a 'Using {} block, the reasons are crystal clear. This kind of over-engineering for simple tasks is what I refer to as "gratuitious elegance." And, if I were a beginner in .NET, I would find this truly confusing. You wrote: "So there may be hundreds of using(new LP_WaitCursor(this)) statements in a single app, each one replacing a pair of Cursor=... statements." My mind boggles trying to envision what type of application really uses "hundreds" of such classes, winking and blinking out of existence, each storing a reference to a Form. If (please, don't let it happen) I ever saw an application with "hundreds" of uses of such a class, with all kinds of controls putting a 'WaitCursor' on an entire Form, I would promptly conclude that: there's an application where the fundamental design involving threading is ... to put it mildly ... 'off the planet.' I can, indeed, imagine a scenario in which some tasks are so critical, their completion essential to so many pending-task-dependencies, that an entire application (or its UI) should "freeze up," or at least present some modal, ... even can't-be-cancelled ? ... dialog or progress bar, or whatever (nuclear power plant control ?), by the way. disputatur in spiritu fratribus, Bill
"Is it a fact - or have I dreamt it - that, by means of electricity, the world of matter has become a great nerve, vibrating thousands of miles in a breathless point of time? Rather, the round globe is a vast head, a brain, instinct with intelligence!" - Nathanial Hawthorne, House of the Seven Gables
-
Eddy Vluggen wrote:
why use a
Form
over aControl
?The way I see it, it is the Form that: - the user is talking to; - is performing some operation, and hence is busy. So I decided to make the parameter a Form, not a more general Control. But of course Control would be fine.
Eddy Vluggen wrote:
an optional parameter to indicate whether to disable the control
I considered that too, but I would probably never use it. Most apps have several ways to get something done (MainMenu.MenuItem, ContextMenu.MenuItem, Button, KeyDown), so there isn't just a single Control to disable, I often have a Command object that sits in between several Controls and a single Action, however that would make my WaitCursor too specialized IMO. Also disabling a synchronous Control isn't all that useful IMO, so I only disable Controls for long, hence asynchronous, operations (which you don't want to run twice by accident).
Eddy Vluggen wrote:
LockWindowUpdate
Never used it. As it is bound to be a short operation, I probably would not care much. Longer operations, I'd do in a BGW, and I make sure the GUI updating is periodic rather than continuous. Summary: KISS, I'd say.
Luc Pattyn [My Articles] Nil Volentibus Arduum
Luc Pattyn wrote:
I considered that too, but I would probably never use it. Most apps have several ways to get something done (MainMenu.MenuItem, ContextMenu.MenuItem, Button, KeyDown), so there isn't just a single Control to disable, I often have a Command object that sits in between several Controls and a single Action, however that would make my WaitCursor too specialized IMO.
True, and I rarely take the time to encapsulate it in an action-class that's responsible for synchronizing it's state; most actions in a WinApp can be initiated from multiple points in the application, each needing to be updated to reflect it's status.
Luc Pattyn wrote:
Never used it. As it is bound to be a short operation, I probably would not care much. Longer operations, I'd do in a BGW, and I make sure the GUI updating is periodic rather than continuous.
Sounds like a better approach than disabling drawing for more than a second. Tx :)
Bastard Programmer from Hell :suss:
-
Hi Luc, Yes, I fully understand what you are doing in the class: I created it myself, out of curiousity, before you posted your example here, and tested it, with the significant exception that I changed a Control's (Button, whatever) Cursor to 'WaitCursor, I didn't change the Cursor on the entire Form. Do you have any evidence whatsoever that changing the visual appearance of a Cursor on a Control or a Form is a cause of a memory leak, or consumes resources in a way that is not garbage collected ? And, that the solution you present here in any way prevents a memory leak ? I repeat that there is no valid reason for use of such a technique for so simple a task as changing the visual appearance of a Cursor, and, for those not-common, and well-documented cases, where .NET tells us we should bracket certain objects in a 'Using {} block, the reasons are crystal clear. This kind of over-engineering for simple tasks is what I refer to as "gratuitious elegance." And, if I were a beginner in .NET, I would find this truly confusing. You wrote: "So there may be hundreds of using(new LP_WaitCursor(this)) statements in a single app, each one replacing a pair of Cursor=... statements." My mind boggles trying to envision what type of application really uses "hundreds" of such classes, winking and blinking out of existence, each storing a reference to a Form. If (please, don't let it happen) I ever saw an application with "hundreds" of uses of such a class, with all kinds of controls putting a 'WaitCursor' on an entire Form, I would promptly conclude that: there's an application where the fundamental design involving threading is ... to put it mildly ... 'off the planet.' I can, indeed, imagine a scenario in which some tasks are so critical, their completion essential to so many pending-task-dependencies, that an entire application (or its UI) should "freeze up," or at least present some modal, ... even can't-be-cancelled ? ... dialog or progress bar, or whatever (nuclear power plant control ?), by the way. disputatur in spiritu fratribus, Bill
"Is it a fact - or have I dreamt it - that, by means of electricity, the world of matter has become a great nerve, vibrating thousands of miles in a breathless point of time? Rather, the round globe is a vast head, a brain, instinct with intelligence!" - Nathanial Hawthorne, House of the Seven Gables
Hi Bill, 1. memory leak prevention and memory consumption aren't a factor here. All the
IDisposable
interface and theusing
pattern achieve isDispose()
is getting called, what you do inside Dispose() is up to you, it doesn't have to relate to memory or resources, see it as "automatic post-processing". 2. I didn't say it was all happening on a single Form. A database (or any networked) application could easily have say 10 Forms, each Form having five buttons, and a memu bar with 5 main menu's, each holding 5 menu items. That is 250 menu items and 50 buttons all together. And more tables in the database would probably lead to more Forms, more Controls, more WaitCursors. If I expect any of the click handlers to regularly take more than say 100 msec to execute, I'd use threading, disable the Control and probably show a progress bar and possibly a Cancel button, I would then NOT show a WaitCursor (IMO WaitCursors are for synchronous operations only). If OTOH I expect those actions to normally take less than 100 msec, then I'd organize them as a synchronous operation, running on the main thread; I would then typically not disable the Control, and show a WaitCursor instead. Of course I'd organize the WaitCursor at the front of the click handler; I would not hide it somewhere in the data access layer (which would reduce the number of WaitCursor statements but also violate the separation-of-concerns concept. 3. The frequency of LP_WaitCursor instantiations is pretty low, as they appear only in some GUI handlers, i.e. they require the user to click something. That is expected to be less than once per second. If you really care about the number of objects created, abandoned, and finally collected in an app (which I do care about very much), then I suggest you: - eliminate allnew
keywords inside handlers that don't take human action as a trigger, say thePaint
,Tick
, andDataReceived
handlers. Ideally there should be NOnew Font(...)
ornew Pen(...)
ornew Anything(...)
inside those, that is where performance is going down the drain. - carefully investigate everything you do with strings. Most apps, as soon as they are doing something, create hundreds of strings per second, most of them extremely short lived. Using StringBuilder may reduce those numbers, and it may or may not improve performance, however IMO it is strings that justify the existence of automatic garbage collect -
Hi Bill, 1. memory leak prevention and memory consumption aren't a factor here. All the
IDisposable
interface and theusing
pattern achieve isDispose()
is getting called, what you do inside Dispose() is up to you, it doesn't have to relate to memory or resources, see it as "automatic post-processing". 2. I didn't say it was all happening on a single Form. A database (or any networked) application could easily have say 10 Forms, each Form having five buttons, and a memu bar with 5 main menu's, each holding 5 menu items. That is 250 menu items and 50 buttons all together. And more tables in the database would probably lead to more Forms, more Controls, more WaitCursors. If I expect any of the click handlers to regularly take more than say 100 msec to execute, I'd use threading, disable the Control and probably show a progress bar and possibly a Cancel button, I would then NOT show a WaitCursor (IMO WaitCursors are for synchronous operations only). If OTOH I expect those actions to normally take less than 100 msec, then I'd organize them as a synchronous operation, running on the main thread; I would then typically not disable the Control, and show a WaitCursor instead. Of course I'd organize the WaitCursor at the front of the click handler; I would not hide it somewhere in the data access layer (which would reduce the number of WaitCursor statements but also violate the separation-of-concerns concept. 3. The frequency of LP_WaitCursor instantiations is pretty low, as they appear only in some GUI handlers, i.e. they require the user to click something. That is expected to be less than once per second. If you really care about the number of objects created, abandoned, and finally collected in an app (which I do care about very much), then I suggest you: - eliminate allnew
keywords inside handlers that don't take human action as a trigger, say thePaint
,Tick
, andDataReceived
handlers. Ideally there should be NOnew Font(...)
ornew Pen(...)
ornew Anything(...)
inside those, that is where performance is going down the drain. - carefully investigate everything you do with strings. Most apps, as soon as they are doing something, create hundreds of strings per second, most of them extremely short lived. Using StringBuilder may reduce those numbers, and it may or may not improve performance, however IMO it is strings that justify the existence of automatic garbage collectHi Luc, +5 Once again I would like to thank you for the extended response. To be able to hear from someone at your level of technical excellence is a privilege ! The information provided in your last response shifts the entire frame of reference of this discussion to a multi-user, multi-session, (undoubtedly multi-threaded ?) application, probably client-server, with multiple tiers, data-access layers, business rules ... at least one of every fruit that was ever on one of Carmen Miranda's hats :) In those circumstances the architecture you describe makes perfect sense, of course. But, that scenario was not where this discussion started. It started with a simple question about why a visual cursor didn't properly refresh, probably in an asynchronous application without use of explicit threading. And, I must point out that you were the one who specifically mentioned the use of "hundreds" of instances of your LP_WaitCursor class, which I wondered, at the time I read it, if you were perhaps speaking in 'hyperbole.' Your excellent advice about 'exorcising' as much use of 'New' as possible, and using StringBuilder were long ago adopted by me as a general principle. How I (and others ?) would appreciate an article from you discussing in more detail your approach to threading strategy and ui update ! best, Bill
"Is it a fact - or have I dreamt it - that, by means of electricity, the world of matter has become a great nerve, vibrating thousands of miles in a breathless point of time? Rather, the round globe is a vast head, a brain, instinct with intelligence!" - Nathanial Hawthorne, House of the Seven Gables
-
Hi Luc, +5 Once again I would like to thank you for the extended response. To be able to hear from someone at your level of technical excellence is a privilege ! The information provided in your last response shifts the entire frame of reference of this discussion to a multi-user, multi-session, (undoubtedly multi-threaded ?) application, probably client-server, with multiple tiers, data-access layers, business rules ... at least one of every fruit that was ever on one of Carmen Miranda's hats :) In those circumstances the architecture you describe makes perfect sense, of course. But, that scenario was not where this discussion started. It started with a simple question about why a visual cursor didn't properly refresh, probably in an asynchronous application without use of explicit threading. And, I must point out that you were the one who specifically mentioned the use of "hundreds" of instances of your LP_WaitCursor class, which I wondered, at the time I read it, if you were perhaps speaking in 'hyperbole.' Your excellent advice about 'exorcising' as much use of 'New' as possible, and using StringBuilder were long ago adopted by me as a general principle. How I (and others ?) would appreciate an article from you discussing in more detail your approach to threading strategy and ui update ! best, Bill
"Is it a fact - or have I dreamt it - that, by means of electricity, the world of matter has become a great nerve, vibrating thousands of miles in a breathless point of time? Rather, the round globe is a vast head, a brain, instinct with intelligence!" - Nathanial Hawthorne, House of the Seven Gables
Hi Bill, you're welcome. And I hope all is clear now. At some point in time I was considering writing an article on threading, however I decided against it when I saw a large number of new CP articles on the subject, there even is a series by Sacha[^] which is bound to be excellent. I must admit I haven't read them, but I doubt there is much I could add. :beer:
Luc Pattyn [My Articles] Nil Volentibus Arduum
-
I set the Form's cursor explicitly, so it looks like:
this.Cursor=Cursors.WaitCursor; // code that may take some tens or, when unlucky, hundreds of milliseconds this.Cursor=Cursors.Default;
Of course, the cursor stuff isn't pretty, however you can easily make a disposable object for these purposes, so my code actually looks like:
using(new LP\_WaitCursor(this)) { // code that may take... }
where LP_WaitCursor holds a
Cursor=Cursors.WaitCursor
in its constructor, and aCursor=Cursors.Default
in its Dispose() method. Side note: If the slow operation is bound to take more time, it should not happen on the main thread (which blocks the GUI), instead it should be another thread (maybe a BackgroundWorker) and then it is: - either irrelevant to the user, hence no indication; - or relevant, and then it deserves a progress bar and a cancel button. :)Luc Pattyn [My Articles] Nil Volentibus Arduum
As others have already mentioned, the use of
using
for this purpose and similar ones is a very interesting technique. However, in this situation, it won't work. The long lasting operation is started (by user pressing a button) sending a message to some hardware device. The application then waits for an answer, sends another message, waits and so on. The operation is finished when receiving a message meaning "All done" or "Error". Since start and end of the operation reside in separate methods, I'll stick with explicit calls tosomeControl.UseWaitCursor = someBooleanValue
and try setting the focus to make it happen immediately. As for the side note: communication runs in another thread, of course. And user shall not be allowed to do anything in the meantime except for pressing the cancel button (by customer request). Therefore, all controls are disabled except for the cancel button and all its parenting controls up to the main form.Ciao, luker
-
As others have already mentioned, the use of
using
for this purpose and similar ones is a very interesting technique. However, in this situation, it won't work. The long lasting operation is started (by user pressing a button) sending a message to some hardware device. The application then waits for an answer, sends another message, waits and so on. The operation is finished when receiving a message meaning "All done" or "Error". Since start and end of the operation reside in separate methods, I'll stick with explicit calls tosomeControl.UseWaitCursor = someBooleanValue
and try setting the focus to make it happen immediately. As for the side note: communication runs in another thread, of course. And user shall not be allowed to do anything in the meantime except for pressing the cancel button (by customer request). Therefore, all controls are disabled except for the cancel button and all its parenting controls up to the main form.Ciao, luker
With the risk of stating the obvious, a simple way to disable all controls while showing a progress bar and cancel button, is using a progress dialog (modally, with ShowDialog), which just holds the progress bar and cancel button. :)
Luc Pattyn [My Articles] Nil Volentibus Arduum
-
Hi experts, an application I'm developing needs to tell user that it is busy. I therefore made most controls (excluding the "Cancel" button)
control.UseWaitCursor = true;
control.Enabled = false;When busy time ends, controls are set back to
control.Enabled = true;
control.UseWaitCursor = false;Now, cursor stays in WaitCursor representation until user moves the mouse by at least one pixel. How can I make cursor change to normal immediately when resetting the underlying controls?
Ciao, luker
I think you've run into a Windows quirk here. I've had this behaviour with other applications, and most of them weren't .NET. If the user is waiting for a while for the program to load/complete work, then it might be more elegant an idea to give more interface cues than just the wait cursor changing, such as a status text, or progress bar, etc.