Using a RSS Feed to Populate Data in Expression Blend

•March 27, 2012 • Comments Off on Using a RSS Feed to Populate Data in Expression Blend

One of the most simple and effective ways of getting design data for testing look and feel into a WPF or Silverlight project is via RSS (the other way is by defining a data source directly from within Blend). I have used this method for trying out custom control design as seen here and here. Although this technique has been covered in a couple of places on the web I would like to add my entry for this – more as a place holder for my own easy access as anything J.

Well here goes.

  1. Find an RSS feed from your own collection or anywhere else for that matter – the example does not require a specific feed address. This is because all RSS feeds follow the same XML Schema when streaming content.
  2. Open Expression Blend and create either a Silverlight or WPF project – the technique works for both types of project.

  3. In the resulting main window add a list box. Once again the type of control may vary as long as it supports an item source of some kind so data grids, combo boxes, list views, etc. will all suffice.
  4. Now take your RSS feed address and enter it as a new XML data source. Here I am going to use the RSS feed from UX Magazine (http://feeds.feedburner.com/webdesigntutsplus).

     

     

  5. Once the feed data has been parsed the following structure should appear in Blend.

  6. The most important XML element to us is the “item” element. Simply drag this element to the list box (or other element) that was placed on Blend’s design canvas. Something similar to the following screenshot should now appear:

  7. The data context now needs to be set to the LayoutRoot grid by dragging the XML element “rss” onto it as shown below.

  8. The ItemsSource of the list box can be set by dropping the XML element “items” onto the list box and selecting ItemsSource form the combo box as shown here:

    The list box has now the required details within it – even

  9. The final tasks are to style the items, items container, etc. Clicking on the list box and selecting the Edit Additional Templates -> Edit Generated Items ->Create Empty allows the basic data template to be built

  10. Editing the resultant data template

  11. Dropping the XML element “title” from the data source onto the selected data template


    results in the following design-time population:

Of course far more can be achieved on styling the list box than shown here – but that is an exercise for the reader J

That’s all Folks…

Paged Listbox Design

•March 26, 2012 • 1 Comment

Customers and users always have good and sometimes facinating ideas about how thier data should be represented on the screen. One of my recent customers did not like the way the standard list box functions finding it frustrating to search through a long list to select an item. The point being that if I move away from the list box it does not keep the focus of where I last visited in the list – a non-selected element found in the list was difficult to navigate back to based only on the scrollbar position. The proposal was to be able to page through the list box items and to be left with a possible page number if the list box had to be left and returned to later.

The code itself is rather simple – basically there is only a need for a Listbox and a DataPager as can be seen below:

            <ListBox x:Name="listBox"
                ItemsSource="{Binding FeedDetails, Mode=OneWay}" 
                ItemTemplate="{StaticResource itemTemplate}" 
                ItemContainerStyle="{StaticResource ListBoxItemStyle1}" 
                Style="{StaticResource ListBoxStyle1}" 
                ScrollViewer.HorizontalScrollBarVisibility="Disabled" 
                ItemsPanel="{StaticResource ItemsPanelTemplate}" }" 
                />
            <sdk:DataPager PageSize="5" Source="{Binding FeedDetails, Mode=OneWay}" />

The final paged listbox with all its styling can be seen below:

That’s all Folks…

That Missing Silverlight Binding Update

•February 1, 2012 • 2 Comments

I am surely not the first in this case to notice that Silverlight does not perform in the same way as WPF in certain aspects. One of those niceties that I relied on in WPF that does not appear within Silverlight is the ability to receive an event what the contents of a Textbox have be updated. This event is central in the validation process, especially in the reactive validation. For example it is not possible in Silverlight to validate character input via the IErrorInfo process but only to validate the complete content when navigating away from the Textbox. In a great number of real-life situations the validation is directly on input allowing the UI to reactively interact with the user providing instant feedback over the status of the input.

So, what can be done to enhance the Textbox control to support reactive validation? Well, there are three options available for the control designer. Firstly, the designer can subclass the Textbox class and provide an internal handler for the TextChanged event to check and update the binding source. The second option is to provide an attached property that attaches itself to the TextChanged event of the Textbox and updates the binding source. Finally, an Expression Blend Behaviour can be developed that has a similar effect to the attached property previous. It is worth noting here that the ideal solution should also be compatible with the philosophy behind WPF and Silverlight such that there is a separation between business logic and the visual representation i.e. the control is “skin-able”.

Sub-classing the Textbox

In the days of yore this was the only option available to developers and as the development process was fraught with difficulties and customising GUI was rarely if at all undertaken. However, those who did understand the intricacies of such development were forced to into sub-classing the control. As this experience took time and hence embedded itself into the developer as the process it is easy to see how this mentality is extended into the WPF/Silverlight world.

Ultimately these old tricks result in the following code:

 

using System.Windows.Controls;
using System.Windows.Data;

namespace DatabindingAttachedProperty
{
    /// <summary>
    /// The <see cref="TextChangedSubClass"/> class.
    /// </summary>
    public class TextChangedSubClass : TextBox
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="TextChangedSubClass"/> class.
        /// </summary>
        public TextChangedSubClass()
        {
            TextChanged += TextChangedSubClassTextChanged;
        }

        /// <summary>
        /// Handles the TextChanged event of the TextChangedSubClass control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Controls.TextChangedEventArgs"/> instance containing the event data.</param>
        private static void TextChangedSubClassTextChanged(object sender, TextChangedEventArgs e)
        {
            if (!(sender is TextBox))
            {
                return;
            }

            var tb = sender as TextBox;
            BindingExpression bindingExpression = tb.GetBindingExpression(TextProperty);
            if (bindingExpression != null && bindingExpression.ParentBinding.Mode == BindingMode.TwoWay)
            {
                bindingExpression.UpdateSource();
            }
        }
    }
}

The “one trick Pony” development mentality results in awkward code, extension difficulties and maintenance problems – today as it did back then. However, things have changed (unfortunately this development mentality has not) and today we have better ways of solving the problem of the missing binding update.

TextChanged Dependency Property

The Dependency Property provides a simple programming extension that augments a control with a property directly on the class itself, i.e. the class owns the dependency property and do not have much significance outside the class.

 

using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;

namespace DatabindingAttachedProperty
{
    /// <summary>
    /// The <see cref="TextChangedBindDependencyProperty"/> class.
    /// </summary>
    public class TextChangedBindDependencyProperty : TextBox
    {
        public static readonly DependencyProperty TextChangedBindProperty =
            DependencyProperty.RegisterAttached("TextChangedBind", typeof (bool),
                                                typeof (TextChangedBindDependencyProperty),
                                                new PropertyMetadata(false, TextChangedBindCallBack));

        /// <summary>
        /// Gets or sets a value indicating whether text changed binding is active.
        /// </summary>
        /// <value><c>true</c> if text changed binding is active; otherwise, <c>false</c>.</value>
        [Description("Enable or disable TextChanged checking for this control.")]
        [Category("TextChangedBindDependencyProperty")]
        public bool TextChangedBind
        {
            set { SetValue(TextChangedBindProperty, value); }

            get { return (bool) GetValue(TextChangedBindProperty); }
        }

        /// <summary>
        /// Texts the changed call back.
        /// </summary>
        /// <param name = "d">The d.</param>
        /// <param name = "e">The <see cref = "System.Windows.DependencyPropertyChangedEventArgs" /> instance containing the event data.</param>
        private static void TextChangedBindCallBack(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (!(d is TextBox)) return;
            var tb = d as TextBox;

            var isb = (bool) e.NewValue;
            if (isb)
            {
                tb.TextChanged += TbTextChangedBind;
            }
            else
            {
                tb.TextChanged -= TbTextChangedBind;
            }
        }

        /// <summary>
        /// Handles the TextChangedBind event of the tb control.
        /// </summary>
        /// <param name = "sender">The source of the event.</param>
        /// <param name = "e">The <see cref = "System.Windows.Controls.TextChangedBindEventArgs" /> instance containing the event data.</param>
        private static void TbTextChangedBind(object sender, TextChangedEventArgs e)
        {
            if (!(sender is TextBox))
            {
                return;
            }

            var tb = sender as TextBox;
            BindingExpression bindingExpression = tb.GetBindingExpression(TextProperty);
            if (bindingExpression != null && bindingExpression.ParentBinding.Mode == BindingMode.TwoWay)
            {
                bindingExpression.UpdateSource();
            }
        }

        /// <summary>
        /// Gets the text changed bind.
        /// </summary>
        /// <param name="obj">The obj.</param>
        /// <returns></returns>
        public static bool GetTextChangedBind(DependencyObject obj)
        {
            return (bool) obj.GetValue(TextChangedBindProperty);
        }

        /// <summary>
        /// Sets the text changed bind.
        /// </summary>
        /// <param name="obj">The obj.</param>
        /// <param name="value">if set to <c>true</c> [value].</param>
        public static void SetTextChangedBind(DependencyObject obj, bool value)
        {
            obj.SetValue(TextChangedBindProperty, value);
        }
    }
}

However, the disadvantage of the Attached Property is that it extends by sub-classing the original control. Hence a conscious decision has to be made in using the sub-classed control over the original. However, the designer will need to have a little mode control, something more flexible that can be use with base classes, third-party classes as well as the classes developed within the project.

TextChanged Attached Property

The Attached Property solution is the first that leaves the TextBox control intact and provides the binding update outside of the implementation. The great benefit of attached properties is that they allow you to extend the functionality of existing controls without having the source code or using extension methods. The attached property can be addressed directly in XAML as well as from the code behind.

namespace DatabindingAttachedProperty
{
    using System.ComponentModel;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;

    /// <summary>
    /// Definition of the <see cref = "TextChangedInteractor" /> class.
    /// </summary>
    public class TextChangedInteractor : DependencyObject
    {
        /// <summary>
        /// Using a DependencyProperty as the backing store for TextChanged. This enables animation, styling, binding, etc…
        /// </summary>
        public static readonly DependencyProperty TextChangedProperty =
            DependencyProperty.RegisterAttached("TextChanged", typeof (bool), typeof (TextChangedInteractor),
                                                new PropertyMetadata(false, OnTextChangedChanged));

        /// <summary>
        /// The validation Regex.
        /// </summary>
        public static readonly DependencyProperty ExpressionProperty = DependencyProperty
            .RegisterAttached(
                "Expression",
                typeof (string),
                typeof (TextChangedInteractor),
                new PropertyMetadata(string.Empty));

        /// <summary>
        /// Runs test for get Regex.
        /// </summary>
        /// <param name = "obj">
        /// The obj parameter.
        /// </param>
        /// <returns>
        /// The get Regex entry.
        /// </returns>
        public static bool GetTextChanged(DependencyObject obj)
        {
            return (bool) obj.GetValue(TextChangedProperty);
        }

        /// <summary>
        /// Runs test for set Regex.
        /// </summary>
        /// <param name = "obj">
        /// The obj parameter.
        /// </param>
        /// <param name = "value">
        /// If set to <c>true</c> if value.
        /// </param>
        public static void SetTextChanged(DependencyObject obj, bool value)
        {
            obj.SetValue(TextChangedProperty, value);
        }

        /// <summary>
        /// Runs test for on Regex entry.
        /// </summary>
        /// <param name="textChanged">The TextChanged parameter.</param>
        /// <param name="e">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance containing the event data.</param>
        private static void OnTextChangedChanged(DependencyObject textChanged, DependencyPropertyChangedEventArgs e)
        {
            if (!(textChanged is TextBox))
            {
                return; // support only Regex entry in a TextBox
            }

            var textBox = textChanged as TextBox;

            // add the event handles for key press
            if ((bool) e.NewValue)
            {
                textBox.TextChanged += TextBoxPreviewTextInput;
            }
            else
            {
                textBox.TextChanged -= TextBoxPreviewTextInput;
            }
        }

        /// <summary>
        /// Handles the PreviewTextInput event of the textBox control.
        /// </summary>
        /// <param name = "sender">The source of the event.</param>
        /// <param name = "textChangedEventArgs">The <see cref = "System.Windows.Controls.TextChangedEventArgs" /> instance containing the event data.</param>
        private static void TextBoxPreviewTextInput(object sender, TextChangedEventArgs textChangedEventArgs)
        {
            if (!(sender is TextBox))
            {
                return;
            }

            var tb = sender as TextBox;
            var bindingExpression = tb.GetBindingExpression(TextBox.TextProperty);
            if (bindingExpression != null && bindingExpression.ParentBinding.Mode == BindingMode.TwoWay)
            {
                bindingExpression.UpdateSource();
            }
        }
    }
}

Expression Blend Behaviour

Expression Blend is the only design tool for the successful development of rich UIs in both WPF and Silverlight. Primarily a designer’s tool it can still be enhanced to support technical extensions. Blend’s extension mechanism is achieved through Behaviours. Similar in effect to the Attached Property solution, Blend Behaviours leave the base class intact and

using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Interactivity;

namespace DatabindingAttachedProperty
{
    /// <summary>
    /// The <see cref="TextChangedBehavior"/> class.
    /// </summary>
    public class TextChangedBehavior : Behavior<TextBox>
    {
        /// <summary>
        /// Called after the behavior is attached to an AssociatedObject.
        /// </summary>
        /// <remarks>Override this to hook up functionality to the AssociatedObject.</remarks>
        protected override void OnAttached()
        {
            base.OnAttached();

            // Insert code that you would want run when the Behavior is attached to an object.
            AssociatedObject.TextChanged += TextChanged;
        }

        /// <summary>
        /// Handles the TextChanged event of the AssociatedObject control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Controls.TextChangedEventArgs"/> instance containing the event data.</param>
        protected void TextChanged(object sender, TextChangedEventArgs e)
        {
            if (!(sender is TextBox))
            {
                return;
            }

            var tb = sender as TextBox;
            BindingExpression bindingExpression = tb.GetBindingExpression(TextBox.TextProperty);
            if (bindingExpression != null && bindingExpression.ParentBinding.Mode == BindingMode.TwoWay)
            {
                bindingExpression.UpdateSource();
            }
        }

        /// <summary>
        /// Called when the behavior is being detached from its AssociatedObject, but before it has actually occurred.
        /// </summary>
        /// <remarks>Override this to unhook functionality from the AssociatedObject.</remarks>
        protected override void OnDetaching()
        {
            base.OnDetaching();

            // Insert code that you would want run when the Behavior is removed from an object.
            AssociatedObject.TextChanged -= TextChanged;
        }
    }
}

The test program consists of the four TextBox options and associated error processing. Entering a character in one text boxes results in the character being shown in each of the other text boxes. This shows that the binding is taking place on every keystroke and not just on the focus changing as is normal in Silverlight.

<UserControl
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:DatabindingAttachedProperty="clr-namespace:DatabindingAttachedProperty"
    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
    x:Class="DatabindingAttachedProperty.MainPage"
    mc:Ignorable="d"
    d:DesignHeight="300"
    d:DesignWidth="604">
    <Grid
        x:Name="LayoutRoot"
        Background="White"
        DataContext="{Binding Source={StaticResource Locator}}">
        <StackPanel Margin="100,30,300,0" Orientation="Vertical" d:LayoutOverrides="Height">
            <DatabindingAttachedProperty:TextChangedBindDependencyProperty
                x:Name="textChangedDependencyProperty"
                Height="36"
                TextWrapping="Wrap"
                Text="{Binding Main.TextDetails, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnDataErrors=True, ValidatesOnExceptions=True}" 
                TextChangedBind="True"
                 Margin="0,0,0,10" />
            <TextBox
                x:Name="textBox1"
                Height="36"
                TextWrapping="Wrap"
                Text="{Binding Main.TextDetails, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnDataErrors=True, ValidatesOnExceptions=True}" 
                Margin="0,0,0,10"
            />
            <TextBox
                x:Name="textBox"
                TextWrapping="Wrap"
                Text="{Binding Main.TextDetails, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnDataErrors=True, ValidatesOnExceptions=True}" 
                Height="36" 
                d:LayoutOverrides="Height" 
                Margin="0,0,0,10">
                <i:Interaction.Behaviors>
                    <DatabindingAttachedProperty:TextChangedBehavior />
                </i:Interaction.Behaviors>
            </TextBox>
            <DatabindingAttachedProperty:TextChangedSubClass
                x:Name="textChangedSubClass"
                TextWrapping="Wrap"
                Text="{Binding Main.TextDetails, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnDataErrors=True, ValidatesOnExceptions=True}"
                DatabindingAttachedProperty:TextChangedInteractor.TextChanged="true"
                Height="36" 
                Margin="0,0,0,10" />
        </StackPanel>
    </Grid>
</UserControl>

The ViewModel contains validation for itself as well as the Model.

using System.ComponentModel;
using System.Linq;
using DatabindingAttachedProperty.Model;
using DatabindingAttachedProperty.Validation;
using FluentValidation.Results;
using GalaSoft.MvvmLight;

namespace DatabindingAttachedProperty.ViewModel
{
    /// <summary>
    /// The <see cref="MainViewModel"/> class.
    /// </summary>
    public class MainViewModel : ViewModelBase, IDataErrorInfo
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="MainViewModel"/> class.
        /// </summary>
        public MainViewModel()
        {
            Model = new MainModel();
        }

        /// <summary>
        /// Gets or sets the text details.
        /// </summary>
        /// <value>The text details.</value>
        public string TextDetails
        {
            get { return Model.TextDetails; }
            set
            {
                if (Model.TextDetails == value)
                {
                    return;
                }

                Model.TextDetails = value;
                RaisePropertyChanged("TextDetails");
            }
        }

        /// <summary>
        /// Gets a value indicating whether this instance is valid.
        /// </summary>
        /// <value><c>true</c> if this instance is valid; otherwise, <c>false</c>.</value>
        public bool IsValid
        {
            get { return SelfValidate().IsValid; }
        }

        /// <summary>
        /// Gets or sets the model.
        /// </summary>
        /// <value>The model.</value>
        protected MainModel Model { get; set; }

        #region Implementation of IDataErrorInfo

        /// <summary>
        /// Gets a message that describes any validation errors for the specified property or column name.
        /// </summary>
        /// <returns>
        /// The validation error on the specified property, or null or <see cref="F:System.String.Empty"/> if there are no errors present.
        /// </returns>
        /// <param name="columnName">The name of the property or column to retrieve validation errors for.</param>
        public string this[string columnName]
        {
            get
            {
                ValidationResult validationResults = SelfValidate();

                if (validationResults == null) return string.Empty;

                ValidationFailure columnResults =
                    validationResults.Errors.FirstOrDefault(x => string.Compare(x.PropertyName, columnName) == 0);

                return columnResults != null ? columnResults.ErrorMessage : string.Empty;
            }
        }

        /// <summary>
        /// Gets a message that describes any validation errors for the object.
        /// </summary>
        /// <returns>
        /// The validation error on the object, or null or <see cref="F:System.String.Empty"/> if there are no errors present. 
        /// </returns>
        public string Error
        {
            get { return ValidationHelper.GetError(SelfValidate()); }
        }

        #endregion

        /// <summary>
        /// Validates the ViewModel and Model.
        /// </summary>
        /// <returns></returns>
        public ValidationResult SelfValidate()
        {
            ValidationResult vr1 = ValidationHelper.Validate<MainViewModelValidator, MainViewModel>(this);
            ValidationResult vr2 = ValidationHelper.Validate<MainModelValidator, MainModel>(Model);

            foreach (ValidationFailure failure in vr2.Errors)
            {
                vr1.Errors.Add(failure);
            }

            return vr1;
        }
    }
}

The Model contains only one property.

namespace DatabindingAttachedProperty.Model
{
    /// <summary>
    /// The <see cref="MainModel"/> class.
    /// </summary>
    public class MainModel
    {
        /// <summary>
        /// Gets or sets the text details.
        /// </summary>
        /// <value>The text details.</value>
        public string TextDetails { get; set; }
    }
}

The validation relies on Fluent Validation package and the validators for both the ViewModel and the Model are shown below.

using DatabindingAttachedProperty.ViewModel;
using FluentValidation;

namespace DatabindingAttachedProperty.Validation
{
    /// <summary>
    /// The <see cref="MainViewModelValidator"/> class.
    /// </summary>
    public class MainViewModelValidator : AbstractValidator<MainViewModel>
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="MainViewModelValidator"/> class.
        /// </summary>
        public MainViewModelValidator()
        {
            RuleFor(model => model.TextDetails).NotNull().Matches(@"^[0-9]+$").WithMessage("Not a number.");
        }
    }
}

 

using DatabindingAttachedProperty.Model;
using FluentValidation;

namespace DatabindingAttachedProperty.Validation
{
    /// <summary>
    /// The <see cref="MainModelValidator"/> class.
    /// </summary>
    public class MainModelValidator : AbstractValidator<MainModel>
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="MainModelValidator"/> class.
        /// </summary>
        public MainModelValidator()
        {
            RuleFor(model => model.TextDetails).NotNull().WithMessage("Must hold a value.");
        }
    }
}

In conclusion, four options have been put forward as successful in solving the missing property changed binding that exists in WPF but not in Silverlight4. The first two methods of extending the base class invasively are not recommended as the full design flexibility are non-existent. The Attached Property solution as well as the Blend Behaviour solution are, by far, the correct way to approach a solution. Personally, I find that only the latter two options should be considered – but then I am a believer in the WPF and Silverlight design philosophy and hope to rid myself of the burden of the VB control development word someday.

That’s all Folks…

Balsamiq – First Thoughts

•January 8, 2012 • Comments Off on Balsamiq – First Thoughts

Over the last year or more I have been looking for a simple, low cost, but powerful wire-framing design tool. On a couple of occasions I have worked on projects where Balsamiq was being used as the wire-frame design tool. I was always impressed with the tool and how simple it was to put together simple, low-fi GUI mock-ups. However, I wrote the tool off as being too expensive for my irregular usage due to it being used in large corporate projects – a mistake on my part to think that all corporate software was expensive and hence Balsamiq would also be so.

Recently I was working on a Silverlight project where the business analysts were defining the rudimentary GUI wire-frames and I was asked to provide some support on setting out the GUI design. After sitting together for about an hour with a colleague we had managed not only to define the static layout of a form but we had also added a considerable amount of the interaction associated with the form with respect to the button functionality and the associated sub-screens and dialogs. We then exported the PDF rendering of the form’s rudimentary workflow and attached this document to the User Story with TFS.

I was very impressed with what could be accomplished in such a short time.

Later I decided to look deeper, to take a deeper dive as we say these days, into the product. The first point that struck me was the cost of the product – I was amazed that such a powerful product could be sold for such a small cost. Finally, a tool was in my meagre budget. With no second thoughts I purchased to product without downloading the trial version. I was convinced.

Positive

Here are just a number of the big hits in Balsamiq:

Negative

Of course all products have their drawback and Balsamiq has a couple that make it difficult to use. But only a couple. For those interested I find that the lack of internal version control or support for an external product and the lack of a meta-diagram showing the linking between related screen mock-ups difficulties but not show-stoppers.

Conclusion

There is very little more than that I thoroughly recommend Balsamiq to anyone that wishes to design mock-ups for an application or product. I have used the basic low-fi functionality as well as using Balsamiq in hi-fi design mock-ups (but more to that in a later blog entry). The cost is very affordable and the associated website and support from Balsamiq as well as the community is excellent.

Just go out and buy it.

That’s all Folks…

Usability – Amount Ordering Control (Part 2)

•December 27, 2011 • Comments Off on Usability – Amount Ordering Control (Part 2)

In the first part of this mini-series I set out the basic problem and made the first sketch of a solution. In this next part I will open up the design and discuss how the parts are put together the components required to finalise the solution. The wireframe of the solution is shown below. There are only three items in the list, however it can be imagined that this will increase over time and that some sort of list would suffice. As we can fully style the list items the possibility of showing a graphic as well as a selection button or check-box is a simple matter. The amount field will be a textbox that only allows the entry of numeric characters based on the order increment value. Range input will be in the form of a slider that indicates the min, max and warning ordering amount levels associated with the selected item. Additionally the slider shall be colour coded to indicate the okay, warning and error ranges using green, amber and red accordingly. There is an interaction between the numeric input and the slider such that changes in one will result in changes in the XX changes in the other. With respect to the Order button, it will only be enabled when an item is selected and a valid selection has been made in either the amount field or using the slider. During access to the database, either during loading or saving, the control “real-estate” will be disabled using a busy indication of some sort. The basic design can be seen below.

Forget Programmability think Usability

Now let us consider the essential part of the design – the usability. Normally, this type of dialog with the user would be split into either a reactive dialog allows the user to enter anything into the conversation and have the validation take place after the Order button has been pressed or a proactive where errors are indicated at that time they happen. In this case a proactive design will disable the Order button whenever the data input conflicts a rule or constraint. For example, entering a value greater than the clearly indicated maximum allowed amount or that no decision has been made as to which item should be selected.

I will reject the reactive design as it makes the user feel “naughty” or “foolish” having entered data that is invalid. Moreover, the user is frustrated in the sense that their time has been wasted entering data only to find that out at a later stage. However, the proactive approach may also lead to user annoyance if the proactive feedback is in anyway “aggressive” i.e. messages incorrectly phrased or the use of harsh colours (especially red in western society).

To overcome the above issues I will inform the user as to the aim of the dialog and its operation up front. This can be achieved using a banner description above the active dialog content similar to that found in Microsoft Money. The proactive validation feedback will be displayed interactively reinforcing the aims of the dialog. In this case exceeding the maximum will result in the Order button being disabled and a text enforcing this – “The maximum amount has been exceeded. Please reduce your wished amount below XX to enable ordering of the YY item”. Similarly for the selection of an item in the list of possible choices could be similar to “Currently no item has been selected. Your order cannot be processed until an item from the above list has been selected”. The banner description will therefore be along the lines of “Your order cannot be processed until an item has been selected from the list of available items and that the wished amount does not exceed the maximum allowable. Order amounts that are above those normally encountered but do not exceed the maximum will be possible. However, those amounts will be displayed as lying in the amber area of the amount indicator.”

The final validation handling will only result in a friendly and helpful message being displayed and the Order button being disabled and the user is free to enter whatever values they wish and to make a conscious decision on ordering more than the normally ordered amounts.

One last usability consideration come when the number of options available to choose from cannot be displayed given the real-estate available for those items. The problem is such; given that the available real-state for the items will only allow three to be displayed at a time and that more than that number of items that are available for selection is significantly more, 20 say, how will the currently selected item remain in focus when the user scrolls away such that the currently selected item moves out of view? The approach taken here is that of elasticity, i.e. the selected item will be pulled back into view after a certain inactivity delay and that the selected item is out of view.

Finally, we need only to consider the placement of the helpful and dynamic informational message. Attaching the message to the display element in error leads to the user searching the dialog for an entry field in error. More helpful is to situate the message close to the “acceptance” input, in this case the Order button. Hence this message will appear either above or below the Order button as this button will have the user’s focus when the user believes that the dialog has been correctly filled out.