Archive for the ‘User Interface’ Category

Making auto suggestions more appropriate for your textbox

Friday, May 1st, 2009

Screenshot showing the textbox providing a list of suggested phone numbersTo round out my coverage of Input Contexts we need to cover the concept of Input Classes. Put simply an input class is a hint to the Windows Mobile OS as to what kind of information should be entered into a text box (for example an email address, phone number, or person’s name).

Selecting an Input Class for a textbox will change the source of data used for the auto suggestions provided by the OS as the user starts typing. For example in the screenshot above you can see the user is typing in a person’s name yet the operating system is suggesting matching cellphone numbers.

To specify the Input Class for a textbox we can use the native SHSetInputContext API and set the SHIC_FEATURE_CLASS feature. Yesterday’s code sample provided a suitable wrapper around this API for this purpose.

Sample Application

[InputContextSendSMS.zip - 10.8KB]

To demonstrate the use of Input Contexts I decided to make a modified version of the SendSMS application I presented earlier. In this modified version when a user starts to type in a person’s name into the phone number textbox the operating system will provide suggested phone numbers based upon data found within Pocket Outlook’s contacts list.

The code changes required to the original sample application were minimal and consisted of the following two lines of code which use the InputClass developed yesterday.

// Setup the input context for the phone number text box
// so that it will provide auto suggestions of phone numbers
// associated with outlook/SIM contacts.
InputContext.SetClass(txtPhoneNumber, InputContextClass.Phone);
InputContext.SetHaveTrailer(txtPhoneNumber, false);

The call to SetHaveTrailer means that if the user selects a suggestion from the popup list the OS will not automatically insert a seperator character. If you set this feature to true a semicolon would be inserted allowing the user to quickly create a list of two or more recipients.

Programmatically set Input Context features

Thursday, April 30th, 2009

In my last blog post I discussed how an end user could change word completion features at a system wide level. This blog post covers how a .NET Compact Framework developer can programatically change the behaviour on a textbox by textbox basis.

The Windows Mobile operating system provides a function called SHSetInputContext that enables a developer to configure the various properties of the input context associated with a given control.

By using a DllImport declaration we can gain access to this native function from our C# application as shown below:

using System.Runtime.InteropServices;
 
private enum SHIC_FEATURE : uint
{
  RESTOREDEFAULT = 0,
  AUTOCORRECT = 1,
  AUTOSUGGEST = 2,
  HAVETRAILER = 3,
  CLASS = 4
};
 
[DllImport("aygshell.dll")]
private static extern int SHSetInputContext(IntPtr hwnd,
  SHIC_FEATURE dwFeature,
  [MarshalAs(UnmanagedType.Bool)]ref bool lpValue);

Once declared it is as easy as passing a boolean true or false value to SHSetInputContext along with the name of the input context feature we want to configure.

For example to disable auto suggestion for a textbox called “textbox1″ we could use the following code snippet:

bool value = false;
SHSetInputContext(textbox1.Handle,
  SHIC_FEATURE.AUTOSUGGEST,
  ref value);

Sample application

[Download InputContextDemo.zip - 10.6KB]

A sample application is available for download. It wraps up the calls to SHSetInputContext into a handy InputContext class that can be re-used within your own applications.

The sample application has a single textbox and a series of checkboxes. The checkboxes enable you to control the various input context features present within the Word Completion tab of the Input control panel applet for the textbox.

We’ll round out our discussion of Input Contexts by covering Input Classes within the next blog post. In the mean time see if you can determine what changes in behaviour occur when you select the various options within the Input Class combo box.
Screenshot of sample application demonstrating setting Input Context features

Creating custom animated wait cursors

Friday, April 4th, 2008
Screenshot of standard Windows Mobile 6 wait cursor Screenshot of example animated wait cursor Screenshot of example animated wait cursor

Most .NET Compact Framework developers will be familiar with the Cursor.Current property and how to display the standard Wait Cursor, but did you know that you could easily display your own custom cursor? This blog entry discusses how you can replace the standard wait cursor with your own application specific cursor. This is an ideal way to brand kiosk style applications for example.

Creating a custom cursor

Recent versions of the Windows CE operating system (and hence Windows Mobile) support an OS API called LoadAnimatedCursor. This API enables you to specify a sequence of individual bitmap frames and will convert them into an animated cursor. For example an animated cursor of a rotating monkey could be made up of the following 4 bitmaps.

101 102 103 104

The more frames the cursor consists of the smoother the animation will be. Individual frames within the animation should be 48×48 pixel bitmap resources within a *.dll or *.exe file. The bitmap resources are identified by a numeric ID and must be in sequential order (such as the values 101, 102, 103 and 104 used in the example above).

The id of the first bitmap, the total number of frames and the period of time to delay between frames is then passed into the LoadAnimatedCursor API which will return a handle to the newly created cursor (an HCURSOR). Passing this handle to the SetCursor API will then make the cursor visible on the screen.

Screenshot showing bitmap resources within the CustomCursors dllUnfortunately the LoadAnimatedCursor function is not as easy to use from managed code as it should be. The API expects the bitmap images to be native bitmap resources meaning you can not store them within a *.resx resource file within your .NET Compact Framework application. The easiest way to store the bitmaps in the correct format is to create a native C++ DLL project. You can then remove all the C++ source files, leaving a sole Win32 *.rc resource file to which you can add the bitmaps to (as will be demonstrated later).

Sample Application

[Download animatedcursortest.zip - 37KB]

The sample application available for download consists of two projects. The first (called AnimatedCursors) demonstrates how to create a resource only DLL that contains the bitmap images required for the two custom cursors shown above.

The second project is a C# example demonstrating how to use Platform Invoke to access theLoadAnimatedCursor and SetCursor APIs to display the custom cursors. This second project loads the custom cursors from the AnimatedCursors.dll file built by the first project.

The C# sample wraps up the required Platform Invoke code within a class called AnimatedWaitCursor. This class implements the IDisposable interface so that the following syntax can be used to display a custom cursor. This code structure should be familiar to anyone who has used MFC’s CWaitCursor class.

// Use the animated cursor that has 4 frames starting with
// bitmap id 101, delaying 125 milliseconds between each frame.
string dll = @"\path\to\some.dll";
using (AnimatedWaitCursor cursor = new AnimatedWaitCursor(dll, 101, 4, 125))
{
  // do some long running task
}

How to make your ListView columns reorderable

Tuesday, March 18th, 2008

Another finishing touch that I like to see in applications that use ListViews is the ability for the end user to re-order the columns to suit their own preferences. This blog entry discusses one approach for adding this functionality to the ListView control present within the .NET Compact Framework. Although it is difficult to convey in a static screenshot, the screenshot above shows a user dragging the stylus over the header of the listview control to move the position of the “First Name” column.

Obtaining Draggable Columns

The System.Windows.Forms.ListView control is a wrapper over top of the native ListView control. The native ListView control supports the notion of extended styles, which allow various optional features to be enabled or disabled as desired. One of the extended styles is called LVS_EX_HEADERDRAGDROP. If this extended style is enabled the user can re-order the columns by dragging and dropping the headers shown at the top of the listview while it is in report mode.

Although the .NET Compact Framework ListView control does not expose a mechanism to enable extended styles, we can use a technique discussed in a previous blog entry of mine to add or remove the LVS_EX_HEADERDRAGDROP extended style as desired.

private const int LVM_SETEXTENDEDLISTVIEWSTYLE = 0x1000 + 54;
private const int LVS_EX_HEADERDRAGDROP = 0x00000010;
 
public static void SetAllowDraggableColumns(this ListView lv, bool enabled)
{
  // Add or remove the LVS_EX_HEADERDRAGDROP extended
  // style based upon the state of the enabled parameter.
  Message msg = new Message();
  msg.HWnd = lv.Handle;
  msg.Msg = LVM_SETEXTENDEDLISTVIEWSTYLE;
  msg.WParam = (IntPtr)LVS_EX_HEADERDRAGDROP;
  msg.LParam = enabled ? (IntPtr)LVS_EX_HEADERDRAGDROP : IntPtr.Zero;
 
  // Send the message to the listview control
  MessageWindow.SendMessage(ref msg);
}

This method allows the drag feature to be turned on and off for a given ListView control. Notice that this method makes use of a C# 3.0 feature called Extension Methods. The “this” keyword in front of the first parameter means that this method can be called as if it was part of the standard ListView control, meaning the following code snippet will work (assuming listView1 is an instance of the System.Windows.Forms.ListView control).

listView1.SetAllowDraggableColumns(true);

This is pure syntactic sugar, behind the scenes the C# compiler is simply passing in listView1 as the first parameter to the SetAllowDraggableColumns method.

Persisting Column Order Preferences

Once you have reorder-able columns it can be desirable to persist the user’s preferred layout across multiple executions of your application. It would be a pain if the columns always defaulted back to a standard order everytime the form was displayed.

The native ListView control provides two window messages, LVM_GETCOLUMNORDERARRAY and LVM_SETCOLUMNORDERARRAY that can be used to implement this feature. The code sample available for download wraps up these two window messages to allow you to query the current order of the columns by using a statement such as the following:

int[] columnOrder = listView1.GetColumnOrder();
// TODO: save 'columnOrder' to the registry
// or another persistent store

When columns are added to a ListView they are given an index. The first column is column 0 while the second is column 1 and so on. When columns are re-ordered they keep their index value but their position on screen changes. The array returned by the GetColumnOrder function contains the index for each column in the order that they are visible on screen. For example if the array contains the values 2, 0, and 1 it means that the last column (column 2) has been dragged from the right hand side of the listview to become the left most column.

Once we have obtained the order of the columns we can store the data in any persistent storage mechanism such as a file, a database table, or registry key. When the form is reloaded we can initialise the default order of the columns by calling the equivalent SetColumnOrder method with the value we previously saved:

// TODO: should read 'columnOrder' from the registry
// or other persistent store
int[] columnOrder = new int[]{2, 0, 1};
 
listView1.SetColumnOrder(columnOrder);

Sample Application

[Download ListViewExtenderTest.zip - 11 KB]

The sample application displays a list of three columns. While running on a Windows Mobile Professional device you should be able to re-order the columns by dragging and dropping the column headers with your stylus. If you exit and restart the application you should see that your custom column ordering is persisted. Via the left soft key menu item you can select an option that will disable the user from re-ordering the columns.

Most of the magic occurs within a file you can reuse in your own applications called ListViewExtender.cs. The sample application targets .NET CF 3.5 and hence requires Visual Studio 2008. With minor tweaks the source code would also be usable within .NET CF 2.0 projects. I would be keen to hear what your thoughts are about this. Is it time to shift code samples to .NET CF 3.5/Visual Studio 2008 or are you still wanting .NET CF 2.0 and Visual Studio 2005 compatible samples?

Improvements to the OpenFileDialog class

Saturday, March 8th, 2008

Screenshot of OpenFileDialogEx class running on a Windows Mobile Professional deviceJosé Gallardo Salazar has blogged about an enhanced OpenFileDialog implementation that fixes a number of common complaints with the standard .NET Compact Framework version. The most common complaints being not able to browse outside the “My Documents” folder and the lack of support for Smartphone (Windows Mobile Standard) devices.

As indicated in his blog entry on adding Smartphone support the OpenFileDialogEx class is still a work in progress with many possible areas of improvement. I have taken José’s source code and made a number of changes.

My improvements include:

  • Shell Icon List support – The dialog now uses the same icons as shown in File Explorer. Directories for example may have a custom icon (for example the Games folder shown in the screenshot above).
  • DPI / Resolution awareness – icons change size to match current system settings (i.e. larger icons on higher resolution devices).
  • Support for both Pocket PC and Smartphone – the platform is detected at runtime so a single assembly can alter it’s UI behaviour to run on both platforms. Some UI elements are hidden while running on a Smartphone for example.
  • Back Key support – The Back key on Smartphone Devices now moves up one directory when pressed (just like the builtin File Explorer application on WM6 devices).
  • Sorted Directory Listings – Directory entries are now sorted into alphabetical order. There is provision for the sort order to be easily modified (although this is not yet publicly exposed).
  • InitialDirectory property – This property enables the dialog to default to displaying a directory other than the root of the filesystem.

Sample Application

[Download iconviewer.zip - 22KB]

The sample application available for download demonstrates how to use the OpenFileDialogEx class and demonstrates its cross platform capability. Once you have built the application you should be able to run it on practically any Windows Mobile device and see a file dialog that is tailored to the capabilities of the current device.

One small disadvantage of my changes is the fact that the control is now less “pure”. It relies upon a number of Platform Invoke calls to access some of the system functionality not exposed by the .NET Compact Framework. An interesting note is that .NET Compact Framework version 3.5 removes the need for most of the Platform Invoke code due to new functionality or bug fixes. At this point in time however supporting .NET CF 2.0 is still important to me.

Improving Comboboxes on Smartphone devices

Monday, February 11th, 2008

Screen real-estate is at a premium on smaller Windows Mobile smartphone devices, so it is important to maximise the use of every available pixel in conveying useful information to the user. This blog entry demonstrates a technique to maximise the usability of combo boxes within .NET Compact Framework applications by reusing some of the existing screen real-estate.

Defining the problem

When you place a Combobox control on a form within a Smartphone application you get a control which shows a single item with left/right arrows that allow you to scroll through the list of options.

As an alternative if you press the Action key (middle of the D-PAD) on the combobox the full list is displayed in a fullscreen window. This window however is always labelled “Select an Item”. Because the window is fullscreen it is possible for the user to loose context (they can’t see any labels etc you have placed on your form), and forget what they are meant to be selecting. What we would like to do is to replace the “Select an Item” title with something more appropriate for the current field.

A screenshot showing the behaviour of a standard combo box when in fullscreen mode A screenshot showing the behaviour of your modified combo box when in fullscreen mode
What we get by default What we desire

Developing a solution

This is where having knowledge of the underlying native (C/C++) APIs that implement Windows Mobile is useful. When targeting a smartphone device a .NET Compact Framework Combobox control is actually implemented via two separate controls as far as the operating system is concerned, a 1 item high listbox coupled to an up/down control. The native Win32 smartphone documentation calls this type of configuration a Spinner Control.

Screenshot of Remote Winspy application showing the native windows associated with a combo boxBy using a utility included with Visual Studio called Remote Spy++ we can see this collection of controls. In the screenshot to the left you can see that one of the combo boxes in the sample application is selected, and underneath it you can clearly see the listbox and up/down (msctls_updown32) controls it is made up of.

In order to change the title of the popup window associated with a combo box we need to:

  1. Find the native window handle for the up/down control
  2. Change it’s window title to the desired prompt text

The ComboBox class has a Handle property that returns a native window handle (HWND) that is associated with the managed control. For a ComboBox the Handle property actually returns the handle of the listbox control and not the parent “NETCFITEMPICKERCLASS_A” control as may be expected. This was probably done for native code interop compatibility reasons. So to find the native window handle of the up/down control we simply need to find the handle for the window immediately after the window returned by the ComboBox.Handle property.

Once we have found the window handle for the up/down control we are finally ready to replace the popup window title. According to the Spin Box Control documentation, the popup window title comes from the title of the up/down control, and it defaults to the “Select an Item” prompt if a title isn’t specified. We can change the title of a native window by calling the SetWindowText API.

All these individual steps can be wrapped up into an easy to call method as follows:

using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
 
public static class ComboboxExtender
{
  public static void SetPromptText(ComboBox combo, String text)
  {
    // Obtain the native window handle of the up/down spinner control
    IntPtr hWndListBox = combo.Handle;
    IntPtr hWndSpinner = GetWindow(hWndListBox, GW_HWNDNEXT);
 
    // Set the title of the spinner
    SetWindowText(hWndSpinner, text);
  }
 
  [DllImport("coredll.dll")]
  private static extern bool SetWindowText(IntPtr hWnd, String lpString);
 
  [DllImport("coredll.dll")]
  private static extern IntPtr GetWindow(IntPtr hWnd, UInt32 uCmd);
 
  private const UInt32 GW_HWNDNEXT = 2;
}

Sample Application

[Download ComboBoxPromptExample.zip - 16KB]

The sample application available for download demonstrates using the ComboBoxExtender class developed above. The interface consists of two combo boxes which have been configured identically. The first combo box shows the default prompt text, while the second has had its prompt text replaced via a call to ComboBoxExtender.SetPromptText within the Form’s Load event handler as shown below:

ComboBoxExtender.SetPromptText(comboBox2, "Select Car Colour");

I am a stickler for improving the quality and polish of Windows Mobile applications. This tip is a very minimal code change that can be implemented quickly, yet can have a profound impact on the usability of your application if it is full of combo boxes. A similar trick can also be implemented for Expandable Edit controls.

If you are developing .NET Compact Framework applications that target the Windows Mobile Smartphone (Standard) platform, I seriously encourage you to consider making this usability change to your applications.

Aspect Ratio Picture Box control

Tuesday, January 29th, 2008

Screenshot of sample application showing the use of an AspectRatioPictureBox controlOne feature missing from the PictureBox control within the .NET Compact Framework is the Zoom SizeMode. This mode makes the image contained within the PictureBox as large as possible while still maintaining its original aspect ratio. Luckily this feature is easy to implement as a custom control.

Scaling an Image

The ratio of a picture’s width to height is known as its aspect ratio. An image with an aspect ratio greater than 1 means that it is wider than it is high (landscape image). While an image with an aspect ratio of less than one is taller than it is wide (portrait image). If the aspect ratio is exactly 1 the image is square.

By comparing the aspect ratio of an image against the aspect ratio of our control we can determine if the best axis to stretch the image (in order to maximise its size) is vertically or horizontally. Once we have determined the maximum width or height of our scaled image we can then utilise the aspect ratio to calculate the other dimension in order to keep the same aspect ratio.

Creating a custom control

There are two main types of custom controls within a System.Windows.Forms based application, those that derive from the Control base class and those that derive from the UserControl base class. For this kind of custom control, deriving from System.Windows.Forms.Control is the best approach since we are after a fairly light weight control.

To make a custom control we simply need to declare a class which derives from Control:

using System.Windows.Forms;
 
public class AspectRatioPictureBox : Control
{
}

The code sample above is a complete custom control. Once it is compiled you will be able to place an AspectRatioPictureBox control onto a form, in the same way you would any other control such as a Button or Label.

The control however is quite boring, being a completely white rectangle making it indistinguishable from the form it is placed upon. Before we cover how to paint more attractive contents, we need a way for the user to specify the image they would like to display. The easiest way to do this is to implement a public property by adding the following to our class definition:

private Bitmap photo;
 
public Bitmap Image
{
   get { return photo; }
   set
   {
     photo = value;
 
     // Redraw the contents of the control
     Invalidate();
   }
}

Any public property exposed by our control will be accessible via the Properties window within the Forms Designer when the user utilises our control.

Notice the call to the Control.Invalidate method within the Image property setter? This is important, as this is the call which notifies the operating system that our control has changed state and needs to be redrawn. If the control is not invalidated, the OS will not attempt to redraw it and the user would not be able to see the new image that has just been provided. In general any property which affects the onscreen presentation of a custom control should invalidate it.

Now that we have a way to specify the bitmap we want to display, we can finally implement the code that will paint the contents of the control. We do this by overriding the OnPaint method as follows:

protected override void OnPaint(PaintEventArgs e)
{
  if (photo != null)
  {
    float controlAspect = (float)ClientSize.Width / (float)ClientSize.Height;
    float photoAspect = (float)photo.Width / (float)photo.Height;
 
    if (photoAspect < controlAspect)
    {
      // Stretch the height of the photo to match
      // the height of the control
      ....
    }
    else
    {
      // Stretch the width of the photo to match
      // the width of the control
      ....
    }
  }
}

The framework will call the OnPaint method each time the OS needs the control’s contents to be repainted onto the screen. For a complete implementation of this method see the sample application downloadable below.

Using a custom control

Screenshot showing AspectRatioPictureBox control within the Toolbox of Visual StudioUsing custom controls within your projects is reasonably straight forward. Once you have created your new custom control class you simply need to recompile your project in order for the control to be picked up by Visual Studio and displayed in the Toolbox as a custom control. This is shown in the above screenshot. Once an instance of the custom control has been placed onto a form its properties can be configured via the standard Property window.

Unlike the Desktop development environment, Smart Device custom controls do not directly utilise attributes such as [Description()] to configure their appeance and behaviour within the Visual Studio IDE. Instead Smart Device custom controls utilise what is called an XMTA file. Customising the design time experience of your custom controls will be discussed in a future blog post.

Sample Application

[Download aspectratiopictureboxtest.zip - 25.7KB]

A small sample application is available for download. It demonstrates the use of the AspectRatioPictureBox custom control developed within this blog entry in a number of different scenarios.

Feel free to utilise the included AspectRatioPictureBox.cs file within your own projects. As an added feature an additional property called “Restricted” has been added. When the Restricted property of the control is set to true the image will not be stretched any larger than it’s original size. When “unrestricted” the image will be stretched to take the full width or height of the control, even if that means stretching the image beyond it’s original size. The restricted mode is useful for image viewing applications where you don’t want to stretch small images and degrade their quality, while still ensuring large images are scaled down to fit the screen.

Determining the depth of a node within a TreeView control

Sunday, January 13th, 2008

Given a TreeNode somewhere within a TreeView control, a desktop System.Windows.Forms developer would query the TreeNode.Level property to determine the depth of the node within the tree as demonstrated below:

MessageBox.Show(
  String.Format("Depth of node: {0}",
    treeView1.SelectedNode.Level));

However as stated by the MSDN Documentation this property is not available on the .NET Compact Framework. This is because one of the techniques Microsoft has used to reduce the size of the .NET Compact Framework is to remove methods and properties from the Base Class Library when the same behaviour can be implemented with a couple of lines of code written by the user.

So although the .NET Compact Framework does not provide a TreeNode.Level property we can implement our own, but first we must come up with a suitable algorithm.

General algorithm
Given a node within the tree (represented by the TreeNode class) we can determine the node’s Parent by querying the Parent property. If we repeat this process we will eventually get to a node which has null for it’s parent. This node is called the root (or top) of the tree. If we count the number of nodes we walked over to reach this node we have calculated the depth (or level) of the node we started at. This algorithm is implementable within C#.

Diagram outlining algorithm to determine depth of a node within a tree

Pre C# v3 Implementation
The best we can do with C# version 1 or 2 is to replace calls to missing methods with calls to static helper methods we manually write. For example we could write the following helper class to replace the TreeNode.Level property.

namespace MyProject
{
  public static class Utils
  {
    public static int Level(TreeNode node)
    {
      int i = -1;
 
      // Walk up the tree until we find the
      // root of the tree, keeping count of
      // how many nodes we walk over in
      // the process
      while (node != null)
      {
        i++;
        node = node.Parent;
      }
 
      return i;
    }
  }
}

and make use of it as follows

MessageBox.Show(
  String.Format("Depth of node: {0}",
    Utils.Level(treeView1.SelectedNode)));

C# v3 Implementation
C# version 3 (first available with the .NET 3.5 framework release) introduces a language feature called extension methods. This is basically a compiler trick which allows us to call helper methods such as the one created above with a slightly different syntax, that makes it appear as if the new methods we define are part of the existing objects. Behind the scenes the compiler is simply rewriting our statements to use the old syntax.

To convert our helper method into an extension method we only need to convert the method prototype from

public static int Level(TreeNode node) { ... }

to

public static int Level(this TreeNode node) { ... }

The magical “this” keyword is enough to get the following syntax to work

MessageBox.Show(
  String.Format("Depth of node: {0}",
    treeView1.SelectedNode.Level()));

Notice how our new Level() method can be called as it it was a member of the TreeNode class. This is very close to the original syntax that a desktop developer would utilise. In this example ideally we would create an “extension property”, which would mean we could utilise identical syntax on both platforms, however C# v3 does not support extension properties, only extension methods.

Sample Application

[Download TreeNodeLevelExample.zip - 19KB]

A sample application is available for download. The application shows a simple tree view and allows the user to select different nodes within it. As different nodes are selected a label is updated to show the depth of the currently selected node.

There are two versions of the sample application. The first is designed for .NET CF 2.0 and uses the first technique demonstrated, while the second is for .NET CF 3.5 and demonstrates the cleaner extension method based syntax.

Daniel Moth among others have discussed various techniques to support cross compiling code across both the .NET Compact and Full desktop frameworks. Extension methods are another tool to add to the toolbox, allowing you to provide your own implementations for methods missing from the .NET Compact Framework without requiring code changes to your main application source code.

Is anyone aware of any discussions available online about why it was decided not to support extension properties (or events)? Is this something we can expect to see in C# version 4?

Capture and respond to user input to a notification bubble

Tuesday, October 30th, 2007

A popup notification showing an HTML based form for ordering a Pizza.In my previous blog entry about notifications I mentioned that we had not covered the ResponseSubmitted event. Subscribing to this event allows you to respond to user input entered within a notification bubble. This blog entry discusses how you can capture and process user input within notifications.

Creating an HTML based form
The contents of a notification bubble is formatted using HTML. To request user input within a notification bubble we can utilise a HTML based form.

A simple form specified within HTML may look like the following example:

<form>
  Name: <input name="name" /><br />
  <input type="submit" />
</form>

Similar to a System.Windows.Forms base application there are a range of controls which can be used within an HTML based form. These controls are typically specified via an <input /> element, although there are some exceptions as shown in the following table:

Name HTML Syntax
Hidden Field
<input type="hidden" name="fieldname" value="default" />
Textbox (single line)
<input type="text" name="fieldname" value="default" />
Textbox (single line password)
<input type="password" name="fieldname" value="default" />
Textbox (multi line)
<textarea name="fieldname" cols="40" rows="5">
    default value
</textarea>
Radio buttons
<input type="radio" name="fieldname" value="1" />
<input type="radio" name="fieldname" value="2" />
<input type="radio" name="fieldname" value="3" />
Checkbox
<input type="check" name="fieldname" checked="true" />
Combobox
<select name="fieldname">
    <option value="1">option 1</option>
    <option value="2">option 2</option>
    <option value="3">option 3</option>
</select>

A sample form with two textbox controls could be specified within C# source code as follows:

Notification notif = new Notification();
notif.Text = @"<form>
    Field 1: <input type=""text"" name=""fieldname1"" /><br />
    Field 2: <input type=""text"" name=""fieldname2"" />
  </form>";

Buttons
Using the controls specified above allows a notification to accept input from the user. However it does not provide the user with a mechanism to submit a completed form to the application for further processing.

A form is typically submitted when the user presses a submit button. A submit button can be specified within HTML via the use of an <input type=”submit” /> element. Whenever the user presses the submit button the ResponseSubmitted event will be triggered, allowing the form to be processed by the application.

Buttons can also be utilised within a notification to temporarily hide or permanently close a notification without submitting a form (useful for cancel or postpone style buttons etc). These actions can be specified via the use of button elements within the HTML as demonstrated below:

<!-- This button will minimise the notification -->
<input type="button" name="cmd:2" value="Hide" />
 
<!-- This button will permanently close the notification -->
<input type="button" name="something" value="Close" />

The value attribute contains the text displayed on the button, while the name (i.e. “cmd:2″) controls the action which occurs when the button is pressed. The name “cmd:2″ is a special value indicating to the operating system that the notification should be minimised and displayed as an icon that can be clicked to redisplay the notification. Having a button with any other name will cause the notification to permiantly be dismissed without the ResponseSubmitted event firing. All other “cmd:n” style button names are reserved by Microsoft for future use.

Hyperlinks
A HTML form can also contain traditional hyperlinks such as the following example:

<a href="help">Display further help</a>

Whenever such a link is pressed within the notification, the ResponseSubmitted event will trigger and the response string will be the string specified as the href attribute (”help” in this example).

Many of the built in operating system notifications utilise hyperlinks to provide access to settings or customisation dialogs.

Processing the response
When a HTML form within a notification is submitted the ResponseSubmitted event will trigger and this is the ideal opportunity for an application to process the contents of a form. The ResponseEventArgs parameter passed to this event handler contains a Response property that includes the current values of all fields within the form encoded in a format known as application/x-www-form-urlencoded.

Section 17.13.4 of the HTML 4.01 standard discusses application/x-www-form-urlencoded form data with the following description of the encoding process.

This is the default content type. Forms submitted with this content type must be encoded as follows:

  • Control names and values are escaped. Space characters are replaced by `+’, and then reserved characters are escaped as described in [RFC1738], section 2.2: Non-alphanumeric characters are replaced by `%HH’, a percent sign and two hexadecimal digits representing the ASCII code of the character. Line breaks are represented as “CR LF” pairs (i.e., `%0D%0A’).
  • The control names/values are listed in the order they appear in the document. The name is separated from the value by `=’ and name/value pairs are separated from each other by `&’.

As an example, if the form specified above contained the strings “Hello World” and “Good Bye” it would be encoded and appear within the Response property as follows:

?fieldname1=Hello+World&fieldname2=Good+Bye

The Microsoft documentation for the Response property contains an example of how to parse such response strings. It does this via some rather brittle string search and replace style operations. The sample code is not very generic, as it will break if you change the structure of your form even a little (such as renaming a field) and it does not deal with decoding hex escaped characters.

Within the sample application mentioned below I have implemented a function called ParseQueryString which performs the application/x-www-form-urlencoded decode process and returns a more easily used Dictionary of field control names to value mappings. This allows you to write a ResponseSubmitted event handler which looks something like the following:

private void notification1_ResponseSubmitted(object sender,
    ResponseSubmittedEventArgs e) 
{ 
  // This dictionary contains a mapping between 
  // field names and field values. 
  Dictionary<string, string> controls = ParseQueryString(e.Response); 
 
  // Make use of the field values, in this case pulling the
  // values out of the textboxes and displaying a message
  // box.
  MessageBox.Show(String.Format("first field = {0}, second field = {1}", 
	controls["fieldname1"], controls["fieldname2"]); 
}

This should make the intention of the ResponseSubmitted event handler easier to determine, and makes for more easily maintained code. The “hard” part of the response parsing logic is now hidden away within the ParseQueryString function, leaving you with an easy to use collection of field values to play with.

Including images within a notification
Sometimes it is helpful to include a small image within a popup notification. This is possible, but as Keni Barwick found out, the syntax of your HTML has to be fairly precise for the notification balloon to locate the image (a similar situation to images within HTML help files).

You should have good success if you use the file:// protocol, and include a full path to your image using backward slashes to separate directories, i.e. use the format:

<img src="file:///path/to/image.bmp" />

For example to include the image \windows\alarm.bmp you would use the following HTML:

<img src="file:///windows/alarm.bmp" />

You could hard-code the path to your image file but this could cause problems if the user decides to install your application in a different location (on an SD card for example). If your images are stored in the same folder as your main executable you can determine the path to your images at runtime by using a function similar to the following code snippet:

using System.Reflection; 
using System.IO;
 
public string GetPathToImage(string fileName)
{
  // Determine the exe filename and path
  Assembly assembly = Assembly.GetExecutingAssembly();
  string executablePath = assembly.GetName().CodeBase;
 
  // Trim off the exe filename from the path
  executablePath = Path.GetDirectoryName(executablePath);
 
  // Add the specified filename to the path
  return Path.Combine(executablePath, fileName);
}
 
string imageURL = GetPathToImage("foo.bmp");

Sample Application

[Download notificationuserresponsesample.zip - 16KB]

A sample application is available for download. The sample demonstrates using a notification to specify the details of a pizza order. It also demonstrates the inclusion of an image within a notification.

When targeting a Windows Mobile 5.0 or above device, you could extend the sample by using the custom soft keys feature mentioned in a previous blog entry to submit the notification by pressing a subkey instead of pressing a button within the body of the notification.

Please feel free to make use of the ParseQueryString method and any associated functionality within your own applications.

Custom soft keys for Notifications

Thursday, October 4th, 2007

Screenshot of custom notification displayed on the screen with two custom soft key menu items.Since my previous posting about Notifications within the .NET Compact Framework I have seen the question of how to provide custom menu items at the bottom of a notification come up a number of times. Today I am going to provide you with one solution to this challenge.

The short answer is that the Microsoft.WindowsCE.Forms.Notification class provided by the .NET Compact Framework does not expose the required functionality. Although the native notification API allows custom menus to be associated with a notification, for some reason this hasn’t be exposed to managed developers. With a little bit of work however (i.e. some PInvoking) you can produce your own managed wrapper around the Notification APIs to expose the required functionality.

I have done this and produced a class called NotificationWithSoftKeys. You can download a copy of this class, along with a sample application which demonstrates it’s various features below.

[Download notificationsoftkeys.zip 32KB]

The rest of this blog entry will demonstrate how you can utilise the class within your own applications. It is designed to be a stand in replacement for the existing class, but adds some additional properties and events.

Adding Spinners
Screenshot of custom notification showing the use of spinnersSpinners are the first additional functionality the NotificationWithSoftKeys control exposes. A notification spinner is a little bit of text on the right hand side of a notification which sits in-between a pair of < and > buttons. The user can tap on these buttons to “spin” through a series of related notifications.

Here is a brief example of how to enable this feature within your notification.

private NotificationWithSoftKeys notification;
 
private void btnCreateNotification_Click(object sender, EventArgs e)
{
  notification = new NotificationWithSoftKeys();
  notification.Icon = Properties.Resources.Icon;
  notification.Caption = "This is my notification";
  notification.Text = "Spinner Test\t1 of 5";
  notification.Spinners = true;
  notification.SpinnerClicked +=
      new SpinnerClickEventHandler(notification_SpinnerClicked);
  notification.Visible = true;
}

The main thing you need to do is set the Spinners property to True in order to enable the Spinner functionality. After this any text within the Caption property after the first tab (\t) character will be displayed on the right hand side of the notification’s caption.

When the user presses the < or > buttons the SpinnerClicked event will fire. Within your event handler you should update your Notification’s Caption and Text properties to give the illusion that another notification has been displayed.

Display Current Time on Caption
Screenshot of custom notification showing the use of the TitleTimer propertyDisplaying the current system time on the caption is very straight forward. All you need to do is set the TitleTime property to true.

Custom Buttons
This is the feature which will probably generate the most interest. The standard notification has a “Hide” button on the left soft key. Some built in applications however display notifications with alternative softkeys and perform different actions depending upon which softkey was selected. Using the NotificationWithSoftKey class we can achieve the same effect as demonstrated with the following example.

private NotificationWithSoftKeys notification;
 
private void btnCreateNotification_Click(object sender, EventArgs e)
{
  notification = new NotificationWithSoftKeys();
  notification.Icon = Properties.Resources.Icon;
  notification.Caption = "This is my notification";
  notification.Text = "A soft key test";
  notification.LeftSoftKey =
      new NotificationSoftKey(SoftKeyType.Dismiss, "Close");
  notification.RightSoftKey =
      new NotificationSoftKey(SoftKeyType.StayOpen, "View");
  notification.RightSoftKeyClick +=
      new EventHandler(notification_rightSoftKeyClick);
  }
 
  notification.Visible = true;
}

This example will display a notification with two soft keys. The left soft key will be labeled “Close” and will remove the notification, while the right soft key will be labeled “View” and trigger the RightSoftKeyClick event when selected.

Within this event handler you could open up a second form, delete something from a database or perform any other task you would like to in response to the user pressing the soft key.

There are a number of soft key types to choose from as shown below:

Type Description
Dismiss Remove the notification when the soft key is pressed
Hide Hide the notification when the softkey is pressed (but do not dismiss)
StayOpen Do not dismiss or hide the notification when the softkey is pressed.
Submit Submit the HTML form in the notification instead of calling the click event handler
Disabled The softkey is disabled (grayed out)

Backwards Compatibility
The additional features discussed today are only available on devices running Windows Mobile 5.0 or higher. If your application is targeting multiple device types, and you need to run on Pocket PC 2003 devices the new functionality will not work.

The NotificationWithSoftKeys class utilises techniques outlined in my blog posting on device and platform detection to fail gracefully on older devices. When the class detects an older device type, it will simply ignore all the soft key related properties and revert back to the old notification style.

Sometimes you may want to check if this fallback has occurred. For instance you may want to utilise the soft key buttons for user input. In this case you would need to use an alternative approach (such as HTML based buttons) when running on Pocket PC 2003 or older devices which don’t have softkeys. You can do this by checking the PlatformSupportsCustomSoftKeyButtons property and altering the other properties of the NotificationWithSoftKeys class as required.

Summary
This is still only scratching the surface of what is possible with the native notification APIs. There are a number of notification features which are not exposed by this wrapper. For example there are the following flags which could easily be exposed as properties.

  • SHNF_DISPLAYON – the display is forced to turn on for the notification
  • SHNF_SILENT – the notification is forced to be silent and not vibrate, regardless of system settings

Please feel free to download the sample project and have a play around with it. I would be keen to hear from anyone who attempts to use it or modifies it to expose additional functionality etc.