Navigation |
Done Waiting for Godot – Setting/Clearing the Wait cursor in .NETMy compatriots on this blog have been pretty heavy on the theoretical so far, so I thought I would see if I could come up with a first entry that contains some actual code. Of course, that doesn’t mean that I’m not going to philosophize a bit first. When a developer first moves from C++ to C# or another .NET language, arguably the biggest shift is learning how to work with a garbage collector, and going from deterministic destruction (i.e. knowing when your stuff is going to go away) to non-deterministic (don’t worry about, your stuff will go away at some point). You can write decent object-oriented code in just about any language. I’ve seen pattern-based OO code written in GWBasic (and I’ve seen horrendous procedural code in Java and C#). However, it is much easier to write good OO code in modern languages. While it is possible to do without, some patterns really need a garbage-collected language to be practical. Otherwise you end up having to strap on extra appendages to handle memory clean-up. I will talk (a lot) more about this in future articles. There are a few times, though, where deterministic destruction is awfully handy. The classic examples are dealing with handles (file handles, brushes, etc.). In general, these are all examples where a managed world is running into a non-managed world. It was awfully handy in the olden-days to create an object representing a brush or some other object and know that when the method finished executing, the handle would be released. One of the cutesy C++ tricks was to have a class (probably with a name like CWaitCursor) that set the cursor to be the wait cursor in the constructor, and restored it in the destructor. You then just created an instance of your class at the top of your method. When the method was done, the destructor was called automatically, and, voila, your cursor was restored. This, of course, won’t work in a garbage-collected environment, because the cursor won’t be restored until the object is finalized which might happen in a few seconds or a few days. This isn’t really that big a deal, though – you just need to explicitly restore the cursor: public void SomeMethod()
{
Cursor.Current = Cursors.WaitCursor;
// Do some stuff
Cursor.Current = Cursors.Default;
}
You can do this, but there are some issues. First of all, it is pretty easy to forget to add the restore (especially if you happen to have multiple branches of code, or, evilly, return from the middle of the method). Second of all, this code is not quite right – it assumes that the cursor should go back to Default – what if it should return to cross-hairs or something. Probably not a big deal, but you could definitely end up with the wrong cursor. The big thing, though, that is wrong with this code, is that it assumes that your code will run successfully. What happens if an exception is thrown in the middle? The restore code is never run, and you end up with a wait cursor when you try to click on controls. To write this code properly, you have to do this: public void SomeMethod()
{
Cursor oldCursor = Cursor.Current;
Cursor.Current = Cursors.WaitCursor;
try
{
// Do some stuff
}
finally
{
Cursor.Current = oldCursor;
}
}
What a pain! And, you have to do this in every method that might take some time. You might argue that it is not such a big deal, but in my experience, when something is too much hassle, it either gets left out (meaning that you end up not getting the proper cursor) or you get partial handling, which might end up leading to being stuck with the wrong cursor. Fortunately, there is a better way. In .NET, there is a standard pattern for disposing of objects that have dependencies. For example, if you are using a file or a brush, and want to make sure it is released. So long as that object implements the IDisposable interface, you can use a using statement to make sure it is released. Here is an example using a brush: using (Brush brush = new SolidBrush(Color.Red))
{
g.FillRectangle(brush, rect);
}
This code is functionally equivalent (in fact, is changed at compile time to be): Brush brush = new SolidBrush(Color.Red);
try
{
g.FillRectangle(brush, rect);
}
finally
{
brush.Dispose();
}
The Dispose method releases the underlying handle. If you don’t call Dispose() explicitly, then eventually the garbage collector will finalize the object, which will cause the handle to be released, but you have no idea when this will happen. Dispose() is often used to release handles and system resources, but there is no law that it has to do that. Dispose() can do anything, which brings us back to the wait cursor. If we create a WaitCursor object, it can be set up to restore the cursor when the object is disposed. That means that we can write the following code: using (new WaitCursor())
{
// Do some stuff
}
This is much cleaner. The cursor is changed to a wait cursor when we enter the using statement, and is restored when we leave – no matter how we leave. And, the code is simple, and relatively elegant, so we don’t have to worry as much about people not bothering. Here is the entire code for the WaitCursor class (comments removed to save space): using System;
using System.Windows.Forms;
namespace Exotribe
{
public class WaitCursor : IDisposable
{
private Cursor m_oldCursor;
public WaitCursor()
{
m_oldCursor = Cursor.Current;
Cursor.Current = Cursors.WaitCursor;
}
public void Dispose()
{
Cursor.Current = m_oldCursor;
}
}
}
Trackback URL for this post:http://www.exotribe.com/trackback/19 |
Recent blog posts
User login |