|
I have had similar problems, the thing (from what I have gathered) is that when you set the ClientSize property the form resizes then changes the ClientSize again based on the theme settings (i.e. title bar header sizes).
The outcome being your form keeps growing/shrinking (once) every time you show/hide.
The best workaround I have found is to set the form size instead.
Its the man, not the machine - Chuck Yeager
If at first you don't succeed... get a better publicist
If the final destination is death, then we should enjoy every second of the journey.
|
|
|
|
|
Thanks for your response, Mehdi,
In this case, I am using the "shrunken" bounding box of the client area of the Owner Form to hit test when a second, "owned" (but not child) Form is moved, and re-positioning the second Form to always remain in the boundaries of the first form.
When I have time, I am going to experiment further with the differences between Form.Bounds, and the other Form-level 'area' properties under different conditions of configuring Win 7 visual effects, and see if I can't come up with some kind of compromise.
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
|
|
|
|
|
Cheers Bill,
I will try and investigate more (I gave up that feature last time!), let you know.
Its the man, not the machine - Chuck Yeager
If at first you don't succeed... get a better publicist
If the final destination is death, then we should enjoy every second of the journey.
|
|
|
|
|
Hi Bill,
I found the following link while answering a question in the quick answers CustomerBorderForm, it might help you out.
Its the man, not the machine - Chuck Yeager
If at first you don't succeed... get a better publicist
If the final destination is death, then we should enjoy every second of the journey.
|
|
|
|
|
Thanks, Mehdi, I look forward to studying this; haven't found time yet to go back and start turning on and off Win 7 window styles and examining their various effects on DisplayRectangle, ClientRectangle, Bounds, etc.
The last discussion forum item on that CodePlex project site[^] suggests to me there may be problems with that code on Win 7. But, I'll try it out, anyhow.
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 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
|
|
|
|
|
It appears to me that all you have to do is to set the Focus on either the Form or a given control. This worked for me:
private void button2_Click(object sender, EventArgs e)
{
textBox1.Cursor = Cursors.Default;
button1.Cursor = Cursors.Default;
button2.Cursor = Cursors.Default;
this.Focus();
} good luck, 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
|
|
|
|
|
I set the Form's cursor explicitly, so it looks like:
this.Cursor=Cursors.WaitCursor;
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)) {
}
where LP_WaitCursor holds a Cursor=Cursors.WaitCursor in its constructor, and a Cursor=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 wrote: 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)) {
}
where LP_WaitCursor holds a
Cursor=Cursors.WaitCursor in its constructor, and a
Cursor=Cursors.Default in its Dispose() method.
You know, I never thought of doing that. <smacks forehead>
|
|
|
|
|
Yes, it is elegant as it doesn't need explicit post-processing code.
I've learned this trick here on CP, many many years ago. Ever since, there is a lot of things I do with this pattern.
Now take care of that forehead, you will still need it.
|
|
|
|
|
Hi Luc,
That's a very interesting technique (I'd expect no less from you), and it leaves me curious to know if there's something about switching cursors that makes actually encapsulating them in a class, and disposing of them, as you've shown here, via 'using, best practice.
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
|
|
|
|
|
Not sure what you mean here Bill; what needed to be done took two statements, one upfront, and one in the back of the code; with the disposable object approach, all that remains visible is a single upfront statement with a clear intent, so you don't need to worry about the post-processing anymore.
I can't think of a better way, so I'd call it a best practice. And it doesn't apply just to cursors, you can do similar things with many objects you want to use-and-forget-about; either regular objects that just need to be disposed of (graphic objects such as Pens and Fonts, or database connections and commands), or your own objects that you want to give some post-processing actions. And the LP_WaitCursor example is the extreme incarnation: the object is almost empty, its main action happens at its life end.
|
|
|
|
|
Hi Luc,
First, I truly appreciate the time you took to comment further on this technique: I am already 'socialized' to wrapping things like 'StreamReaders' and such in 'Using' statements.
However, in this case, a simple need to change a cursor back to its default visual state, imho using a technique which involves creating a class seems to me like shooting a horsefly with a cannon
What's happening here is just another 'quirk' of Windows Forms: the need to set focus somewhere to 'provoke' the cursor to change visual state properly. A quirk I cannot even reproduce reliably (see example below).
Is it possible that I am 'off the planet' here, and you are talking about a DataBase cursor ... I hope not
In addition, if I understand it correctly, a class to be used in this pattern of Using/Dispose must inherit from IDisposable, a key fact, omitted here, the 'newbie' to .NET may not "get" too easily.
In VS Studio Pro 2010, compiling against .NET FrameWork 4.0 Client Profile, the following code in a button click handler:
using System.Threading;
private void button2_Click(object sender, EventArgs e)
{
button2.Cursor = Cursors.WaitCursor;
Thread.Sleep(10000);
button2.Cursor = Cursors.Default;
} Does reset the cursor properly after the call to the Thread.Sleep.
The "big picture," I am sure we both agree on is: any operation causing a noticeable delay should be handled by threading, so the UI stays responsive.
I look forward to further enlightenment !
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
|
|
|
|
|
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 of Cursor=... statements.
Luc Pattyn [My Articles] Nil Volentibus Arduum
modified on Saturday, September 3, 2011 2:34 PM
|
|
|
|
|
i would go one step further and have a base class that takes 2 action delegates, one executed in the ctor, the other in dispose.
this would allow any pattern of
setup -> action -> teardown
it could also be subclassed to 'hardcode' the delegates in the subclass ctor
Pedis ex oris
|
|
|
|
|
Cool, but why use a Form over a Control ?
..an optional parameter to indicate whether to disable the control would be nice too, as well as a call to the old-fashioned <a href="http://pinvoke.net/default.aspx/user32/LockWindowUpdate.html">LockWindowUpdate</a>[<a href="http://pinvoke.net/default.aspx/user32/LockWindowUpdate.html" target="_blank" title="New Window">^</a>] 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
{
}
finally
{
someObject.SomeValue = temp;
}
Bastard Programmer from Hell
|
|
|
|
|
Eddy Vluggen wrote: why use a Form over a Control ?
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 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
|
|
|
|
|
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 the using pattern achieve is Dispose() 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 all new keywords inside handlers that don't take human action as a trigger, say the Paint , Tick , and DataReceived handlers. Ideally there should be NO new Font(...) or new Pen(...) or new 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 collection, and once you have that in place, I don't mind creating a few more objects I could do without, but choose to have, mostly for structuring my code and improving code readability.
|
|
|
|
|
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.
|
|
|
|
|
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 to
someControl.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.
|
|
|
|
|
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.
|
|
|
|
|