Auction Projection Application
Tuesday, May 15, 2007 8:15 PM
This coming weekend, my son's school is holding it's annual fundraising auction. The event consists of a silent auction (where people bid on items using bid sheets on tables) and a live auction (where all the big ticket items are auctioned in front of a live crowd). This year, we wanted to display the winning bidder names during the live auction, so that when "Bidder 596" wins an item, all of the crowd will know who this is...
To accomplish this, I wrote an application that sits in front of the database that we use for registration and checkout (which is actually a separate application called AuctionPay Event), but following on from some of the UX talks I've been doing recently, I wanted to make sure that I was echoing some of the guidance I've been speaking about when putting this together.
To help validate this, I first created a persona for this application (called Janice). I don't know who exactly will be driving this application on the night, but Janice represented a good cross section of the volunteers that we have. This was super-useful, because at many different points during design the engineer-side-of-me wanted to "just put a button on the screen to do that" yet it just didn't make sense for Janice. As a result, the end product took a little longer to figure out, but I believe should be more successful. A good example of this was running the application in full screen. At first I started thinking "yeah, I'll put some buttons for maximize/restore" before I came to the conclusion that Janice will never want to restore the window - and probably wouldn't understand the terminology anyway - so let's instead write some code to start the application maximized and then take it from there...
Suspecting that Janice has a TV at home, I made the decision to make the application work like a TV remote control (i.e. you just enter the numbers - no need to press the Enter key, and there is no need to have to set focus for the mouse in an edit field etc.). However, just like a TV remote control, if you enter a rogue number (e.g. 8--) and don't finish typing in the rest of the channel number, the input should reset after a few seconds. I recreated the same feel for my application using a System.Windows.Threading.DispatcherTimer (hint - if you are looking for a thread safe timer to work with UI, this is probably what you want vs. dealing with your own delegates). The timer looks at the input and determines whether you are currently entering a valid number or whether the input should reset.
After thinking about the input, the next consideration was making sure that the output can be displayed on most (if not all displays). I was developing on my T60p, which I suspect has a different resolution to the laptop that will be used on the night, so I needed to make sure that the text would scale correctly regardless of the display resolution used. in XAML, this is best done using a Viewbox, which scale both text and images as the screen resolution changes.
<Viewbox x:Name="BidderNameViewbox"><Label/></Viewbox>
Playing around with the margins of the label takes a little time (and I had to add a little padding on the shorter names to get them truly centered), but overall this saved a lot of custom code to resize the text. Finally, I wanted to add a little "wow" by creating some reflections in WPF (e.g. the reflection of the text in the screenshot). This is fairly easy to do in WPF, but not as easy as I thought it would be (OK... I was hoping for <reflection enabled="true" offset="50"/> - no such luck :). Instead, I ended up creating a second label, applying a Y transform to mirror, and then using an opacity brush to fade out the text.
<Label.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="1" ScaleY="-1"/>
<SkewTransform AngleX="0" AngleY="0"/>
<RotateTransform Angle="0"/>
<TranslateTransform X="0" Y="0"/>
</TransformGroup>
</Label.RenderTransform>
<Label.OpacityMask>
<LinearGradientBrush EndPoint="0.499,0.493" StartPoint="0.499,1.066">
<GradientStop Color="#FF000000" Offset="0"/>
<GradientStop Color="#00FFFFFF" Offset="1"/>
</LinearGradientBrush>
</Label.OpacityMask>
All in all a great experience so far (although the true test will be on Saturday evening, with 500 people looking at the application - maybe I need to put some more thought in to the user testing? :-))