Thursday, August 24, 2006

Helping Disabled Controls Lead A Full Life (And Letting Your Users Keep Their Hair)

Ok, so there's two main points I want to cover in this post. The first is my thoughts on when you should disable a control vs. making it invisible vs. allowing the user to activate the control and receive a message. The second part is about creating a Windows Forms component that allows you to place a second tooltip on controls, this tooltip is displayed only when the control is disabled and the mouse hovers over it. This provides feedback to the user about why the control is disabled, which puts to bed my major complaint about disabled controls.

Firstly, disabled vs. invisible vs. messagebox. My colleagues and I often disagree over how we should prevent a user from accessing a certain function through the UI. One of my senior colleagues loves to make controls invisible, it's almost always his preferred option. Another of my colleagues isn't so big on making controls invisible, but prefers it to disabling controls because she feels a disabled control 'teases' her - it advertises a function and then won't let her use it. I share her sentiments. As for making controls invisible, it's not always the wrong answer, but there are a few down-sides to this;

1. I have situations where users have bought other systems or resorted to spreadsheets etc. to accomplish a job simply because they didn't know their existing software would do the job - and they didn't know because they couldn't see the option. This can even occur with entire modules (i.e they didn't know their accounting system could also do payroll, because all the payroll functions are unlicensed and therefore invisible).

2. You end up with weird blank spaces on your windows. If the control you're making invisible isn't a menu item, or isn't contained in a TableLayout/FlowLayout control of some kind, then making it invisible can destroy the look of your form. Sometimes this is true even if it is in a layout container. It can also lead users into thinking there's plenty more room on the window for the extra fields they want (and no one else does), simply because they can't see the hidden controls.

3. Whether a control is invisible or disabled, the user still can't do what they want to, and therefore they're still frustrated. Neither method tells the user why they can't do something or what they can do about it.

4. If you're lucky enough to have users who can read documentation, and you're twice as lucky as to have a user that actually does, they'll probably be frustrated when they check the manual or on-line help to find out if the function is available. Why ? Because the documentation will likely say, go here, click this, do that, now click button X - and button X won't be on the screen. At this point, they're wondering why not - did they get something wrong in one of the previous steps ? Are they in the right place, on the right window ? Do they have the wrong version of the software ? At least if the button is disabled they know they're in the right place - which is marginally better than not knowing anything.

Of course, with point 4, better documentation would help.

So when should you make a control invisible as opposed to disabled ? Well, it's a judgment call, but let me give you an example. In the new POS system I'm working on (Ontempo Store), the Cash Out option on EFTPOS can be disabled. Now this function would typically be disabled for either the whole company (i.e company policy is no cash out on EFTPOS - we're not a bank) and would apply to all stores, or it might be set on a per store basis depending on whether the store managers are given the power to decide, or perhaps it only applies to stores that happen to take a lot of cash transactions.

In any case, the option isn't likely to change states frequently, it's likely to be set one way or the other either permanently, or for months at a time, in any given location. It's state isn't context sensitive. Furthermore, the users of the system themselves aren't going to be able to change the setting. Someone at head office can, or maybe the store managers can, but the permanent, temporary or casual sales staff just aren't going to have permission to do it.

The final factors in the decision is that Cash Out on EFTPOS should be made invisible are;

a. This is an extremely common function of POS software.
b. This would be unlikely to be a huge addition to the system if it's not already supported.
c. If a user thinks cash out should be allowed and can't find the function, they'll likely ring their head office and get told cash out is deliberately turned off, or head office will enable the setting, or head office will contact us to find out if the feature is supported. In any case, we won't get 50 support calls from 50 different stores. This is a symptom of the type of user base that runs POS software.


i.e Our customers aren't likely to go out and buy another POS system because they can't find a cash out field or option, and we're not going to be increasing our support calls because people can't find it.

Given all these factors, I believe it's an appropriate decision to make the text field and label for Cash Out invisible on our EFTPOS payment panel.

If we were talking about a function that isn't strictly POS related, such as timesheeting, accounting, payroll etc. that we also had modules for, then we'd (probably) be better off leaving the controls visible but disabled. This is because it's a constant reminder to people that we can do these (non-standard) things, and they'll almost be sure to talk to us about them if they require them at a later stage. It's almost like free advertising !

The other time disabling controls is a better idea, is when they're disabled because of the state or context of the application. For example, the save button may be disabled because there haven't been any changes made or because the data is invalid. In a graphics program the Set Transparency option may be disabled because the current image type doesn't support transparent colours and so on. Anytime a function is not available but could be at any moment, if only a few changes are made, then disabling the control is appropriate.

Of course, in these situations I generally prefer not to disable controls or make them invisible. Instead I usually leave the controls visible and enabled, but when they're clicked I display a message box to the user saying the feature is unavailable and explaining why. It's that last bit that's key.

Whether you disable a control or make it invisible, you're frustrating the user who wants to use that option. You've taken it away from them, and you haven't told them why. It's like the parent who tells their child, "Because I said so", or the bully that holds your lunch above your head just out of your reach. It's mean, and you shouldn't do it.

By leaving the controls visible and enabled you ensure the user can find and see your feature, either because it's in plain sight or because they found it through the help system. By popping up a message box and using a conditional statement, you can still prevent the user from accessing the function inappropriately but you can also tell them what to do to enable the function ! This makes for a vastly more satisfying user experience.

Of course, disabled controls are a relatively common standard and some developers would just prefer to disable controls than write message box code. Additionally, there is one problem with leaving everything enabled - the user can't tell at a glance which functions are allowed in their current state. This isn't a problem as often as you might think, but in some situations it can be annoying.

So what's the answer ? Well, I suggested to Microsoft they put a second tooltip on the controls in Windows or .NET, one that is displayed only when the control is disabled. This property can then be set either programmatically, or statically in design mode if the control is only ever disabled for one reason. With this second tooltip, developers can still disable controls andusers can still find out why the control is disabled, then and do something about it.

Unfortunately, while Microsoft agreed it was a good idea, they said they had no plans to implement it. So, with the power of the .NET framework and Visual Studio, I did it myself ! Now I post sample code for my solution for your viewing pleasure. I should note this code hasn't been perfected yet, but it does the basic job and should be a great base for you build your own solution on.

So, what's involved ? First, we're going to build a Component that can be placed on the Windows Forms designer. The component will be an Extender Provider which means it provides a new property to (new and) existing controls on the form. Finally, since disabled controls don't receive mouse events or pop-up tooltips on their own, the component will have to track the mouse and pop-up a tooltip itself when the mouse cursor hovers over a disabled control.

To make life easier for myself, I've wrapped the standard ToolTip component shipped with Visual Studio 2005 and used it for actually displaying the tooltip. That means all my component has to do is determine when and which tooltip to display, then call the ToolTip component to have the tooltip displayed.

To get started, create a new Windows Control Library project in Visual Studio and add a new Component to it. Rename the component DisabledTooltip.

Add the following attributes above the component definition, like this;


using System;
using System.ComponentModel.Design;
using System.ComponentModel;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Text;
using System.Threading;
using System.Windows.Forms;

[
ProvideProperty("DisabledTooltip",typeof(Control)),
DefaultEvent("Draw")
]
public partial class DisabledTooltip : Component, System.ComponentModel.IExtenderProvider
{

Place the following field definitions at the top of the component, like so;

//This is a timer used to determine if the mouse has 'hovered' over a disabled control for long enough that we should show the tooltip.
System.Threading.Timer timer;
//This is a hash table that contains the text tooltips to be displayed for each control when it is disabled. The hash table is keyed by the control, and stores strings.
private Hashtable disabledToolTips;
//This is an object used to store the state of the mouse.
private MouseEventArgs mouseValues;
//This is a reference to our parent, either a Form or UserControl. Since both of these inherit from Control, I've used that as the type of variable to store the reference in.
private Control parentControl;

//This is a boolean flag that tells us not to redisplay the tooltip, because we've just done it.
private bool ignoreNext = false;


//This is a delegate used to call the ShowTooltip method of the internal ToolTip component. We need the delegate because we want to show the tooltip from the callback made by our timer object which is on another thread, so we must invoke the call to cast it back to the UI thread.
private delegate void ShowToolTipDelegate(string toolTipText, Control control);

Now, declare some events for our component - just in case anyone wants to do owner drawing etc.


///
/// Occurs when the ToolTip is drawn and the OwnerDraw property is true.
///

public event EventHandler<DrawToolTipEventArgs> Draw;
///
/// Occurs before a ToolTip is initially displayed. This is the default event.
///

public event EventHandler<PopupEventArgs> PopUp;

Next, replace the default constructors with these ones


///
/// Default Constructor.
///

public DisabledTooltip()
{
InitializeComponent();

//Make sure we initialise our hash table for storing the tooltips in.
disabledToolTips = new Hashtable();
}

///
/// Full contructor.
///

/// The container owning this component.
public DisabledTooltip(IContainer container)
{
//This line is important - it helps us to obtain a reference to our parent.
container.Add(this);
InitializeComponent();

//Make sure we initialise our hash table for storing the tooltips in.
disabledToolTips = new Hashtable();
}

Ok, now we get to the hard part. We need to setup our component so it has a reference to it's parent. This is easier said than done, luckily Rick Strahl has a solution. Basically, we create a property that allows our parent to be set externally. We then override the Site property of the component, which is set by Visual Studio at design time prior to it generating the code to instantiate our component in the InitializeComponent method of the parent. We use the value of the Site property to pre-set our parent property, and the result is that Visual Studio automatically generates code that sets our property to 'this'. That gives us our reference !

So, add the following override for the Site method of the Component class, and the ParentControl property like so;


///
/// Gets or sets the System.ComponentModel.ISite of the System.ComponentModel.Component.
///

public override ISite Site
{
get { return base.Site; }
set
{
base.Site = value;
if (value != null)
{
IDesignerHost host = (IDesignerHost)value.GetService(typeof(IDesignerHost));
if (host != null)
{
IComponent componentHost = host.RootComponent;
if (componentHost is Control)
ParentControl = (Control)componentHost;
}
}
}
}


///
/// Gets or sets the parent form or user control of this component.
///

[Browsable(false)]
public Control ParentControl
{
get { return parentControl; }
set
{
//Don't bother doing anything if we're given the same value we've already got.
if (parentControl != value)
{
//If we already have a parent, unsubscribe from it's events.
if (parentControl != null)
parentControl.MouseMove -= this.parentForm_MouseMove;

parentControl = value;


//If our new value is a valid parent not null), then connect to it's mouse move event so we
//can watch the mouse and decide when to popup a tooltip. Also, initialise and start our
//timer.
if (parentControl != null)
{
parentControl.MouseMove += new MouseEventHandler(parentForm_MouseMove);
SetupTimer();
}
}
}
}

Ok, that let's us get a reference to our parent ! Now we need to implement the IExenderProvider interface so we can add our DisabledToolTip property to the other controls on the form. Throw the following code into your component.

bool IExtenderProvider.CanExtend(object extendee)
{
bool retVal = false;
if (extendee is System.Windows.Forms.Control)
retVal = true;
return retVal;
}


///
/// Gets the value of the DisabledTooltip property for the specified control.
///

/// The control to retrieve the property value of.
/// A string.
[
DefaultValue("")
]
public string GetDisabledTooltip(Control control)
{
string text = (string)disabledToolTips[control];
if (text == null)
text = string.Empty;
return text;
}

///
/// Sets the value of the DisabledTooltip property for the specified control.
///

/// The control to set the property value of.
/// A string containing the disabled tooltip for the specified control.

public void SetDisabledTooltip(Control control, string value)
{
if (value == null)
value = String.Empty;
if (value.Length == 0)
{
disabledToolTips.Remove(control);
}
else
{
disabledToolTips[control] = value;
}
}

Great. Now, we need to create some routines we've referred to in the code we've already written. We need a routine to initialise our timer (and another to reset it), as well as an event handler for the parent controls mouse move event and a callback method for the timer.

First, flick to the designer for our component and add a standard .NET ToolTip component called toolTip to our own. Then add the following code;


private void SetupTimer()
{
if (parentControl != null && !this.DesignMode)
{
if (timer == null)
timer = new System.Threading.Timer(this.MouseStopped, timer, toolTip.InitialDelay, toolTip.InitialDelay);
else
timer.Change(toolTip.InitialDelay, toolTip.InitialDelay);
}
}

private void ResetTimer()
{
if (timer == null)
SetupTimer();
else
timer.Change(toolTip.InitialDelay, toolTip.InitialDelay);
}

//This procedure is called back from our timer, and is the key to showing the tooltip.
//The timer fires when the mouse has been still long enough that a tooltop should be shown.
//In this routine, we check to see if the mouse if over a child control, and if so, if that control
//is disabled and has a disabled tooltip. If all of those criteria are met, we show the tooltip.
private void MouseStopped(object state)
{
//If we've already shown the tooltip, ignore the next timer event. This is because when the
//tooltip disappears on it's own, a mouse move event seems to fire on the parent, which
// causes us to be called again, and causes the tooltip to flicker and become unreadable.
if (!ignoreNext)
{
//First, stop our timer to prevent it firing again while we're busy.
if (timer != null)
{
timer.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
}

//Now, if we know the last location of the mouse, check to see if it's over a child control.
if (mouseValues != null)
{
Control c = null;
c = GetControlAtPoint(parentControl.Controls, mouseValues.Location);

//If the mouse is over a control, and the control is disabled...
if (c != null && !c.Enabled)
{
//Get the disabled tooltip from our hash table
string toolTipText = (string)disabledToolTips[c];
if (c != null && !String.IsNullOrEmpty(toolTipText))
{
//If our parent control hasn't been disabled and if we do have a disabled tooltip for this
//control then invoke back to the main thead and show the tooltip.
if (!parentControl.IsDisposed)
parentControl.Invoke(new ShowToolTipDelegate(ShowTooltip), toolTipText, c);
}
}
}
}
else
ignoreNext = false;
}

//This routine is 'invoked' from MouseStopped, and is reponsible for actually showing the tooltip
//by calling the Show method on our internal ToolTip component.
private void ShowTooltip(string toolTipText, Control control)
{
//Set a flag to say we should ignore the next MouseStopped call.
ignoreNext = true;
//Ask the internal ToolTip component to show our tooltip for us.
toolTip.Show(toolTipText, parentControl, control.Left, control.Top + System.Windows.Forms.SystemInformation.CaptionHeight, toolTip.AutoPopDelay);
}

//This is a recursive routine that finds the child control located under the mouse cursor, or
//returns null if there isn't one. This is called from MouseStopeed.
private Control GetControlAtPoint(System.Windows.Forms.Control.ControlCollection controls, System.Drawing.Point location)
{
bool foundControl = false;
Control c = null;

//Loop through each of the controls on the parent passed...
for (int cnt = 0; cnt < c =" controls[cnt];" color="#33cc00">//See if this control contains our mouse cursor co-ordinate
if (location.X >= c.Left &&amp;amp;amp; location.X <= c.Left + c.Width && location.Y >= c.Top && location.Y <= c.Top + c.Height) { //If this control has children then we need to check them too...
if (c.Controls.Count > 0)
{
Control c2 = GetControlAtPoint(c.Controls, location);

//If there is no other child under our cursor, return this control, otherwise return it's child
if (c2 != null)
c = c2;

foundControl = true;
break;
}
else
{
//Stop searching, c now contains the control we're looking for.
foundControl = true;
break;
}
}
}

//If we didn't find any control, then return null
if (!foundControl)
c = null;

return c;
}

//Each time the mouse moves on our parent, reset our timer to stop it from firing and record
//the positon of the mouse cursor.
void parentForm_MouseMove(object sender, MouseEventArgs e)
{
ResetTimer();
mouseValues = e;
}

Now we're nearly done. All we need to do is connect to the events on on internal ToolTip component and raise our own events in response, and also expose the useful properties of ToolTip component.

Connect the following event handlers to the appropriate events on the ToolTip component.

private void toolTip_Popup(object sender, PopupEventArgs e)
{
OnPopUp(e);
}

private void toolTip_Draw(object sender, DrawToolTipEventArgs e)
{
OnDraw(e);
}


///
/// Raises the Draw event.
///

/// A DrawToolTipEventArgs event arguments object.
protected void OnDraw(DrawToolTipEventArgs e)
{
if (Draw != null && OwnerDraw)
Draw(this, e);
}

///
/// Raises the PopUp event.
///

/// A PopupEventArgs event arguments object.
protected void OnPopUp(PopupEventArgs e)
{
if (PopUp != null)
PopUp(this, e);
}

Now add the following property definitions, and you're done !

///
/// Gets or sets the automatic delay for the tooltip.
///

public int AutomaticDelay
{
get { return toolTip.AutomaticDelay; }
set { toolTip.AutomaticDelay = value; }
}

///
/// Gets or set the amount of time the ToolTip remains visible if the pointer is stationary on a control with the specified ToolTip.
///

public int AutoPopDelay
{
get { return toolTip.AutoPopDelay; }
set { toolTip.AutoPopDelay = value; }
}

///
/// Gets or sets background colour for the ToolTip.
///

public Color BackColor
{
get { return toolTip.BackColor; }
set { toolTip.BackColor = value; }
}

///
/// Gets or sets the foreground colour for the ToolTip.
///

public Color ForeColor
{
get { return toolTip.ForeColor; }
set { toolTip.ForeColor = value; }
}

///
/// Gets or sets the time that passes before the ToolTip appears.
///

public int InitialDelay
{
get { return toolTip.InitialDelay; }
set { toolTip.InitialDelay = value; }
}

///
/// Gets or sets a value indicating whether the ToolTip should use a balloon window.
///

public bool IsBalloon
{
get { return toolTip.IsBalloon; }
set { toolTip.IsBalloon = value; }
}

///
/// Gets or sets a value indicating whether the ToolTip is drawn by the operating system or by code that you provide.
///

public bool OwnerDraw
{
get { return toolTip.OwnerDraw; }
set { toolTip.OwnerDraw = value; }
}

///
/// Gets or sets a value that determines how ampersand (&) characters are treated.
///

public bool StripAmpersands
{
get { return toolTip.StripAmpersands; }
set { toolTip.StripAmpersands = value; }
}

///
/// Gets or sets a value that defines the type of icon to be displayed alongside the ToolTip text.
///

public ToolTipIcon ToolTipIcon
{
get { return toolTip.ToolTipIcon; }
set { toolTip.ToolTipIcon = value; }
}

///
/// Gets or sets a title for the ToolTip window.
///

public string ToolTipTitle
{
get { return toolTip.ToolTipTitle; }
set { toolTip.ToolTipTitle = value; }
}

///
/// Gets or sets a value determining whether an animation effect should be used when displaying the ToolTip.
///

public bool UseAnimation
{
get { return toolTip.UseAnimation; }
set { toolTip.UseAnimation = value; }
}

///
/// Gets or sets a value determining whether a fade effect should be used when displaying the ToolTip.
///

public bool UseFading
{
get { return toolTip.UseFading; }
set { toolTip.UseFading = value; }
}

With the component complete, you can now add it to any .NET Windows Form. Once the component is added, any other controls on that form will get a property called Disabled ToolTip on xxxx where xxxx is the name of the component. Just set this property to the text you want in your disabled tooltip, and a way you go !

Now you can have the best of both worlds, disabled controls that don't tease your users.

Monday, August 21, 2006

Application.RenderWithVisualStyles - Maybe

Just a quick note about the System.Windows.Forms.Application.RenderWithVisualStyles property in the .NET 2.0.

I had to write some manual resizing/layout code the other day and I discovered that I needed to alter the size and location of some controls differently depending on whether XP themes were enabled or not. I'm still not quite sure why this was the case, but one set of values certainly wasn't right asthetically in both cases.

It seems the RenderWithVisualStyles property is supposed to tell you whether or not controls and forms inside your application should render 'themed'. This is based on a number of things, such as whether visual styles have been enabled for your application, whether they're supported and enabled in the OS etc.

Well, the property works fine until you change settings with your application open. If you do this, the value of the property doesn't appear to change until your application is restart - even though the standard Windows Forms controls (such as the Button control) will alter their display without the application being restarted. This means you can end up with a situation where RenderWithVisualStyles returns the wrong value compared with what's being drawn on screen.

Of course, most people probably won't change this setting often with your application open and it will correct itself as soon as the application is restarted, but it's still something to be aware of.

Monday, August 07, 2006

Make Asynchronous User Interface Programming Easier

So it's becoming quite popular to write 'Asynchronous user interface code' these days. Particularly in 'Smart Clients', where, often a Windows Forms application, calls a web service that may take a noticeable amount of time to return it's result.

This is great from the user's perspective, the user interface remains responsive, the application won't be reported as 'not responding' by Windows or Task Manager, and the user may be able to perform other operations in the same application while waiting.

The problem is while it's simple, it's also quite tedious, to write multi-threaded event handlers for buttons. First you have to start your thread, using either a new Thread object, the ThreadPool object or a BackgroundWorker component. Then you have to perform your long-running operation on that other thread, using a delegate. When that's over, if you want to display the results, you need to use Control/Form.Invoke passing another delegate to a function that will load the results back into the user interface.

Ok, so as I said this isn't hard, but creating (probably) two methods per button, creating and starting the other thread , and then invoking the second method is a lot of work if you have a lot of buttons you want to run Asynchronously.

The good news is, you can simplify this by writing your own Asynchronous Button control (or you can use mine). Better yet, writing the control isn't hard at all.

First, start a new Windows Control Library project in Visual Studio. Setup the project properties (company name, assembly title etc.) the way you want, and delete the user control VS added automatically for you.

Next, add a Custom Control to the project. Open the code for the control and change it's class declaration so it inherits from Button, like this;


[DefaultEvent("AsyncClick")]
public partial class AsynchronousButton : Button

Notice the use of the DefaultEvent attribute, this tells Visual Studio which event to create a handler for when you double click a control in the designer. We're going to create an AsyncClick event that runs on a separate thread, so we've declared that as our default.

Also, don't forget to rename the default constructor VS created for us, so it's name matches the control name - AsynchronousButton. While we're playing the default code, remove the override for OnPaint too, we don't need it since we've inherited from Button and we're not doing any custom drawing.

Now add a new event called AsyncClick, and replace the inherited Click event like so;

public event
EventHandler<AsynchronousClickEventArgs> AsyncClick;
public new event
EventHandler<AsynchronousClickEventArgs> Click;
Next, override the OnClick method of the parent
class. This method usually raises the Click event.
We're going to change it so that it raises our
AsyncClick event.
protected override void OnClick(EventArgs e)
{
//Check to see if anyone is subscribed to our
//event.

if (AsyncClick != null)
{
//Start a new thread, and use it to run a method
//that actually raises the event. Also note,
//we're using the thread pool here but you might
//want to use some other mechanism for
starting
//the thread.

System.Threading.ThreadPool.QueueUserWorkItem(
new System.Threading.WaitCallback(
this.RaiseAsyncClickEvent));
}
}



Ok, now that's done we need a new private method called RaiseAsyncClickEvent. Add one that looks like this;


private void RaiseAsyncClickEvent(object state)
{
AsynchronousClickEventArgs e =
new AsynchronousClickEventArgs(
System.Threading.Thread.CurrentThread);

//Ensure there's still at least one event handler
//connected to our event.

if (AsyncClick != null)
{
//Actually raise the event.
AsyncClick(this, e);

//Ok, so now we've performed our long-running
//operation,
let's raise the normal click
//event on the same thread the button was
//created on. This gives us the opportunity
//to update the UI after our async
operation.
if (Click != null)
this.Invoke(new
System.Threading.ParameterizedThreadStart(
RaiseClickEvent), e);
}
}



And finally, we need another private method called RaiseClickEvent which actually raises the click event.


private void RaiseClickEvent(object state)
{
if (Click != null)
Click(this, (AsynchronousClickEventArgs)state);
}



That's the control complete ! But we're not done yet, one last step. Add the following class to your project;


public class AsynchronousClickEventArgs
: EventArgs
{

private System.Threading.Thread thread;
private object state;

public AsynchronousClickEventArgs()
{
}


public AsynchronousClickEventArgs(
System.Threading.Thread thread)
{
this.thread = thread;
}

public object State
{
get { return state; }
set { state = value; }
}

public System.Threading.Thread Thread
{
get { return thread; }
set { thread = value; }
}
}



The above class is used to provide parameters to the event handlers for both the AsyncClick and Click events. Note the 'State' property. This can be set to any value (or array/collection of values) you like during your AsyncClick event handler. It can then be read by your Click event handler. For example, suppose your AsyncClick event handler executes a SQL query and fills a dataset with your result. You can set e.State = myDataSet during your AsyncClick event handler, then in your Click event handler you can say DataSet myDataSet = (DataSet)e.State which gives you a strongly typed reference to your data set again. You can then data bind the DataSet to a grid or whatever to update the user interface.


Once you've built your control, all you have to do to use it is;


  • Compile the control library project.
  • Open your applications project, right-click the toolbox and select 'Choose Items'.
  • Browse to the control library project assembly and add it's controls.
  • Add your AsynchronousButton control to your form and set it's properties appropriate.
  • Double click the button and write code in the AsyncClick event handler.
  • If you want to update the user interface after the Asynchronous operation then also attach an event handler to the Click event.
Of course there's a number of other things you can do. My own version of this control has the following additional features;



  • Specify how the thread should be created, New Thread, Thread Pool or Background Worker. The thread or BackgroundWorker component used available as a property on the AsynchronousClickEventArgs class.
  • If the thread is started using a new Thread object, you can specify a name and priority to be assigned to the new thread, using properties on the control.
  • Specify the control should automatically disable itself when clicked (to prevent multiple clicks during an existing operation). A separate property specifies whether or not the control automatically re-enables itself after the Click event completes successfully.
  • Naturally, the control has it's own toolbox bitmap.
  • XML documentation for the properties, methods and events of the control and it's event arguments class.
  • After raising the AsyncClick event, invoke another method on the buttons thread that raises the original Click event. This allows you to place non-UI code (such as database or webservice calls) in the AsyncClick event and then code that updates the UI with the result in the normal Click event.

Visualizers and Remote Debug Sessions

This is a quick follow up to my previous article on Debug Visualizers.

I was having some problems using my new visualizers during a remote debugging session the other day. Every time I tried to use the visualizer I was told the visualizer dll file, or one of it's dependencies could not be loaded. This seemed strange as it had been working earlier in the day.

I realised that 'earlier' I hadn't been in a remote debug session, but rather a local one. I confirmed the visualizer still worked in a local debug session. I decided the problem must be the visualizers weren't installed on the remote PC. I installed them, and tried the debug session again and my problems went away.

It was only at this point I remembered reading that Debug Visualizers work have code that runs on both the debugee and debugger sides, so it does kinda of make sense for the files to be required on both sessions. I guess it's too much work, or too hard for the compiler, for the local Visual Studio to send the debugee Visualizer data to the remote session each time it connects.

In any case, if you're trying to use Visualizers across a remote debug session, ensure the remote PC has the Visualizers installed first.

The Invisible Member

When writing objects that implement or inherit interfaces, it is a rule that you must implement the interface in full. You cannot remove members from the interface or just not provide an implementation for them. Otherwise, someone using your component through the parent/implemented interface may run into an unexpected error because your component has broken the contract it agreed to by implementing that interface.

So what do you do when you've inherited a member that isn't useful in the context of your derived class ? Using C# and .NET you can actually hide the member to avoid confusing developers consuming your component. There are two things to note here;

1. When we talk about 'hiding' the member, we're not talking about hiding it by replacing it with a new implementation, which is the common usage for the term hiding. 'Hiding' a member this way involves using the new operator, where as hiding a member to deter other developers from using it (like we're talking about) uses an attribute.

2. The member is only 'hidden' in the sense that it doesn't appear in intellisense, or the object browser, property window etc. The member can still be used, and it can be found via reflection etc. If someone calls the method or sets/requests the property value, the call will still succeed because the member really does exist. However, since no one knows the property exists unless they really go looking for it, it should only be called if the developer is using the parent object's interface.

As an example, let's look at the TableLayoutPanel control. The TableLayoutPanel inherits from the Control class, which has a Text property, and therefore the TableLayoutPanel must also have one. If you place a TableLayoutPanel control on a form and view it's properties in the property window, you won't see a Text property. If you go to the code view and type

tableLayoutPanel1.Text = "my new value";

you'll find the intellisense doesn't list the Text property, but the code will compile and run. How can this be ? It's because the Text property is hidden using the System.ComponentModel.EditorBrowsable attribute. You can apply this attribute to a class member, like so;

[System.ComponentModel.EditorBrowsable(EditorBrowsableState.Always)]
public string Text
{
get { return text; }
set { text = value; }
}

There are three values for the EditorBrowsableState enum; Always, Never and Advanced.
Always and never are fairly self-explanatory, however, Advanced might require a little more explanation. In the Visual Studio options dialog (tools -> options), under Text Editor\All Languages you'll find a checkbox labeled Hide Advanced Members. If you check this box, you'll see all members whose attribute is set to Always or Advanced (or where the attribute hasn't been applied). If you uncheck this field, you won't see members with the attribute specifying Advanced.

Personally, I always check this box. That way, if I'm using intellisense to look for a member to perform some action or if I get someone else's code from the Internet as an example, I'm not confused by members I can't see.

For members that don't apply to your component you can usually leave/override the member with a blank implementation, or leave the member with the base class' behaviour. If you really don't want people calling a method for some reason you can throw the NotImplementedException from the method or property accessors. Note this exception doesn't exist in the compact framework, and as a consequence Visual Studio sometimes (incorrectly in my opinion) writes code for you that throws new Exception (which isn't recommended by most people, or even by FXCop). You should always change this code to throw the NotImplementedException so code consuming your component can catch this exception explicitly and handle the result gracefully.

More importantly, you need to be very careful where you throw this exception. If you're component is being consumed by a framework, the framework may or may not support catching the exception and failing gracefully which could mean your component crashes the application. If you're component is stand-alone, you should still clearly document the fact the member throws this exception, and you should consider as many foreseeable contexts for your component as possible to make sure the exception never causes a problem it shouldn't.

Friday, August 04, 2006

Get Superman Like X-Ray Vision with New Debug Visualizers

Visualiser Basics

Ok, so if you don't know what a visualiser is or you want to know how to build one, you're in the wrong place. A Google search ought to satisfy your curiosity, or you could try the Dr.Dobb's Portal article, which seems fairly comprehensive. I also highly recommend Frans Bouma's blog : Tips for writing Debugger Visualizers in VS.NET.

If you just want to download my new visualizers and try them yourself, go here.

Why Build New Visualizers ?

To be honest, I've only just woken up to how cool visualizers are. The company I work for has been using Visual Studio 2005 for about two years now (starting with beta 1) to produce two major products. We discovered visualizers early on, and even went to a TechEd session last year that was largely about visualizers. We weren't that impressed though, mostly because the visualizers that come with Visual Studio, while both functional and useful to a point, aren't exactly cool. Since the standard ones aren't particularly interesting and we were busy building our own products, we had little interest in spending time to write our own visualisers. We simply used the ones available and lived with their limitations.

This changed recently, when for about the millionth time, I had to write an XPath query against a piece of foreign Xml. I used the XML visualiser to bring up the Xml during my debug session (I often code in edit and continue mode, if only because I'm too lazy to hit the stop button). Of course the Xml document was quite large and the visualiser didn't load all that quickly, then when it did display I had to get rid of the SP2 security warning at the top of the browser control. At this point I was now able to view the node heirachy and attributes names etc. Cool.

Unfortunately, visualizer dialogs are modal. So having looked at the Xml I was forced to close the visualizer before I started writing my XPath query. Of course I got interrupted, and when I got back to my desk I couldn't remember the Xml structure again. Why, oh why, can't I do something useful with the Xml visualizer like write and test XPath queries using it ? Or search a large document for a particular node/attribute name or value. Even copying & pasting Xml from the standard visualizer doesn't work well, since copying from the browser control includes all of the +/- symbols from it's tree display. Worst of all, you can't use the standard visualizer on most of the Xml or XPath objects (XmlDocument, XPathDocument, XmlNode, XPathNavigator etc.)

The frustrations don't end there. The text visualizer suffers from similar problems, no search, save or print functions (search being the most useful). There is no image visualizer, and both myself and two of my colleagues have had to deal with retrieving images from web services, databases, or modifying images with drawing commands, and an image visualiser that could cope with visualizing strings, streams and images would have been useful in all those situations.

Building the New Visualizers

With my frustration mounting, I searched the web for a new Xml visualizer. There are several around, and they look quite good - except they don't do what I want. All of the Xml visualizers I found display the Xml in some graphical view, which is very pretty and perhaps good for navigating through a document, but not what I need. Some of them also explicitly state they don't work well with large documents. In the end, I decided to write my own Xml visualiser.

I started by first creating the Image Visualiser, figuring it would be the simpler of the two (and it was). I later updated this to support saving the image to disk or copying it to the clipboard.

Now that I knew what I was doing (sort of), I set about creating the Xml Visualiser. This was good practice for me anyway, since I got to try out some globalisation/localisation tools and windows forms controls that I haven't been able to use in my own projects at work for technical reasons. In any case, I now have a visualizer for Xml that will do everything I want and a bit more besides (apply an XSLT and view the result etc). It will even view XmlNode or NodeList objects (it applies a temporary root node to the visualized data so it can be treated as a valid Xml document).

Nothing Is Ever Easy

I was about done at this point, but then realised I had never much liked the UI for the Managed Debug Assistant for Exceptions. It doesn't show the inner exception unless you click on View Details, and even then all the text for the stack trace and so on is shown in a really small font, in a grid cell. This makes it hard to read, as well as copy and paste etc. I decided to make a visualizer for exceptions. This was where I hit my first snag.

I expected I could specify System.Exception as a target for the visualizer, and then visualize any object that inherits from it. Sadly this was not the case. Confusion struck momentarily when I realised that I could specify System.IO.Stream as a target and visualize derived objects. The key seems to be that System.IO.Stream is an abstract class and can't be instantiated, so the system assumes you must want to visualize derived objects. For objects that aren't abstract, the reverse case is applied, only variables of the target type explicitly listed in the
DebuggerVisualizer attribute can be visualized and not derived classes. I've reported this to Microsoft through the Visual Studio Feedback Centre and have suggested they add a boolean parameter to the attribute to specify whether derived types should be allowed. I can only hope it will be fixed in the future. For now, I've given up on the idea of an exception visualizer.

The same problem also limited the usefulness of a collection/list/dictionary visualizer I tried to build, since it also applies to specifying an interface as the target type for visualization.

My second problem was related to error handling. I wanted to protect the user's Visual Studio/debug session from any problems occurring in my visualizer. I didn't need anything too fancy in the way of error handling though so I put a try/catch in all my routines with a simple message box to display any errors that occurred. This was a bad idea inside the VisualizerObjectSource classes, since Visual Studio will timeout if methods in these objects take too long. Too long being determined by Visual Studio, and usually being less than the time the message box was displayed on screen for.

The end result was that everything either crashed in a nasty way or gave you a series of rude error messages that left you with a sour taste in your mouth. Instead, I simply changed this routine to catch and hide the errors, and return an empty VisualizerObjectSource object, which means the visualizer runs without crashing but simply doesn't show any data.

Later when I have more time I might change the code so the error message is placed in a property on the VisualizerObjectSource and then displayed in the visualizer. Since errors shouldn't occur anyway though, I haven't worried too much about it for now.


But Wait... There's More !

I thought I had pretty much covered my bases at this point. I wasn't interested in building a text visualizer since I didn't use the standard version much and all it was really missing was a search option. I figured I could easily get around that by copying and pasting text from the text visualizer into notepad. Of course, that same week I needed to use it several times, got feed-up again, and so I built my own.

This was also a good excuse to try out the new PrintDocument and PrintPreview controls which I hadn't used previously. They're pretty cool, but not very interesting if you have SQL Report Services or Crystal Reports at your disposal.

I was certain I was done now. Turns out I was wrong again. Surprising, I know. I think it happened once in 1984...

One of our projects at work deals heavily with Message Queue. Most of our messages have their contents compressed with Gzip, which means the body/bodystream properties are boring unless they are first decompressed, and it's pretty hard to get a good view of a message object simply using the Debug Tips anyway. We also frequently find, during development, that we'd like to delete just one or two messages from a queue, rather than purge the whole queue. This is something that MSMQ explorer doesn't allow you to do.

I built the MSMQ Message visualizer pretty easily, although it appears a System.Messages.Messsage object isn't serializable, so I had to create a VisualizerObjectSource object and manually pull out the properties I was interested in.

The MSMQ Queue visualizer was a little different. I had the same problem with the queue object not being serializable, but that only took a bit of typing to solve, nothing new there. When I started thinking about the user interface and what I wanted to be able to do with the queue though, I realised I might well want the same features outside of a visualizer (perhaps in a support tool, or a stand-alone exe for use on our test systems that don't have development tools). Therefore, I decided to create a composite control to do most of the work around displaying and managing the queue, and the visualizer dialog just plays host to the control.

The new visualizers have actually made my experience with debugging our MSMQ code a lot more pleasant, although I still haven't added all the features I'd like to either the queue explorer control or the message visualizer. I'm also not entirely happy with the performance of displaying each of the visualizers, and I've had a few issues I can't explain when using them during remote debugging sessions. The extra's can wait for now though, and I'm working on the performance/remote debugging issues.

In any case, having built the visualizers (and been told how cool the Xml one is by one of my colleagues), I've put them up on the web for download. Just make sure you're ok with the license agreement before you use them. It basically says I'm not responsible for anything :)

A Family of Forms

Occasionally you might want to make a form the child of another, or the child of a control. In VB6 you were pretty much limited to using an MDI parent form or the Win32 API to parent windows. In .NET, you now have another choice.

Without resorting to Win32 API's, you can make a form the child of another form or control in two ways.

The Controls Collection
Since System.Windows.Forms.Form inherits from System.Windows.Forms.Control, each form is in fact a control. The Control class exposes a Controls property, which is a collection of it's child controls. Since a form is a control, you can add a form to the Controls collection of any other control. For example, say you had Form1 containing a panel control called Panel1, and you wanted to make Form2 the child of Panel1, you could achieve this with the following code;

Form2 f2 = new Form2();
f2.TopLevel = false;
Panel1.Controls.Add(f2);

It's important to note the following line;

f2.TopLevel = false;

If you try to make a top-level form a child of another form or control you'll get an exception thrown. By explicitly setting the child forms TopLevel property to false before adding it to the controls collection, you'll avoid the error.

Unfortunately, that's not the end of the story though. Once you've made your form a child, it's behaviour will change slightly. Certain events, such as the Activate and Deactivate events won't fire, and you may experience problems with other features such as KeyPreview. In effect, if you're making an existing form (or a form that inherits from a specialised form class) a child, you'll need to test it thoroughly as a child window to ensure it hasn't lost any functionality.

Using this technique is the only 'managed' way you can make a form the child of a control (rather than another form), and it has the benefit of working with both controls and forms.

MDI Windows
You can also create parent/child relationships between Forms using the MDI properties of the Form class. To create the parent form, set it's MdiContainer property to true. To create a child, set it's MdiParent property to the parent form's reference. For example, the following code would make Form2 the MDI child of Form1;

this.IsMdiContainer = true;

Form2 f2 = new Form2();
f2.MdiParent = this;
f2.Show();

This method isn't without it's problems though. Firstly it appears that MDI children are always drawn with the FixedSingle form border style when they are initially displayed, regardless of the property setting on the form in question. The window is then redrawn using the correct style, so you might not notice this symptom unless the PC is slow or under load when the window is displayed.

The second problem is you may still find some events don't fire exactly the same way as they used to when the form wasn't an MDI child.

Finally, you may also encounter a Win32Exception with the message Error creating window handle. If you search the web you'll find a number of possible causes for this, including;

However, I am currently having this problem in one of my applications and it isn't explained by any of the above causes. I haven't yet been able to diagnose the reason for this behaviour.

So, while you have a couple of ways to parent controls and forms, be careful about how you choose to do so and be sure to test your code well.

A CE Database Alternative

After all my wailing and nashing of teeth last week about SQL mobile not performing well, and our other CE database engine options looking limited, we've now found a possible alternative.

VistaDb is a fully managed database engine, with SQL parser, transactioning, triggers, stored procedures etc. Best of all, version 3 (currently available as a CTP if you're prepared to pay) is supposed to be both compact framework and Mono compatible.

Unfortunately finding a CE database engine has fallen down our priority list so we're not about to fork out for the CTP at this point, but we have VistaDb scheduled as our first stop when we get back to this problem.

We've looked at VistaDb once before, for use in another project for keeping a small database on each client. We didn't want to have to install and manage SQL Express, so we considered VistaDb which has worked quite well in our prototypes. Unfortunately at the time we only have version 2.0 available and that wasn't compact framework compatible so we kinda forgot about it until now. We also haven't done any intensive analysis or performance testing on it, but it has worked well enough so far that we're hopeful it will solve our CE problem.