Creating List Picker for WP7.


For the application that I am working on right now I needed a control that would allow a user to select a single item from a small list. Similar to how the list picker is defined in the design templates:

This document also specifies that when the user selects one of the entry points, the control should expand to show all available items. Once the user taps on an item to select it, the list picker is then closed. The document also states that this control can have up to 5 items.

The list picker control is not going to be shipped as a part of the Windows Phone Develop Tools, so I’ve decided to take matters into my own hands and try to create one by myself. I thought that it should be a good excersise for me  to learn how to create a custom control in Silverlight for Window Phone 7 platform and pickup up a few new things in the process.

Looking at the behavior of the list picker in the built-in applications (Setting /Theme) on the emulator, I realized that there’s no need to create control from the scratch. I should be able to re-use the ListBox control and animate changing its height at the appropriate times. So I’ve created a custom control and derived it from the ListBox:

    /// <summary>
    /// The implementation of the inline ListPicker control
    /// </summary>
    public class ListPicker : ListBox
    {
        #region fields
       
        private double itemHeight;
        private bool expanded;
        private int selectedIndex;
        private Storyboard storyboard;

        #endregion

        #region constructor
      
        public ListPicker()
        {
            this.DefaultStyleKey = typeof(ListPicker);
            base.LayoutUpdated += new EventHandler(ListPicker_LayoutUpdated);
            base.SelectionChanged += new SelectionChangedEventHandler(ListPicker_SelectionChanged);          
        }

        #endregion
        
       // ... more code

     }

I also created an override for the OnMouseLeftButtonUp event:

     protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
     {
          base.OnMouseLeftButtonDown(e);
          // Change background color
          this.Background = new SolidColorBrush(Colors.White);
          if (!expanded)
          {
              // Show border when expanded
              this.BorderThickness = new Thickness(2);
              // Assign previously selected index
              this.SelectedIndex = selectedIndex;
              // Create and begin animation
              this.storyboard = GetDropDownAnimation(this.Height, itemHeight * this.Items.Count);
              this.storyboard.Begin();
           }
           else
           {
              // Hide border
              this.BorderThickness = new Thickness(0);
              // Restore background 
              this.Background = (Brush)(Application.Current.Resources["PhoneTextBoxBrush"]);
              // Unselect an item in the listbox
              this.SelectedIndex = -1;
              // Create and begin animation
              this.storyboard = GetDropDownAnimation(this.Height, itemHeight);
              this.storyboard.Begin();
            }

            expanded = !expanded;
      }   

In the code above I identify the current state of the control whether it’s expanded or collapsed, change its backround color and show/hide the border to match to the look of the native control. Then I create an animation with the appropriate parameters for the height of the control.

In order to match the style of my control to the native one I also needed to set some default values in the XAML, so I’ve added the generic.xaml file to my project. One important note is that this file must be located in the “\Themes” folder of your project in order for it to be picked up by the compiler:

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:Phone.Controls;assembly=Phone.Controls">
    
    <Style TargetType="local:ListPicker">
        <Setter Property="Background" Value="{StaticResource PhoneTextBoxBrush}"/>
        <Setter Property="Foreground" Value="{StaticResource PhoneTextBoxForegroundBrush}"/>
        <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled"/>
        <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Disabled"/>
        <Setter Property="BorderThickness" Value="0"/>
        <Setter Property="BorderBrush" Value="Black"/>
        <Setter Property="Padding" Value="0"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="local:ListPicker">
                    <ScrollViewer x:Name="ScrollViewer" Foreground="{TemplateBinding Foreground}" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}">
                        <ItemsPresenter/>
                    </ScrollViewer>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

</ResourceDictionary>

For this file I re-used the default ListBox’s style template and modified a few values for the Backround, Foreground and BorderBrush. I also needed to disable the scrolling in the control this is why the VerticalScrollBarVisibilty property is also set to “Disabled”. In order for the control to use this style we also need to assign the DefaultStyleKey property in the constructor of the control as you can see in the first code snippet above.

This is how the sample that’s using this control looks like:

 

In addition, the native control has a tilting effect when tapping on items, so I just added the most excellent Peter Torr’sTiltEffect class to my sample project after which my control has become a pretty close to the native one by the looks and behavior.

Feel free to re-use the code for your needs.

Enjoy…

UPDATE: I’ve uploaded a new version of the control with a few bug fixes.

UPDATE2: Uploaded version that is compiles against public beta tools.

UPDATE3: The beta version of the tools has the bug that causes the OnMouseLeftDown event raised twice. Put the workaround into the code and uploaded the new version.

Comments (14)

  1. I can't get this solution to build.  I get the following two errors both in VS2010 Express and Blend.

    Error 1 Predefined type 'System.Object' is not defined or imported Phone.Controls

    Error 2 Metadata file 'G:Desktop Contentslist-pickerPhone.ControlsBinDebugPhone.Controls.dll' could not be found TestListPicker

  2. AlexYak says:

    It looks like you've lost a reference to the mscorlib. Try to add it manually.

  3. Thank you for providing this control.  However, when I try to build it, I get this error message:

    Error 1 Predefined type 'System.Object' is not defined or imported Phone.Controls

    How can I resolve this message?  Do I need a different version of the tools?

    Dick Heuser – dick.heuser@cadorian.com

  4. When I try to add it manually, I get a message that mscorlib was not built using the Windows Phone runtime, yet, it is the same version that I use daily to build WP7 apps.

    Dick Heuser – dick.heuser@cadorian.com

  5. AlexYak says:

    I've recompiled the solution for the public beta tools. Please re-download it from the blog.

    Thanks… Alex

  6. The control doesn't appear to work.  When you tap the box, it animates down, showing the other items, but clicking them does not change the selected item, and clicking the "3 minute" option seems to freeze the box, and it doesn't animate closed again.

  7. AlexYak says:

    The beta version of the tools has the bug that causes the OnMouseLeftDown event raised twice. Put the workaround into the code and uploaded the new version.

    Thanks… Alex

  8. I can now build and run this control.  It seems to work well.

    Thanks,

    Dick

  9. RichardHeuser says:

    Actually, it seems that the selection picked does not stay highlighted.

  10. Michel van der Vlugt says:

    There is a problem when using a binding on SelectedItem. When the ListBox is shown is doesn’t present the selected item, always the first. To fix it I’ve changed the implementation of ListPicker_SelectionChanged to:

               if (base.SelectedIndex > -1)

                   SelectedIndex = base.SelectedIndex;

    This seems to work. Do you see any potential problems?

  11. Adrian says:

    Hi.

    I have the same problem when using a binding on SelectedItem. When the ListBox is shown is doesn’t present the selected item, always the first. To fix it I’ve changed the implementation of ListPicker_SelectionChanged to but I had the error:

    System.Windows.ContentControl does not content a definition for SelectedIndex

  12. frank says:

    thanks for sharing this control code

  13. Motasim says:

    I want to get the text of the selected item in the list picker but i keep getting some other thing like assistant*page1*sampledata

    can any body help me please ??