Introduction

In this article, I will describe a UIPickerView-like control I created using WPF. The UIPickerView is an iOS control for picking an
item from a list via a tumbler. Typically, this control is associated with touch input, but I have found it to work well with the mouse as
well. I can envision it being potentially useful in a desktop application, especially if a touch-enabled monitor is part of the system.

Background

A couple of months ago, while writing an article on user interface design, I was
trying to think of different ways to construct a date/time control. One of the ideas I came up with was to use tumblers for the different date/time parts,
just like in a UIPickerView control. During that exploration, I created a prototype WPF implementation. I have decided to clean up that prototype and
share it with the CodeProject community.

Using the Code

Using the control is pretty straightforward. Simply include the WpfUIPickerLib namespace in your XAML file and create a WpfUIPickerControl element.
There are only two custom dependency properties you need to be aware of:

AlwaysOpen: If set to True, WpfUIPickerControl will always be open and the tumblers will be visible.
If set to False (the default), the control will look like a textbox displaying only the selected value(s). Clicking on the control will open it
and the tumblers will then become visible, enabling the user to pick a new value. Clicking anywhere outside the control will close it (if AlwaysOpen is False).

Tumblers: This dependency property is a list of WpfUIPickerLib.TumblerData objects that represent the different tumblers you want
to display. Each TumblerData instance represents a different tumbler in the control.

The following XAML demonstrates including the WpfUIPickerLib namespace and how to declare a WpfUIPickerControl object:

<Windowx:Class="WpfUIPicker.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:wpfui="clr-namespace:WpfUIPickerLib;assembly=WpfUIPickerLib"Title="MainWindow"Height="300"Width="325"x:Name="thisWnd"><StackPanel><TextBlockText="WpfUIPickerControl with AlwaysOpen set to True"HorizontalAlignment="Center"/><wpfui:WpfUIPickerControlHorizontalAlignment="Center"AlwaysOpen="True"Tumblers="{Binding ApparelTumblers, ElementName=thisWnd}"/><TextBlockText="WpfUIPickerControl with AlwaysOpen set to False"HorizontalAlignment="Center"Margin="0,20,0,0"/><wpfui:WpfUIPickerControlHorizontalAlignment="Center"AlwaysOpen="False"Tumblers="{Binding DateTimeTumblers, ElementName=thisWnd}"/></StackPanel></Window>

The TumblerData object is a thin wrapper around the list of values you want to display in a single tumbler (ToString will be called on each
value). It is needed by the control to trigger updates, to get/set the selected value, and to display a "seperator" (optional). Because I originally created this control for date/time,
I wanted the "closed" version of the control to be able to display string tokens between tumbler values. For example, if I created three tumblers for the date (month, day, year),
and the user picked 9 for the month, 5 for the day, and 2005 for the year, then I wanted the closed version of the control to display "09/05/2005". To support this
usage, you can just set the string "/" to be the separator in the TumblerData object for the month and day tumblers.
The TumblerData.Seperator property is ignored if the control is set to AlwaysOpen=true.

The following code demonstrates how to create TumblerData objects for a date/time implementation that supports dates from 1/1/1990 to 12/31/2011:

Points of Interest

WpfUIPickerControl supports three different ways to change the value of a tumbler: drag, mouse wheel, and just click on the desired value.
The control implements "closed" by hiding non-selected values (hence the funky binding in the valueTemplate). Each tumbler is rendered
as a StackPanel within a grid on a canvas. Drag is implemented by changing the Canvas.Top property on the grid.

Comments and Discussions

Hi thanks so much for sharing this amazing control! I've been trying to add this control to a windows form application but have been unsuccessful. Can you please walk me through the process of hosting this control in my existing windows form app? Thanks!

I am very new to wpf but very much like your wpfUIPickerViewer. I have added it to a view in a mvvm application and it works exactly how I want except that I do not understand how to get the selected values out to the view model. Would you be able to give me some guidance?

I would like to know if it is possible when I am dragging the elements to the top there are 3 elements on the top and everything else is empty.
is is possible to all the control will be fill? because when I have only 3 elements on top I can drag it down unless I touch the elements itselfs and not the empty "grid" below.

This looks a great control.
Is there a quick way to adapt this control to display images loaded from jpg files? So that the look is very much like a fruit machine reel?
Initially I thought it would be an easy thing for me to add but i'm finding the coding of this to be a bit beyond my level.

very nice control there.
I wanted to implement, that there are not always 31 days within every month,
but when I change tumblers to runtime, they update, but they are not shown
correctly. The whole field is empty then, i dont get it, it's the same
method call like at the beginning of the program start, but just with the
selectedvalues?

The c# code is working, in VB it shows a NullReferenceException when daysTumbler should get the updated values from iDays variable. By Setting a break Point during debugging the daysTumbler is filled with values and the iDays Variable too.

Now it has its propertyChanged and fires it, I can change the values from a Tumbler. (Don't get the Nullreferenceexception anymore) But the UI does not update. The Tumbler Values only changes in code, do I Need to set the Datacontext in VB?

By chaning the value of the control, I take the selected Date and try to parse it into DateTime. Because of the Culture Information I get an error if I'm choosing 30 February 2014 for example.(february has only 28 days) How can I update the Days Range from the Selected Month in this example.

4. When you create your TumblerData objects (in this case the month tumbler), subscribe to the TumblerUpdated event. In this event handler you can change the Values of your day tumbler based on the selected Value of the month tumbler.

First of all: excellent job on the control! There is one thing I can not get to work: setting the SelectedValueIndex for one of the tumblers from code.
The initial one (which is passed through the constructor of TumblerData) works fine. But after adding entries in TumblerData I need to update the SelectedValueIndex. How to achieve that?
I'm using your sample and added the following code to test this (where _apObjects is one of the tumblers in ApparelTumblers):

Yikes, you found a nasty bug. Unfortunately I'm very busy at the moment and wrote this article years ago. However, I do have a fix for you albeit not a very pretty one. I was able to get this to work like you want (I think) by using a ContentControl to house the WpfUIPickerControl. In your xaml, remove the WpfUIPickerControl and add a ContentControl instead. Then, in your button click, set the Content of the ContentControl to be a new WpfUIPickerControl instance with the Tumblers set the way you want them (with the new value already added and the selectedvalueindex set properly). I'm not sure why just updating the Tumblers does not redraw correctly and I don't have the time right now to properly look into it. With the method I described above, it basically just forces a brand new WpfUIPickerControl to be created and drawn -- and as you pointed out the initial draw of the control works fine.

Hope that helps. You can email me directly if you're still stuck and I'll try to help. (mheydlauf at gmail).
Mike.

Are you using Binding to set the tumbler value like I have in the example? If so, when you change the tumbler data you have to call PropertyChanged to force the binding to re-evaluate (your backing class has to implement INotifyPropertyChanged).

That's a great question Marco, and kind of a tricky one. The short answer is, yes you can. If you want to bind to the selected value of one of the tumblers, no problem, TumblerData implements INotifyPropertyChanged so you're all set. If you want to bind to the selected values(plural) of all of the tumblers, you need to modify my code slightly so that PropertyChanged gets called for the "SelectedValues" property of the control every time the SelectedValueIndex gets changed on a tumbler.

Then just change the places in WpfUIPickerControl.xaml.cs where td.SelectedValueIndex is set (there are only two places) and replace them with this method instead, then every time a tumbler selected value is changed, anything bound to the controls SelectedValues property will re-evaluate.