Toolkit Control – TimePicker

 

 

 

You can learn a great deal about the new TimePicker class with a fairly simple example program that I will lay out and zip through here (a full video on the topic is coming very soon).

ClickOnGetStartedTo begin, you’ll want to download the latest Silverlight Toolkit, which you can easily do by  clicking on Get Started in the left margin of my blog (or on the image to the left) and from our Get Started page, click on number 4, Download Silverlight Toolkit.

That will bring you to the Codeplex home of the Toolkit where you can download any or all of the controls, the source to the controls, and/or the samples for Silverlight 2 or 3.  To get the latest controls, click on the menu item Releases, and then download either or both of the installers.

 

 

ToolkitDownload

The installer will place the libraries in the right location and make adding the resources to your application a snap.

 

Recreating The Example

Create a new Silverlight application (Silverlight 2 or 3) named TimePicker and add two resources: Windows.Controls and Windows.Controls.Input.Toolkit

AddRef

Add a name in the Xaml for “input” and let Intellisense help you choose System.Windows.Controls.Input.Toolkit”.  Set up a grid with five rows and three columns, as shown here,

<UserControl x:Class=”TimePickerBlogSL3.MainPage”
    xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
    xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
    xmlns:input=
“clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Input.Toolkit

    Width=”800″ Height=”200″>
   <Grid x:Name=”LayoutRoot”
         Background=”White”>
      <Grid.RowDefinitions>
         <RowDefinition Height=”1*” />
         <RowDefinition Height=”1*” />
         <RowDefinition Height=”1*” />
         <RowDefinition Height=”1*” />
         <RowDefinition Height=”1*” />
      </Grid.RowDefinitions>
      <Grid.ColumnDefinitions>
         <ColumnDefinition Width=”3*” />
         <ColumnDefinition Width=”2*” />
         <ColumnDefinition Width=”2*” />
      </Grid.ColumnDefinitions>

 

Our goal now is to add a Time picker and to allow the user to set the PopupButtonMode (do you click on the picker or hover over it to make it open) and the PopUp (RangeTimePicker or ListTime). 

ListTime looks like this

popuptime

 

RangeTimePicker looks like this:

 

RangePickerTime

Be Careful with Seconds

To add an interesting twist, the RangeTime popup optionally allows you to set the seconds while the Listtime does not, though it does allow you to set the number of minutes between each interval. (For example, I can set the interval to 7 minutes, and the ListTime changes accordingly, as shown in the next image)

7Minutes

Getting Started

With the grid iin place, let’s add a very simple TimePicker control to get started,

 

<input:TimePicker x:Name=”FirstTP”
                  Width=”200″
                  Grid.Row=”0″
                  Grid.Column=”0″
                  Margin=”5″
                  HorizontalAlignment=”Left”
                  VerticalAlignment=”Center” />

Run the program. That’s it, you’re done. Works great.

Of course, it would be good to get the value from the time picker (you know, just to show we can).  To satisfy the cynics, we’ll add a prompt in the second column and display the value in the third,

<TextBlock x:Name="TimePrompt"
Text="Time:"
FontSize="18"
VerticalAlignment="Bottom"
HorizontalAlignment="Right"
Margin="5"
Grid.Row="0"
Grid.Column="1"/>
<TextBlock x:Name="Time"
FontSize="18"
Foreground="Red"
VerticalAlignment="Bottom"
HorizontalAlignment="Right"
Margin="5"
Grid.Row="0"
Grid.Column="2" />

There are two ways to get the value from the time picker into the “Time” textblock.

In Silverlight 2 (and optionally in Silverlight 3) you can respond to the DropDownClosed event that will fire when the user makes the time selection and the popup window closes

FirstTP.DropDownClosed += 
new RoutedPropertyChangedEventHandler<bool>( FirstTP_DropDownClosed );


void FirstTP_DropDownClosed(
object sender,
RoutedPropertyChangedEventArgs<bool> e )
{
Time.Text = FirstTP.Value.ToString();
}

Element Binding

In Silverlight 3 you can eliminate this event and handler and instead data bind the text element directly to the value of the TimePicker element as shown in this video and illustrated here,

<TextBlock x:Name=”Time”
  FontSize=”18″
  Foreground=”Red”
  Text=”{Binding Value, Mode=OneWay, ElementName=FirstTP}”
  VerticalAlignment=”Bottom”
  HorizontalAlignment=”Right”
  Margin=”5″
  Grid.Row=”0″
  Grid.Column=”2″ />

Setting the Mode and Type

We’ll let the user choose the mode and  popup type with radio buttons,

<!--Pop up mode-->
<TextBlock x:Name="PopupModePrompt"
Text="Style:"
FontSize="18"
VerticalAlignment="Bottom"
HorizontalAlignment="Right"
Margin="0,0,5,0"
Grid.Row="1"
Grid.Column="1" />
<StackPanel x:Name="PopUpModeStackPanel"
Orientation="Horizontal"
Grid.Row="1"
Grid.Column="2"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch">
<RadioButton x:Name="PopUpModeClickRB"
GroupName="PopUpMode"
Content="Click"
Width="Auto"
Height="Auto"
VerticalAlignment="Bottom"
HorizontalAlignment="Left"
Margin="10,0,0,0"
FontFamily="Georgia"
FontSize="18"
IsChecked="true" />
<RadioButton x:Name="PopUUpModeHoverRB"
GroupName="PopUpMode"
Content="Hover"
Width="Auto"
Height="Auto"
VerticalAlignment="Bottom"
HorizontalAlignment="Left"
Margin="10,0,0,0"
FontFamily="Georgia"
FontSize="18" />
</StackPanel>

(I won’t take the space here to show the PopUpBoxPrompt and radio buttons except to point out that they will be on Row 3 and otherwise very similar to the above.)

Optional Input Fields

Rows 2 and 4 are reserved for the input for the “Interval” (which is visible only when the user chooses Popup) and the UseSeconds checkbox (which is visible only when the user chooses RangePicker.) In the following illustration all are visible, a condition that is never seen in the wild,

 

4Rows

We want to display MinutesInterval when the user clicks on Pop Up and hide that and display Use Seconds when the user selects Range Picker.

 

 

 

The only tricky part is that while it is perfectly safe (if meaningless) to have an interval when using the RangePicker, an exception will be thrown if the TimePicker’s PopupTimeSelectionMode is set to AllowSecondsSelection and the TimePicker’s Popup is of type ListTimePickerPopup, so this must be carefully avoided. The place you typically run into this is when the user changes to RangePicker, selects UseSeconds, and then switches back to Popup. So we need to be just a bit careful.

 

Opening Vs. Opened

Like many controls, the TimePicker fires four events related to opening and closing its drop down (there does seem to have been some debate about whether it is a drop down or a pop up!)

  • DropDownOpening
  • DropDownOpened
  • DropDownClosing
  • DropDownClosed

That gives us exactly the level of control we need.

void FirstTP_DropDownOpening(
   object sender,
   RoutedPropertyChangingEventArgs<bool> e )
{
   SetMinutesInterval();
   SetSeconds();
}

We will check (and set) the interval and most important whether we are allowing seconds just before we open the chosen popup (dropdown) and thus when it is certain which one is wanted.

Sequence of Events

the sequence of events, then, is that we present the time picker on the left and two sets of radio buttons on the right, defaulting to Pop Up. We also present a text box in which the user can fill in the Minutes Interval (which we default to 15),

TimePickerStep1

 

When the page is loaded the constructor runs in which we assign to the value of the Time Picker the current time. We also set the event handlers and the initial PopupMode and PopUpType based on reading which radio buttons are checked (in this case, the radio buttons were set in the Xaml).

 

 

 
The Constructor
public MainPage()
{
InitializeComponent();

// initialzie the value to the current time
FirstTP.Value = DateTime.Now;

// radio button event handlers
PopUUpModeHoverRB.Checked +=
new RoutedEventHandler( PopUpMode_Change );
PopUpModeClickRB.Checked +=
new RoutedEventHandler( PopUpMode_Change );
PopupRB.Checked +=
new RoutedEventHandler( PopUpBox_Change );
RangePickerRB.Checked +=
new RoutedEventHandler( PopUpBox_Change );

// time picker event handlers
FirstTP.DropDownOpening +=
new RoutedPropertyChangingEventHandler<bool>(
FirstTP_DropDownOpening );
FirstTP.DropDownClosed +=
new RoutedPropertyChangedEventHandler<bool>(
FirstTP_DropDownClosed );
FirstTP.DropDownOpened +=
new RoutedPropertyChangedEventHandler<bool>(
FirstTP_DropDownOpened );

// set initial modes
SetPopUpMode();
SetPopUpType();
}

At the end of the constructor we call both SetPopupMode (click vs. hover) and SetPopupType (popup vs. range)

private void SetPopUpMode()
{

   // radio buttons are nullable booleans,  check for true
   if (PopUUpModeHoverRB.IsChecked == true)
   {
      FirstTP.PopupButtonMode = ClickMode.Hover;
   }
   else
   {
      FirstTP.PopupButtonMode = ClickMode.Press;
   }
}

SetPopUpType is very similar, but here we also set the visibility of the mutually exclusive user input fields (time interval or number of seconds)

private void SetPopUpType()
{
bool range = RangePickerRB.IsChecked == true;
UseSecondsCB.Visibility = range ? Visibility.Visible : Visibility.Collapsed;
UseSecondsPrompt.Visibility = range ? Visibility.Visible : Visibility.Collapsed;
TimeMinutesIntervalPrompt.Visibility = range ? Visibility.Collapsed : Visibility.Visible;
MinutesInterval.Visibility = range ? Visibility.Collapsed : Visibility.Visible;
   // old c programmer habit, same as if (range == true) for non-nullable bool   
if (range)
{
FirstTP.Popup = new RangeTimePickerPopup();
}
else
{
FirstTP.Popup = new ListTimePickerPopup();
}
}

The construct in the second through fourth lines is the C# ternary operator. You read this line

UseSecondsCB.Visibility = range ? Visibility.Visible : Visibility.Collapsed;

like this:  “if range is true, set UseSeconds.CB’s Visibility property to the enumerated value Visibility.Visible, otherwise set it to the enumerated value Visibility.Collapsed.” 

More generally, you can write

<type> <identifier> = <bool> ? <true> : <false>;

That is, test the boolean and assign the true value to the identifier if the bool evaluates true, otherwise assign the false value.  A simpler example would be

string nickname = IsOver18? “buddy” : “kid”;

which will set the string variable nickname to “buddy” if IsOver18 is true and will set it to “kid” otherwise.

 

Implementing DropDownOpening

As noted above, each time the drop down is opened we call the two methods that set one of the two mutually exclusive states, paying particular attention to the seconds.

void FirstTP_DropDownOpening( 
object sender,
RoutedPropertyChangingEventArgs<bool> e )
{
SetMinutesInterval();
SetSeconds();
}

SetMinutesInterval checks to see if the text box has something in it, and if so it converts that value to an int (ignoring the bulletproofing of testing for invalid values) and then assigns that value to the PopUpMinutesInterval of the timePicker. If the textbox has no value in it, the value is set to 10.  We don’t bother checking to see why type of Popup the user has chosen because there is no harm or foul in setting this when it isn’t used.

private void SetMinutesInterval()
{
if (MinutesInterval.Text.Length > 0)
{
// need try block here!
FirstTP.PopupMinutesInterval =
Convert.ToInt32( MinutesInterval.Text );
}
else
{
FirstTP.PopupMinutesInterval = 10;
}

}

 

SetSeconds has to be a bit more cautious because, as noted, an exception will be thrown if you set the TimePicker’s PopupTimeSelectionMode to AllowSeconds when the Popup type is not RangeChecker. In fact, (at the risk of saying this one more time)  the reason we’ve created this method is to ensure that we’re testing at exactly the last possible moment when the user can no longer change this mode!

 

private void SetSeconds()
{
if (RangePickerRB.IsChecked == true &&
UseSecondsCB.IsChecked == true)
{
FirstTP.PopupTimeSelectionMode =
PopupTimeSelectionMode.AllowSecondsSelection;
FirstTP.Format = new LongTimeFormat();
}
else
{
FirstTP.PopupTimeSelectionMode =
PopupTimeSelectionMode.HoursAndMinutesOnly;
FirstTP.Format = new ShortTimeFormat();
}
}

We only set the PopupTimeSelectionMode to AllowSecondsSelection if two things are true. First, it must be true that the user has requested a RangePicker and second the user has to have checked the checkbox indicating to use seconds,

 

UseSecondsCB

 

While it causes an exception to use seconds with a Popup picker, it is optional to use seconds with a RangePicker and the RangePicker works fine either way as shown below,

 

 

 

 

 

 

RangePickerNoSecondsRangePickerSeconds

 

Adding Formatting

A final touch that is a good example of where small things make a big difference, is that we change the Format property of the TimePicker if we are using second from ShortTimeFormat to LongTimeFormat, and thus show the seconds. You see this in the SetSeconds method, above,

 

FirstTP.PopupTimeSelectionMode = 
PopupTimeSelectionMode.AllowSecondsSelection;
FirstTP.Format = new LongTimeFormat();

Rather than post a complete project (which would require having one for Silverlight 2 and one for Silverlight 3, I’ve opted to post only the xaml and xaml.cs files. For Silverlight3 just copy the Xaml to MasterPage.xaml and for Silverlight to copy to Page.xaml. Be sure to fix up the Namespace names and to add the two references.  The source code is here.

 
 

About Jesse Liberty

Jesse Liberty has three decades of experience writing and delivering software projects and is the author of 2 dozen books and a couple dozen online courses. His latest book, Building APIs with .NET will be released early in 2025. Liberty is a Senior SW Engineer for CNH and he was a Senior Technical Evangelist for Microsoft, a Distinguished Software Engineer for AT&T, a VP for Information Services for Citibank and a Software Architect for PBS. He is a Microsoft MVP.
This entry was posted in z Silverlight Archives. Bookmark the permalink.

One Response to Toolkit Control – TimePicker

Comments are closed.