Get Microsoft Silverlight

Monday, October 29, 2007

Time Picker User Control

The easiest way to create a WPF TimePicker control is to make a user control wrapped for TimeSpan CLR object. Here is a very simple sample which does that. Basically TimeControl is having 4 DPs one for the entire Value (Which is a TimeSpan) and individual Hours, Minutes and Seconds. (We can optionally add Days and Milliseconds also to make the control rich).

Here is a C# class TimeControl.xaml.cs


    public partial class TimeControl : UserControl
    {
        public TimeControl()
        {
            InitializeComponent();
        }
        public TimeSpan Value
        {
            get { return (TimeSpan)GetValue(ValueProperty); }
            set { SetValue(ValueProperty, value); }
        }
        public static readonly DependencyProperty ValueProperty =
        DependencyProperty.Register("Value", typeof(TimeSpan), typeof(TimeControl),
        new UIPropertyMetadata(DateTime.Now.TimeOfDay, new PropertyChangedCallback(OnValueChanged)));
 
        private static void OnValueChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            TimeControl control = obj as TimeControl;
            control.Hours = ((TimeSpan)e.NewValue).Hours;
            control.Minutes = ((TimeSpan)e.NewValue).Minutes;
            control.Seconds = ((TimeSpan)e.NewValue).Seconds;
        }
 
        public int Hours
        {
            get { return (int)GetValue(HoursProperty); }
            set { SetValue(HoursProperty, value); }
        }
        public static readonly DependencyProperty HoursProperty =
        DependencyProperty.Register("Hours", typeof(int), typeof(TimeControl),
        new UIPropertyMetadata(0, new PropertyChangedCallback(OnTimeChanged)));
 
        public int Minutes
        {
            get { return (int)GetValue(MinutesProperty); }
            set { SetValue(MinutesProperty, value); }
        }
        public static readonly DependencyProperty MinutesProperty =
        DependencyProperty.Register("Minutes", typeof(int), typeof(TimeControl),
        new UIPropertyMetadata(0, new PropertyChangedCallback(OnTimeChanged)));
 
        public int Seconds
        {
            get { return (int)GetValue(SecondsProperty); }
            set { SetValue(SecondsProperty, value); }
        }
 
        public static readonly DependencyProperty SecondsProperty =
        DependencyProperty.Register("Seconds", typeof(int), typeof(TimeControl),
        new UIPropertyMetadata(0, new PropertyChangedCallback(OnTimeChanged)));
 
 
        private static void OnTimeChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            TimeControl control = obj as TimeControl;
            control.Value = new TimeSpan(control.Hours, control.Minutes, control.Seconds);
        }
 
        private void Down(object sender, KeyEventArgs args)
        {
            switch (((Grid)sender).Name)
            {
                case "sec":
                    if (args.Key == Key.Up)
                        this.Seconds++;
                    if (args.Key == Key.Down)
                        this.Seconds--;
                    break;
 
                case "min":
                    if (args.Key == Key.Up)
                        this.Minutes++;
                    if (args.Key == Key.Down)
                        this.Minutes--;
                    break;
 
                case "hour":
                    if (args.Key == Key.Up)
                        this.Hours++;
                    if (args.Key == Key.Down)
                        this.Hours--;
                    break;
            }
        }
 
    } 



And here is the XAML for TimeControl.xaml





<UserControl

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    x:Class="DateTimeTest.TimeControl"

    Height="Auto" Width="Auto" x:Name="UserControl">

  <Grid x:Name="LayoutRoot" Width="Auto" Height="Auto">

    <Grid.ColumnDefinitions>

      <ColumnDefinition Width="0.2*"/>

      <ColumnDefinition Width="0.05*"/>

      <ColumnDefinition Width="0.2*"/>

      <ColumnDefinition Width="0.05*"/>

      <ColumnDefinition Width="0.2*"/>

    </Grid.ColumnDefinitions>

    <Grid x:Name="hour" Focusable="True" KeyDown="Down">

      <TextBlock x:Name="mmTxt" TextWrapping="Wrap" Text="{Binding Path=Hours, ElementName=UserControl, Mode=Default}"

                 TextAlignment="Center" VerticalAlignment="Center" FontFamily="Goudy Stout" FontSize="14"/>

    </Grid>

    <Grid  Grid.Column="1">

      <TextBlock  x:Name="sep1" TextWrapping="Wrap" VerticalAlignment="Center" Background="{x:Null}" FontFamily="Goudy Stout"

                  FontSize="14" Text=":" TextAlignment="Center"/>

    </Grid>

    <Grid  Grid.Column="2" x:Name="min" Focusable="True" KeyDown="Down">

      <TextBlock  x:Name="ddTxt"  TextWrapping="Wrap" Text="{Binding Path=Minutes, ElementName=UserControl, Mode=Default}"

                  TextAlignment="Center" VerticalAlignment="Center" FontFamily="Goudy Stout" FontSize="14"/>

    </Grid>

    <Grid  Grid.Column="3">

      <TextBlock x:Name="sep2"  TextWrapping="Wrap" VerticalAlignment="Center" Background="{x:Null}" FontFamily="Goudy Stout"

                 FontSize="14" Text=":" TextAlignment="Center"/>

    </Grid>

    <Grid  Grid.Column="4" Name="sec" Focusable="True" KeyDown="Down">

      <TextBlock x:Name="yyTxt"  TextWrapping="Wrap" Text="{Binding Path=Seconds, ElementName=UserControl, Mode=Default}"

                 TextAlignment="Center" VerticalAlignment="Center" FontFamily="Goudy Stout" FontSize="14"/>

    </Grid>

 

  </Grid>

</UserControl>


And the control can be used as

<ctrl:TimeControl Hours="10" Minutes="10" Seconds="30" x:Name="TimeControl"/>

5 comments:

C# Disciple said...

Hi there,
I also created a time picker. Have a look at it and give me your feedback..

http://marlongrech.wordpress.com/2007/11/18/time-picker/

Thanks

Gimble said...

Thanks for this, but it seems that the beginning of the .cs file is missing :(

Brandon said...

I couldn't get this code to work for the life of me. Finally, after getting some help, I discovered that change the TextBlocks to TextBoxes (for focus issues maybe?) and the KeyDown to PreviewKeyDown, at least the numbers can be changed with the up/down keys!

It still throws an exception at over 59 minutes, and under -59 minutes. I'm not sure if that was in the scope of what you did here though.

Hope this helps someone! :)

Brandon said...

I couldn't get this code to work for the life of me. Finally, after getting some help, I discovered that change the TextBlocks to TextBoxes (for focus issues maybe?) and the KeyDown to PreviewKeyDown, at least the numbers can be changed with the up/down keys!

It still throws an exception at over 59 minutes, and under -59 minutes. I'm not sure if that was in the scope of what you did here though.

Hope this helps someone! :)

Sameh Mikhail said...

Thank you ,
I used it after modifying it ,
to control range I added MouseWheel event in each textbox , please note that I renamed the Textboxs also for hh,mm,ss

private void hhTxt_MouseWheel_1(object sender, MouseWheelEventArgs e)
{
scroll(sender, e, 23);
}
private void mmTxt_MouseWheel_1(object sender, MouseWheelEventArgs e)
{
scroll(sender, e, 59);
}

private void ssTxt_MouseWheel_1(object sender, MouseWheelEventArgs e)
{
scroll(sender, e, 59);
}
private void scroll(object sender, MouseWheelEventArgs e, int maxVal = 59)
{
var v = int.Parse(((TextBox)sender).Text);
if( e.Delta<0)
if (v == 0) v = maxVal ; else v = v - 1;
else
if (v == maxVal ) v = 00; else v = v + 1;

var s = v.ToString("00");
((TextBox)sender).Text = s;
((TextBox)sender).Select(0, 2);
}