Click here to Skip to main content
15,886,026 members
Articles / Programming Languages / C#
Article

Enhancing User Experience - Part 1 : A Simple Unit Converter Application

Rate me:
Please Sign up or sign in to vote.
2.89/5 (19 votes)
12 Apr 200414 min read 80.3K   1.7K   22   19
Writting a Unit Converter application for an improved user experience (ease of use).

Image 1

Figure 1: The Unit Converter application for an enhanced user experience.

Contents

Introduction

A recent (march 2004) Code Project poll asked: When writing desktop applications, which is the most important for you? "Ease of use" was the most popular choice, followed by “reliability”, and a “good looking UI”. This inspired me to write this article on making an application easier to use. This article is in two parts, the second part will take a closer look at the controls on this application.

A considerable time ago, while learning to programme in java, one of the first Swing applications I wrote was a Unit Converter. Therefore, it’s only fitting for my first .Net article that I rebuild that application with a dot net flavour. This article is written with new C# programmers in mind, so I apologise in advance to anyone who feels that I seem to be stating the obvious!

Let’s start by deciding on what the Unit Converter should do and how it should do it. Before any programming begins it is always wise to determine and document what the applications functionality should be.

Application Functionality

Whilst building any application it is very important to keep in mind the end user. Generally, the application functionality goes hand in hand with the end application user’s requirements. From the name it’s obvious that the Unit Converter application converts units of measure, e.g. between metric metres and imperial feet.

Amongst the questions that need to be asked of the application are:

Q: What are the functionality requirements?

  • The Unit Converter should be able to convert many types of units, e.g. length, area, mass, volume etc…
  • The user should be able to specify and extend the range of units that the application can handle. (This came a second in the same Code Project poll.)

Q: How is the application going to be used?

  • In this case it’s very simple; users are going to want to enter data into the application and expect to see the converted result.

Q: What Actions is the user going to go through on the application to achieve this?

  • Due to the nature of this application the answer to this question is also straight forward. On a complex application this would be modelled using UML and “Use Cases” would help visualise this process. But for our unit converter we don’t need to go into this level of detail. However, we still need to think through and understand how the application is going to be used.
  1. Choose a conversion type, e.g. Length or Weight etc...
  2. Enter data.
  3. Select the units of the entered data.
  4. Select the desired conversion units.
  5. Execute the conversion.
  6. View the result.

UI Design

At a bare minimum the Unit Converter needs an input field (TextBox), two controls that allow us to select given data (possibly ListView or ComboBox), a control to display the Data (a Label or read-only TextBox) and a button to instruct the application to convert the entered data value.

Image 2

Figure 2: Bare minimum UI design. A poor UI design is likely to lead to a poor user experience.

This design (figure 2) is functional, it has all the necessary controls, but it does look poor. Another problem, from the user’s perspective, is that the two ComboBoxes contain a long list of various units of measure. This can lead to exceptions being thrown if the user is not careful in choosing the conversion units, e.g. if the user accidentally tries to convert from a weight to a length. This can lead to a poor user experience.

User Experience in Mind (while Designing the Application)

Once the application functionality has been established it is time to reflect on how the application is going to be used. How the user will interact with the application. The way to make an application’s UI user-friendly is to make sure the user does little as possible. The less the user needs to think about and do, the more user-friendly the application. Even the number of mouse clicks, and the distance the mouse is moved/dragged across the screen, all go towards improving the application user’s experience.

The UI colour scheme is very important. With the boom of the web, users have been exposed to web applications that look great, with smart colour schemes and layouts. There is no reason why windows developers now days have to restrict themselves to a mono-tone UI. At the same time it is important not to go over board with garish colours. Colours such as certain shades of Green and Blue are soothing to the eye. So if the application being designed is one that has the user working with it for prolonged periods of time, it is advisable to use an eye-friendly colour scheme. If, however, the application does not require the user to stare at the UI for long periods of time then, possibly, more adventurous colour scheme may be adopted.

UI controls on an application screen should, where possible, fit into a neat layout. Use of the Visual Studio.Net automatic alignment (“align to grid”) should be taken advantage of. Visually, if the controls don’t line up with each other or are haphazardly laid out on the screen then the user may find it difficult to visually navigate the screens flow logic. This will decrease the usability of the application screen.

Now let’s look at the Unit Converter application. The Unit Converter may have more than one conversion type, and will need to provide the user with the ability to select a given conversion type. A good candidate for this type of action would be the ComboBox control. A universal rule in clean UI design is not to clutter the application screen with excessive controls; only show the controls that are used. In the Unit Converters case it should only show a “conversion-type” selector control (ComboBox) when there is more than one type to choose from.

The layout of the Unit Converter’s controls should flow naturally, keeping in line with the user’s actions. This makes the layout easier to read.

The next logical action that the user will take is to input data (that requires conversion) into the Unit Converter. For this situation a TextBox control is the logical choice. If the TextBox only accepted numeric values this would help catch erroneous data and avoid exceptions being thrown in such scenarios. As the Unit Converter deals in numeric data the TextBox control should be extended to only allow numeric inputs. If non numeric data is entered, the TextBox can either flag up a message box or provide some other type of indication that erroneous data has been entered (e.g. validation using the “ErrorProvider” class) and wait for the user to respond to the alert. The user experience would be greatly enhanced if this TextBox field intelligently handled the data and allowed the user to continue with the conversion process uninterrupted.

Once the data is entered the user will select its unit of measure, e.g. “metres” or “kilometres” for a “Length” conversion. Following this the user will choose the desired conversion-to type, e.g. if the input data’s unit of measure was “metres” then the conversion-to unit of measure may be “millimetres”. This type of choice is ideal for a ComboBox control.

Remember from school science experiments, when writing down the distance between two points or the mass of an object, System International (SI) unit abbreviations were used, as opposed to writing down the full name of the units e.g. m for “metres” or kg for “kilograms”. Therefore, displaying the unit of measure in an abbreviated form would make the application more intuitive to many users. The Unit Converter would be further enhanced if a full description of the unit of measure was also available to the application user, so if they are not fully familiar with the abbreviated units then it would not be a problem. This smart display can be achieved by creating a custom control using the ComboBox as the base object. Call this control a SplitComboBox.

At any one time the SplitComboBox should only display a single conversion type. Therefore, if the selected conversion type is "Length", then both SplitComboBoxes must only be populated with "Length" units. On change of conversion type, the SplitComboBoxes must be repopulated and display the change.

Minimising the number of actions a user need to carry out improves the usability of the application. So making the unit converter execute the conversion process on change of selected unit of measure will reduce the need for the user to click the convert button.

When the Unit Converter is launched automatic focus should be placed upon the first control the user is likely to interact with, and subsequent focus should move onto controls which follow the logical user path, and hopefully UI control layout!

Another way to enhance the Unit Converter (and the user’s experience) is by allowing the end users to add their own conversion types. A simple way of achieving this is by using an xml file to store the Conversion data. If the user is not careful whilst adding their own data, this approach may cause problems. If correct data values and types are not adhered to or the xml file structure is accidentally broken this can lead to Unit Converter falling over.

For application reliability thought needs to be given to exception handling. To reduce application fall over, the application must have ways of gracefully resolving problems that it faces. These problems could be a result of the conversion xml file or its data not being correctly formatted.

UI Design; a Second Look

Image 3

Figure 3: An enhanced UI design for the Unit Converter application.

Figure 3 shows the Unit Converter with the custom SplitComboBox and DecimalBox field, for an enhanced user interface. The Unit Converter’s look and colour scheme are based around a blue palette, consisting of 4 different shades. These colours project a fresh clean design. The Background is set using a jpeg; this is a great improvement on the default Windows form background of Control Grey. A word of caution: Using background images can dramatically increase the size of your final application. Therefore, if an image is to be use, it is very important to optimise it much as possible. Bitmap or png formats are much larger in terms of file-size than Gif and Jpeg formats. So it is advisable to use the latter for background images. However, the quality of the image is poorer in the jpeg and gif formats. The gif image format is suited for images that are made up from flat blocks of colour, where as the jpeg format is more suited to images with colour gradients such as photographs.

Coding the Application

At the Core of any application is its data. An application that relies on large amounts of data typically uses a database to store its data. Using a database in this case would be overkill. The Unit Converter data is stored in an xml file ("ConversionData.xml"). This allows the end user to extend the application with relative ease. The xml file is shown below:

XML
<?xml version="1.0" encoding="utf-8" ?> 
<UNITCONVERTER>   <TYPE NAME="Length">     
<UNIT NAME="Meters" ABBREVIATION="m" CONVERSIONFACTOR="1" />     
<UNIT NAME="Kilometres" ABBREVIATION="km" CONVERSIONFACTOR="1000" />     
<UNIT NAME="Millimetres" ABBREVIATION="mm" CONVERSIONFACTOR="0.001" />    
<UNIT NAME="Foot" ABBREVIATION="ft" CONVERSIONFACTOR="0.3048" />     
<UNIT NAME="Mile" ABBREVIATION="mile" CONVERSIONFACTOR="1609.3" />   
</TYPE>   
<TYPE NAME="Volume">         
<UNIT NAME="Litres" ABBREVIATION="l" CONVERSIONFACTOR="1" />     
<UNIT NAME="Decilitres" ABBREVIATION="dl" CONVERSIONFACTOR="0.1" />    
<UNIT NAME="Centilitres" ABBREVIATION="cl" CONVERSIONFACTOR="0.01" />     
<UNIT NAME="Millilitres" ABBREVIATION="ml" CONVERSIONFACTOR="0.001" />   
</TYPE> </UNITCONVERTER>

Within the “UNITCONVERTER” element the “TYPE” tags are individual conversion types. The “UNIT” element is where the individual unit of measure’s descriptive name (“NAME”), abbreviated unit name “ABBREVIATION”, and “CONVERSIONFACTOR” values are stored as attributes. The conversion factor values are relative to the base unit of measure, e.g. Conversion type is Length; therefore 'metre' is the base unit of measure, so its 'CONVERSIONFACTOR' = 1. All the other units within the Length conversion type must be relative to this base [metre] conversion unit’s value. As a kilometre contains 1000 meters, the “CONVERSIONFACTOR” value for kilometres is 1000, likewise the “CONVERSIONFACTOR” values for centimetres is 0.01.

At start up, the application reads in the XML data and holds this data in a Hashtable as a key-value pair; "key" being the conversion-type, and "value" being the collection of conversion UnitData objects which are to be used to create the conversion matrix.

C#
public sealed class UnitData
{
  private string sCode, sText;
  private decimal iValue;
  public string Code
  {
    get{return sCode;}
  }
  public string Text
  {
    get{return sText;}
  }
  public decimal Value
  {
    get{return iValue;}
  }
  public UnitData(string asCode, string asText, decimal aiValue)
  {
    sCode  = asCode;
    sText  = asText;
    iValue = aiValue;
  }
}// End UnitData  

The Unit Converter ideally wants to load this data once, and keep it in memory, so it does not need to reload the xml file each time the user changes the conversion type. This approach is correct for the Unit Converter as the amount of data is very small, and it doesn’t change during the active state of the application. Using the XmlTextReader the Unit Converter reads in the xml document into memory.

C#
XmlTextReader Xreader = new XmlTextReader(asFileName);
XmlDocument XmlDoc = new XmlDocument();
XmlDoc.Load(Xreader);

Each conversion unit read in from the XML file is stored in a UnitData object and this is how the data is passed around within the application. The GetDataFromEachUnit(...) method below does exactly this.

C#
private UnitData GetDataFromEachUnit(XmlNode axnDataNode, 
  string asUnitType)
{
  bool bNodeSucess    = true;
  decimal dConversionVal  = 0.0m;

  string  sDataType  = "", sUnit   = "",
      sAttType  = "", sValue = "", sErroInfo = "";
  int iAttribute    = axnDataNode.Attributes.Count;
  

  for(int k=0; k<iAttribute; k++)
  {
    sAttType = axnDataNode.Attributes[k].Name.Trim().ToUpper();
    sValue   = axnDataNode.Attributes[k].Value;

    switch(sAttType)
    {  
      case "CONVERSIONFACTOR":
        dConversionVal = Convert.ToDecimal(sValue);
      break;

      case "NAME":
        sDataType = sValue;
      break;

      case "ABBREVIATION":
        sUnit = sValue;
      break;

      default:
        bNodeSucess = false;
      break;
    }

    sAttType = "";
    sValue   = "";
  }

  if(bNodeSucess)
    return new UnitData(sUnit, sDataType, dConversionVal);
  else      
    return null;
}// Exception code has been removed for clarity.

As the xml file can be modified by the user, there is a higher probability of an exception occurring due to potential user/xml error within the methods that carry out xml file reading and populating of the UnitData objects. Extra emphasis needs to be placed on exception handling within these sections of coding, (see the Unit Converter code for basic exception handling). Exception handling is done through the try{}catch(){} syntax. The message that an exception throws up for the application user should, ideally, be in a user-friendly format. To aid debugging for the programmer, the application could write a separate error message to a log file containing a more detailed exception message; this would naturally be hidden from the user.

In the InitializeApplicationData() method the “conversion type” selector" ComboBox's Items are populated using a foreach loop. If there is only a single Item then the conversion type selector (ComboBox) is hidden and the UI layout is altered to accommodate for its absence. This results in a cleaner UI.

C#
foreach(string sType in ahtData.Keys)
  this.cbx_ConvertionType.Items.Add(sType);


this.cbx_ConvertionType.SelectedIndexChanged += 
  new System.EventHandler(this.cbx_ConvertionType_SelectedIndexChanged);
this.cbx_ConvertionType.SelectedIndex     = 0;


if(ahtData.Count == 1)
{
  this.cbx_ConvertionType.Visible    = false;
  this.lbl_ConvertionType.Text    = 
      this.cbx_ConvertionType.SelectedItem+ " Conversion";
  this.lbl_ConvertionType.Size    = new Size(360, 25);
  this.lbl_ConvertionType.Location  = new Point(24, 80);        
  this.lbl_ConvertionType.Font    = 
      new Font("Microsoft Sans Serif", 9.75F, FontStyle.Bold);
  this.lbl_ConvertionType.TextAlign = ContentAlignment.MiddleCenter;
}

object oItemSelected = this.cbx_ConvertionType.SelectedItem;
this.alUnitObjects   = (ArrayList)ahtData[oItemSelected];
this.PopulateControls(oItemSelected);

To see this in action first make a backup copy of ConversionData.xml file and then modify it so that only a single conversion type exits. After reading the XML data into the application the Unit Converter creates a matrix of conversion values for a selected conversion type. The CreateDataMatrix(...) method is responsible for this. The resulting decimal matrix is used to convert user input values.

private decimal[,] CreateDataMatrix(ArrayList aalConversionObjects)
{
  int iConvUnitCount  = aalConversionObjects.Count;
  decimal[,] daMatrix = new decimal[iConvUnitCount, iConvUnitCount];
  string sCode    = "";
  decimal dData    = 0.0m, dInternalData  = 0.0m;
  UnitData udUnitCode = null, udInternalCode  = null;
  
  for(int i=0; i<iConvUnitCount; i++)
  {
    udUnitCode  = (UnitData)aalConversionObjects[i];
    sCode    = udUnitCode.Code;
    dData    = udUnitCode.Value;

    for(int j=0; j<iConvUnitCount; j++)
    {
      udInternalCode  = (UnitData)aalConversionObjects[j];          
      dInternalData  = udInternalCode.Value;
      daMatrix[i,j]  = dInternalData/dData;
      udInternalCode  = null;
      dInternalData  = 0.0m;
    }
    
    udUnitCode  = null;
    dData    = 0.0m;
  }

  return daMatrix;
}

The two unit-of-measure SplitComboBoxes and the input DecimalBox are custom build for this application. A closer look at the way these controls are build will be taken in the second part of this article. For now a quick overview of what they do for the application will be discussed.

The DecimalBox only allows the displays of a decimal value; even if the user inputs an alpha numeric data the DecimalBox strips out inappropriate characters leaving only the inferred decimal value. This greatly improves the user experience, since if the user does accidentally enter an invalid character then application can compensate for this, instead of throwing a warning or even an exception. This approach may not be appropriate in an application where the input data is being saved back to a database for use later. In such situations the use of an “ErrorProvider” class would be more suitable as this would indicate to the user that there was a problem with the input data.

The behaviour of the SplitComboBox, as discussed earlier, shows only the unit of measures abbreviated value, while on the dropdown it provides the full descriptive name. The SplitComboBox’s datasource is an ArrayList of UnitData objects, and it is these objects that hold the key information with regards to the conversion process. This control also has the added benefit of saving on UI space.

The ConvertInput(...) method shown below carries out the actual conversion, and updates the relevant UI components.

private void ConvertInput(decimal adInputValue)
{
  decimal dResult = 0.0m;
  int iIndex1    = 0, iIndex2 = 0;
  string sResult  = "";

  try
  {
    iIndex1    = this.scbx_Unit1.SelectedIndex;
    iIndex2    = this.scbx_Unit2.SelectedIndex;
    dResult    = adInputValue * this.daDataMatrix[iIndex2, iIndex1];
    sResult    = dResult.ToString();

    if(sResult.Length > 25)
      sResult = sResult.Substring(0, 25);

    object oItem  = this.scbx_Unit2.SelectedItem;
    UnitData udCur  = (UnitData)oItem;

    this.lbl_Display.Text = sResult+"  "+udCur.Code;  
  
    sResult = "";        
  }
  catch(Exception ex)
  {
    eLog.Error("ConvertInput", ex.Message, "001");
  }      
}

This method is called when the "Convert" button is clicked or when the “convert-to” SplitComboBox’s selected unit of measure is changed. This allows the user to eliminate the need to click the “Convert” button and helps towards making the application easier to use.

Summary

As with the majority of applications, the development process does not stop here, there is always scope for improvement. For example in version 2, the Unit Converter could have another UI screen to enable the user to modify the Conversion XML file thus, reduce the possibility of having erroneous data entered into the conversion xml file. This would reduce the chances of application failure. Another enhancement to the application should be allowing the user to determine the number of “significant figures” of the displayed result.

The Unit Convert is a very simple application and this article has tried to illustrate that building from the users perspective usually leads to a better application. In the second part of this article, Enhancing User Experience, Part 2; Extending Controls, looks at the development and coding of the custom controls used within this application.

History

  • Version 1 of Unit Converter (April 2004).

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United Kingdom United Kingdom
After a formal education in the field of Physics and Engineering and with aspirations of becoming a Nobel Prize winning physicist Al become tired with the monotony of research in this field. This is when he made a conscious decision to turn what was a hobby into a career.

Graphic Design has always been one of his passions combined with his Vulcan-like (logical) mind; programming emerging web technologies was the perfect choice. In the early days he designed and coded webpages, programmed Java applets and taught HTML and Internet technology courses at university level. Nowadays he works as a Research and Development programmer for a company that produce award winning logistics software.

His current interests include .Net C#, Winforms, Web Services, ASP.net, XML, XSLT, Macromedia Flex and any new software or technology that has a pulse.

Comments and Discussions

 
GeneralRe: "Ease of use" Pin
Daniel Turini13-Apr-04 8:18
Daniel Turini13-Apr-04 8:18 
GeneralRe: "Ease of use" Pin
Al Choudhury13-Apr-04 8:39
Al Choudhury13-Apr-04 8:39 
GeneralRe: &quot;Ease of use&quot; Pin
Michael P Butler13-Apr-04 11:12
Michael P Butler13-Apr-04 11:12 
GeneralRe: &quot;Ease of use&quot; Pin
Anna-Jayne Metcalfe13-Apr-04 22:48
Anna-Jayne Metcalfe13-Apr-04 22:48 
GeneralRe: "Ease of use" Pin
Al Choudhury14-Apr-04 11:53
Al Choudhury14-Apr-04 11:53 
GeneralRe: &quot;Ease of use&quot; Pin
Anna-Jayne Metcalfe16-Apr-04 8:21
Anna-Jayne Metcalfe16-Apr-04 8:21 
GeneralRe: &quot;Ease of use&quot; Pin
Al Choudhury17-Apr-04 0:06
Al Choudhury17-Apr-04 0:06 
GeneralRe: "Ease of use" Pin
Anna-Jayne Metcalfe13-Apr-04 22:57
Anna-Jayne Metcalfe13-Apr-04 22:57 
GeneralRe: "Ease of use" Pin
Al Choudhury14-Apr-04 11:36
Al Choudhury14-Apr-04 11:36 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.