Saturday, November 18, 2006

On-screen Keyboards

Update 18/01/2011 : Check out this code project article if you're interested in an on-screen keyboard implemented in WPF; http://www.codeproject.com/KB/miscctrl/Virtual-WPF.aspx

Recently, I wanted to create an on screen keyboard for use with our POS. Actually, I wanted a full keyboard for our 'kiosk' mode, and either a full keyboard or more likely a numeric keypad for the POS mode when running with a touch screen.

So, how do you create a virtual, on screen keyboard ? Creating a form/usercontrol with buttons or some other control(s) on it to represent the keyboard is relatively simple. What's difficult is knowing where to send the key data when the control is clicked, because when you click on the virtual keyboard it tends to get focus, taking it away from the input field that needs the data.

Googling the problem I found several samples that simply raised an event from the on screen keyboard form/control, but that still leaves you with the issue of passing on the key data to some unknown edit field. Most of these samples assumed the keyboard control was either on the same form as the field receiving the key data, or that a reference to the control could be provided, which is fine if you only want to deal with forms within your own application, but sucks otherwise.

A few other samples worked by using API's to obtain the handle of the control that currently has focus before the keyboard form/control is activated. When the buttons on the virtual keyboard are clicked, the key data is sent to the 'remembered' window handle and then the original window is refocused using API's, or the window is refocused first and then SendKeys is used. This doesn't work very well though. If the virtual keyboard window overlaps the client window then you get 'flickering' as the focus changes between the windows. Also it seems some key data just gets 'lost' if you hit buttons on the virtual keyboard quickly, although I'm not sure why.

In the end, thanks to two http://www.codeproject.com/ articles and another Google search, as well as some trial and error on my part, I managed to find the 3 magic things you must do to make an on screen keyboard work. These are;

  1. Show your virtual keyboard form without 'activation', so it doesn't get focus when the keyboard is shown.
  2. Intercept the WM_MOUSEACTIVATE message and return MA_NOACTIVATE as the result.
  3. Most important of all - you must NOT use controls that can get focus to represent your buttons.
The third point is the one that I missed for quite a while. Say you place a button control on your form for every 'key' on the virtual keyboard. You've overridden the WM_MOUSEACTIVATE event so clicking on the form won't activate it - but you haven't overridden the individual button control's so they can still get focus, and when they do they will cause the parent form to be activated as well. If you use a label, picture box or some other control that can't get focus though, then the form won't get activated even when the control is clicked - which means you're free to use SendKeys to pass on the keystrokes. This works exceedingly well, there is no flickering, and none of the keystrokes seem to get lost. Best yet, the code is relatively simple.
So, how exactly do you build the virtual keyboard ?
I recommend you grab a copy of FoxholeWilly's keyboard control from CodeProject at http://www.codeproject.com/cs/miscctrl/touchscreenkeyboard.asp
This gives you a nice looking, resizable, keyboard. It also supports both alphabetical and QWERTY layouts, as well as a 'kids' version. It doesn't have the Numeric Keypad like I wanted, but with a little image editing and some tweaking of the code it's easy enough to add.
Unfortunately, this control doesn't take care of points 1 and 2 from before, or actually pass on the key data for you. To do this, place the control on a host form and connect the control's
UserKeyPressed event to an event handler. In that event handler place the following line of code;
SendKeys.Send(e.KeyboardKeyPressed);
Because FoxholeWilly's control already provides a SendKeys formatted string as an event argument, that's the only line of code needed to pass on the key data.
Now, to prevent the form receiving focus or being activated when it's shown, simply override the CreateParams property on the host form, like this;
private const int WS_EX_NOACTIVATE = 0x08000000;

protected override CreateParams CreateParams
{
get
{
CreateParams createParams = base.CreateParams;
createParams.ExStyle = createParams.ExStyle | WS_EX_NOACTIVATE;
return createParams;
}
}
Finally, you need to prevent the form getting focus or being activated when it or the keyboard control are clicked on. This is as easy as adding the following code to the host form;
private const int WM_MOUSEACTIVATE = 0x0021;
private const int MA_NOACTIVATE = 0x0003;
protected override void WndProc(ref Message m)
{
//If we're being activated because the mouse clicked on us...if (m.Msg == WM_MOUSEACTIVATE)
{
//Then refuse to be activated, but allow the click event to pass through (don't use MA_NOACTIVATEEAT)
m.Result = (IntPtr)MA_NOACTIVATE;
}
else
base.WndProc(ref m);
}
This code actually came from a comment posted by Dirk Moshage, on Randy More's CodeProject article (http://www.codeproject.com/samples/onscreenkeyboard.asp) about creating an on screen keyboard. However, because Randy's keyboard project uses button controls to represent the buttons on screen, most people found the above code didn't work properly because the child windows (buttons) still got activated, and then activated the parent anyway. In our case, neither FoxholeWilly's keyboard control or the picturebox it uses to show the keyboard can get focus, which means the WM_MOUSEACTIVATE code actually works the way it's intended.
That's all there is to it. Once you have your host form and control configured, you can run the project. Simply put keyboard focus in any control on any window, and use the mouse (or your touch screen) to click on your virtual keyboard. As you click each virtual button, the appropriate key stroke is sent to the control that currently has focus, and the focus remains where it is.
Of course there's a lot more you can do to improve your keyboard, such as adding a sound when keys are clicked on or implementing 'type rate' so keystrokes are sent repeatedly while the mouse button is held down over a particular key. For a basic keyboard though, you should have all you need.

Monday, September 04, 2006

Why Help Usually Doesn't, and How To Do It Better

I'm neither a technical writer nor an expert on documentation and help systems. As a user and an IT professional who observes other, less computer literate users, I've noticed a few things about most documentation that are the cause of hair loss for many people.

The most common advice given to people writing help or documentation is, pretend you're computer illiterate. Imagine you don't know anything about computers. This is either harder than it sounds, or people's imaginations aren't what they used to be.

Better advice, I think, would be to ask yourself some basic questions :

1. Where is the option/feature accessed from ?
2 Why would I use it ?
3. What happens when I use this option, and is it different sometimes than others ?
4. Which data/documents/windows are affected ?
5. When can/can't I use this option ?

Let's look at an example. Take the typical File -> Save menu item. Documentation for this often looks like the following;

Save Menu Item

Saves the current changes.

Great. Doesn't tell us anything that 'Save' on the menu didn't already. Does the save option save only the active document, or all documents ? Is it disabled if there haven't been any changes since the last save ? What if the file is read-only, or if it was opened in read-only mode ?

More importantly, let's look at the typical behaviour of the Save menu item. Let's say Bert starts a new document, he types away and then he wants to save his work. He finds the File -> Save option, but he's confused about where it's going to be saved. He wants to save it to a floppy (he's a typical home user and is still running a 286 he bought from Noah, since he doesn't need a PC that works faster than he thinks). He checks the help, which only tells him it saves his changes. Now he's confused. He still doesn't know where his data's going to be saved, and his document is new, so if File -> Save only saves changes is he looking at the wrong option ? How does he save a new document ?

Ok, so he decides to be brave and try the option anyway. Now he gets presented with the typical Save File dialog. Two problems here, one, this likely isn't documented in his application's help since it's considered 'Standard'. If he wants help on how to use this dialog to save to a floppy disk he's going to have to read the Windows documentation, which he probably doesn't realise, probably doesn't have (second hand PC/OS ?) and probably won't do anyway. Plus, he's a user, we're lucky he's reading the documentation for the application in the first place. In fact, we're lucky he can read !

Now, let's say he works out the File -> Save dialog and manages to save his document to a floppy. He closes down his PC and doesn't think about it any more for a week.

Now he's back and he needs to do some more work on his document. Let's just assume he manages to find the document on the floppy and open it (yeah, right). Bert now makes his changes, and he wants to save them butt his time to a new file so he can keep the original. He happily clicks File -> Save knowing the save option presents him with the Save File Dialog - Oh wait ! It doesn't ! Worse yet, he's just over-written and lost the original. There will be tears before bedtime.

Ok, putting aside the possible design issues with the open/save dialogs, the File -> Save option itself, not to mention the whole drive, disk and file system tree which users often don't get, good help could've prevented a lot of frustration here.

Based on our original questions we could write;


Save Menu Item

Location
This option is located on the File menu, on the menu strip at the top of the main window.

Purpose
Use this option to save a new document, or changes to an existing document. By saving your changes they can be recalled again later, even if your computer has been turned off in between times.

Usage
When this option is selected;
  • For new documents the Save File dialog is shown, allowing you to choose where to save your document and what it is called. You can also select various formats to save your document in. For more help, see your Windows manual.
  • For existing documents, or documents that have already been saved, the file is saved again using the last location, name and format. There will be a slight pause when the document is saved, and the status bar at the bottom of the screen will show 'Document saved'. If you want to save your file to a new location or a different name/format then choose the Save As option on the File menu.

If an error occurs during the Save operation, a message box will be shown explaining the error to you.

Effects
The save function affects only the active window.

Not Allowed When
This option is disabled (shown with grey text and can't be clicked) if there are no changes to save.


This is fairly generic help (for a generic situation), and it isn't perfect. A better idea than directing the user to the Windows documentation would be to document the dialog ourselves, or provide an actual hyperlink to the Windows help page for the specific item we're discussing. Hyperlinks or pop-ups for other items, such as the 'Menu Strip' and 'Active Window' concepts would also be a good idea, as well as a link to a 'concept' page on different 'formats' might also be useful. A picture speaks a thousand words too, so a screen shot showing the Save menu item expanded on the File menu and possible what the 'active window' looks like might also be useful.

That's beside the point however, because the help we have given is useful because in several situations, unlike the original help we had.

First of all, we've told the user where the item is, so if they can't find it at all, the help will direct them there. Of course if there were any circumstances when this item was made invisible or moved to another menu (perhaps on child window) then we should note that here. It's important to remember that users don't always find items in the help based on context, some users actually read from end to end or browse manuals and help files, others may have come here from a link etc.

Next, we've told the user what this function does. With the Save option this should be self-explanatory to even the least computer literate user, but we can't take that for granted. Note that we do mention here that the function can be used on both new and existing documents (changes).

The Usage section is possibly the most important. Here we tell the user exactly what to expect when they click the button, and how to tell if there was a problem or if the function succeeded. We cover the fact that the function does different things the second or subsequent times it's used, or for existing documents rather than new ones.

The Effects section tells the user what the scope of the operation will be. This is important, especially in MDI application where a novice user might expect 'Save' to save all the open documents.

The Not Allowed When section is possibly the second most important section, even if it's the last one. This gives the user the all important information about when/why the option is disabled, and what to do about it. Of course, you could also help them out with this by implementing my Disabled Tooltip Component for .NET.

So we've gone from one not very useful line of text to several paragraphs of information that are all useful at different times - and all this for a relatively standard function that's available but poorly documented in most software !

That brings me to my next point - standard functions. It's generally not good enough to say "I don't need to document this, it's a standard so everybody knows it". Apart from the 1% of users who might be using your application as their first/second/third even program and might not have figured out or had the standards explained to them, there's a problem in that unless 100% of software acts exactly the same way then some users are still going to be kept up at night wondering if they really should click that button or if your program is one of those weird ones that works differently.

Maybe you've tried to simplify your program by removing the Save As function and always showing the file save dialog. Maybe you've built your own dialog to remove unnecessary complexity in the existing one. Possibly 'Save' doesn't make sense in the context of your application, or it automatically saves changes after each one is made. All of these situations need to be documented, as does the 'standard', if that's how your application works - because someone else's program might be different and that might be what your user is used to.

The second part of this rant is about help systems themselves. It seems to me the help systems we have today are pretty terrible. Of course better natural language queries would help, although the Microsoft Office help systems do an ok job of this most of the time. The problem goes deeper than that though.

First of all, most help systems are extremely slow to load compared with applications, and the bigger the help the worse it gets. Ever hit F1 by accident in Office, SQL Server, Visual Basic or Visual Studio .NET ? There's usually enough time to make get your cake and eat it too.

I also think it sucks that most helps systems are 'compiled', and included in the compilation is the display format as well as the help data. If you go looking for systems to generate help for .NET assemblies from the .XML documentation files the compiler can create you'll find lots of them that use plug-ins or style sheets to achieve different looks and feels. That same applies to 'user help' creators. Why ? Why isn't the help content stored in a database, or better yet some open standard like XML, and then read directly by the help system with whatever 'style' the user has chosen as their preference being applied when the data is displayed ?

Also, since most help doesn't make sense to users, why can't they update it themselves once they've worked out what's going on ? I guess this should be sort of like a Wiki, but there needs to be two or three levels of 'edit'.

There needs to a 'local' edit which can be made just on the users PC or for users on that local LAN. This would allow them to make general internal comments just for themselves, either that make sense to them (without having to be in pristine English) or with their own internal company policy notes about usage.

The second level could possibly be a more 'global' edit where the change is posted to the web and available for all users to see. This is more like the Wiki concept.

The third style of edit would actually be a support incident. Like Microsoft's knowledge base or help that says, "Did this page help your solve your problem ?", but instead of just taking a boolean response it should allow the user to key in their problem/question and have it posted to a support desk. The support person receiving the problem can then post a response which can be sent back to the original user, and optionally (based on the support agents choice) posted as an addition to the help on-line.

The key factor here though is that help, both the original and the changes, must posted back to the user's PC. I am always frustrated by software that only has 'on-line' help, whether this is just pages on their site or something fancier. Sometimes people use software on laptops (or even desktops) without Internet connections, some people are still using dial-up and don't want to have to wait for a connection (or pay their ISP for the time/traffic spent getting help for your application).

What if your application only runs when there is an Internet connection available ? Well you'd still better make your help file available off-line and outside of your application. Bert may be telling his friend Earnie about how great it is and then Ernie asks if it supports feature X. If Bert doesn't know but he has his laptop then he's likley going to try checking the help to find out. It's not going to be a good look from either Bert or Ernie's perspective if he can't even find out the answer to that simple question because he's not connected right now.

Personally, I'd also like better interconnection between help files. I really don't want to have to document the open and save dialogs in every application I write, even if I can write the documentation once and 'link' the source files into seperate help projects. I need a good, simple way of linking to the existing Microsoft help, or a common help file of my own. I also need the link to be super fast when the user clicks it, I don't want them waiting for the entirety of the help for setting up active directory and burning CD's to load when all they want to know how to do is browse for drives in the Save dialog.

Finally, all help systems should have a 'favourites' option (which Microsoft have been pretty good about implementing recently). If only I had a penny for every time my father said, "I know I did this last week, I just can't remember exactly how..." !

So, how good's your documentation ?

Have you got any ideas about help systems ? Disagree with me ? Why not leave your comments here...

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.

Saturday, July 22, 2006

Framework Incompatibility Proven

A few days ago I made a post about a problem my boss and I discovered when running a SmartDevice project's binary on our desktop PC's. Our understanding is the .NET compact framework and full framework are supposed to be 100% compatible, so SmartDevice applications will run on desktop operating systems. This seems to be true until you host a web browser control in the SmartDevice project, and then you run into issues relating to the process threading model.

While we suspected the problem was an incompatibility, we couldn't' be sure because we didn't have a Windows CE device on which to test the application. The problem didn't occur in the PocketPC emulator that shipped with VS - but that isn't a guarantee the problem won't occur on an actual device (although it is pretty good circumstantial evidence).

A friend of my owns an HP iPAQ running the PocketPC 2003 SE version of Windows CE, and today he allowed me to borrow it for some testing. I created a sample SmartDevice project, which consisted of a single form hosting the mobile version of the web browser control with it's URL set to the Microsoft home page.

I compiled the application and tried to run the binary on my development PC. The application crashed with an unhandled exception, when creating the WebBrowser control, stating the control can only be hosted in single threaded apartments (which doesn't seem to be possible in compact framework applications).

I then installed the compact framework 2.0 SP1 on the iPAQ, downloaded the program and ran it. The program not only started and displayed the form successfully, but the WebBrowser control displayed the Microsoft home page after a few seconds.

So it seems you cannot be guaranteed SmartDevice projects will run on both desktop operating systems and devices. I've posted this problem on the Visual Studio Feedback site, but haven't got a response yet. Which leaves the question, how do you easily develop applications that will run on both platforms ?

Thursday, July 20, 2006

.NET Framework Incompatibility ?

It looks like my boss has discovered an interesting incompatibility between the .NET Framework and the .NET Compact Framework (version 2.0).

We haven't actually confirmed this yet, but I'll describe the symptom and our suspicion about the problem now, and post a follow up if we find out what's really going on. If anyone else knows, or has alternative theories, please leave a comment 'cause I'd love to hear what you think.

Ok, so here's the background to the problem;

My boss has been working on a SmartDevice project in VS 2005. Specifically the project is designed to run on a custom build of Windows CE 5.0 on some hardware we've bought from Taiwan. Our problem has been that we've been unable to successfully build a version of CE (or even an emulator) that works. We've tried outsourcing this part of the project, but without any success. While we continue to try to build our own CE platform, development on our application software has continued. That's beside the point, except that it explains why we're building our project the way we are.

Since we have no CE platform or emulator (yet), we're building the (Smart Device) project, compiling it, and running the executable on our desktop PC's (running XP) to test it. If we really want to debug it, we compile it with debug symbols and attach the debugger to the exe after it's started. This has been working really well for us so far, and it should since exe's created with the compact framework are supposed to work %100 on desktop PC's with the full framework. As a side note, the compact framework is also installed on the desktop PC (it seems to have been installed with VS).

The problem we've discovered occurs when we add a web browser control to our Smart Device forms. The (full framework) .NET web browser control will only load in an application running in Single Threaded Apartment mode. This is normally specified in an application by applying the STAThread attribute to the Main procedure in Program.cs. Unfortunately, we can't find STAThread anywhere in our compact framework application or it's references (using intellisense or the object browser), and by default VS has marked the main method with MTAThread. We therefore presume STAThread is not supported in the compact framework.

The compact framework .NET version of the web browser control must therefore support applications with the MTAThread attribute applied. We tested this by creating a new SmartPhone application, placing a web browser on it's form and running it inside the standard SmartPhone emulator. The application and it's form loaded fine, and the web browser control didn't throw any exceptions. We couldn't run the binary for the SmartPhone application on XP though. I'm not sure if this is because of the problem we've discovered, or because SmartPhone applications aren't supposed to run on XP.

In any case, if we run our actual SmartDevice application on XP it crashes as soon as we load the form with the browser control on it. We get an exception stating the control cannot run in a process that is not a single threaded apartment.

Given that;

  1. We can load and use the (compact framework) browser control in a project running under an emulator;
  2. We can run and load our application on XP if we're not using the web browser control;
  3. The full framework web browser control will only run in STA mode but the CF control appears to support MTA...

We suspect the problem is that at runtime the application is loading the full-framework version of the web browser control - even though that's not the one we referenced in the project itself. Since the controls are in fact different (the STA/MTA issue), this causes the application to crash. The end result is that compact framework applications aren't necessarily executable on desktop operating systems with the full .NET framework.

If this is the case, it's possible we could implement some kind of assembly binding policy to force the application to use the compact framework control, but I haven't done much work in this area and I'm not sure how, or if it will work with a compact framework executable.

Of course we're just guessing at what the problem is. If we had a working version of Windows CE we could try our application on it and see if it ran. If it did, that would go along way to proving the problem wasn't with our application or code, and that it is a compatibility issue. Until we can do that though, we can't be sure what the issue is.

I've posted this issue on the Visual Studio Feedback site so we'll have to wait and see what Microsoft say about it.