Tim Hibbard

CEO for EnGraph software
posts - 626, comments - 1586, trackbacks - 459

My Links

News



Add to Google

Twitter












Tag Cloud

Article Categories

Archives

Post Categories

Image Galleries

EnGraph Blogs

Links

Other

Roll

Thursday, April 14, 2011

WPF TimeSpinner Control

spinner

As I discussed in my last post, we created a TimeSpinner control based on the Extended WPF Toolkit’s ButtonSpinner.

Now, the toolkit has a DateTimeUpDown control that will display a date or time and allow the user to click the spinners for each time part, but I needed a little more. I needed the raw text to be editable and I wanted the spinners to just modify the minutes portion. I also wanted to have special parsing based on the number of characters entered.

4 chars:
Parse as military time.

3 chars: 
Parse as modified military time, but only assume waking hours.

So 933 becomes 9:33 AM

345 becomes 3:45 PM

We put the cut-off at 7. Anything before 7 assumes PM anything 7 or after assumes AM.

2 chars:
Assuming shortened military time.

12 = 12:00 PM

15 = 3:00 PM

1 char:
Parses as shortened military time, but assumes waking hours like 3 chars.

1 = 1:00 PM

8 = 8:00 AM

 

The TimeSpinnerControl exposes one bindable property, Time. So it would be consumed like:

   1: <runtime:TimeSpinnerControl Time="{Binding Path=ReturnTime}"
   2:                             x:Name="spinnerReturnTime"/>

 

So, here is what we did:

TimeSpinnerControl.xaml:

   1: <UserControl x:Class="ParaPlan.Controls.TimeSpinnerControl"
   2:              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
   5:              xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
   6:              xmlns:wpftoolkit="clr-namespace:Microsoft.Windows.Controls;assembly=WPFToolkit.Extended"
   7:              mc:Ignorable="d">
   8:     <wpftoolkit:ButtonSpinner Spin="Time_Spin" IsTabStop="False">
   9:         <TextBox Name="textTime" 
  10:                  MinWidth="60" 
  11:                  Margin="0"
  12:                  MouseDoubleClick="textTimeDoubleClick"
  13:                  Keyboard.GotKeyboardFocus="textTimeGotKeyboardFocus"
  14:                  MouseEnter="textTimeMouseEnter"
  15:                  Keyboard.LostKeyboardFocus="textTimeLostKeyboardFocus"/>
  16:     </wpftoolkit:ButtonSpinner>
  17: </UserControl>

 

TimeSpinnerControl.xaml.cs:

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Linq;
   4: using System.Text;
   5: using System.Windows;
   6: using System.Windows.Controls;
   7: using System.Windows.Data;
   8: using System.Windows.Documents;
   9: using System.Windows.Input;
  10: using System.Windows.Media;
  11: using System.Windows.Media.Imaging;
  12: using System.Windows.Navigation;
  13: using System.Windows.Shapes;
  14: using ParaPlan.Extensions;
  15: using System.Globalization;
  16:  
  17: namespace ParaPlan.Controls
  18: {
  19:     /// <summary>
  20:     /// Interaction logic for TimeSpinnerControl.xaml
  21:     /// </summary>
  22:     public partial class TimeSpinnerControl : UserControl
  23:     {
  24:         public TimeSpinnerControl()
  25:         {
  26:             InitializeComponent();
  27:         }
  28:  
  29:         #region Time Property
  30:  
  31:         /// <summary>
  32:         /// Time Dependency Property
  33:         /// </summary>
  34:         public static readonly DependencyProperty TimeProperty =
  35:             DependencyProperty.Register("Time", typeof(DateTime), typeof(TimeSpinnerControl),
  36:                 new FrameworkPropertyMetadata(DateTime.MinValue,
  37:                     FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
  38:                     new PropertyChangedCallback(OnTimeChanged)));
  39:  
  40:         /// <summary>
  41:         /// Gets or sets the Time property.
  42:         /// </summary>
  43:         public DateTime Time
  44:         {
  45:             get { return (DateTime)GetValue(TimeProperty); }
  46:             set { SetValue(TimeProperty, value); }
  47:         }
  48:  
  49:         /// <summary>
  50:         /// Handles changes to the Time property.
  51:         /// </summary>
  52:         private static void OnTimeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  53:         {
  54:             ((TimeSpinnerControl)d).OnTimeChanged(e);
  55:         }
  56:  
  57:         /// <summary>
  58:         /// Provides derived classes an opportunity to handle changes to the Time property.
  59:         /// </summary>
  60:         protected virtual void OnTimeChanged(DependencyPropertyChangedEventArgs e)
  61:         {
  62:             DateTime value = (DateTime)e.NewValue;
  63:             if (value.IsMinValue())
  64:             {
  65:                 textTime.Text = "";
  66:                 return;
  67:             }
  68:             textTime.Text = value.ToShortTimeString();
  69:         }
  70:  
  71:         #endregion
  72:  
  73:         #region Event Handlers
  74:         /// <summary>
  75:         /// Handles when the user clicks a spinner
  76:         /// </summary>
  77:         /// <param name="sender"></param>
  78:         /// <param name="e"></param>
  79:         private void Time_Spin(object sender, Microsoft.Windows.Controls.SpinEventArgs e)
  80:         {
  81:             if (e.Direction == Microsoft.Windows.Controls.SpinDirection.Increase)
  82:             {
  83:                 this.Time = this.Time.AddMinutesRoundUp(5);
  84:             }
  85:             else
  86:             {
  87:                 this.Time = this.Time.AddMinutesRoundUp(-5);
  88:             }
  89:  
  90:         }
  91:  
  92:         /// <summary>
  93:         /// Handles when the user double clicks in the textbox
  94:         /// </summary>
  95:         /// <param name="sender"></param>
  96:         /// <param name="e"></param>
  97:         private void textTimeDoubleClick(object sender, MouseButtonEventArgs e)
  98:         {
  99:             this.textTime.SelectAll();
 100:         }
 101:  
 102:         /// <summary>
 103:         /// Handles when the user tabs in the control
 104:         /// </summary>
 105:         /// <param name="sender"></param>
 106:         /// <param name="e"></param>
 107:         private void textTimeGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
 108:         {
 109:             this.textTime.SelectAll();
 110:         }
 111:  
 112:         /// <summary>
 113:         /// Handles when the user enters the control by mouse
 114:         /// </summary>
 115:         /// <param name="sender"></param>
 116:         /// <param name="e"></param>
 117:         private void textTimeMouseEnter(object sender, MouseEventArgs e)
 118:         {
 119:             this.textTime.SelectAll();
 120:         }
 121:  
 122:         /// <summary>
 123:         /// Handles when the control loses focus. Now we want to parse what
 124:         /// the user entered
 125:         /// </summary>
 126:         /// <param name="sender"></param>
 127:         /// <param name="e"></param>
 128:         private void textTimeLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
 129:         {
 130:             this.Time = ParseText(this.textTime.Text);
 131:         } 
 132:         #endregion
 133:  
 134:         private DateTime ParseText(string value)
 135:         {
 136:             //empty...move on
 137:             if (string.IsNullOrWhiteSpace(value))
 138:             {
 139:                 return DateTime.MinValue;
 140:             }
 141:             
 142:             //parsed as valid date
 143:             DateTime date = DateTime.MinValue;
 144:             DateTime.TryParse(value, out date);
 145:             if (!date.IsMinValue())
 146:             {
 147:                 return date;
 148:             }
 149:  
 150:             string input = value;
 151:             int first = int.Parse(value.Substring(0, 1));
 152:             switch (value.Length)
 153:             {
 154:                 case 3:
 155:                     input = "0" + value;
 156:                     if (first > 0 && first < 7)
 157:                     {
 158:                         input = (first + 12).ToString() + value.Substring(1, 2);
 159:                     }
 160:                     break;
 161:                 case 2:
 162:                     input = value + "00";
 163:                     break;
 164:                 case 1:
 165:                     input = "0" + value + "00";
 166:                     if (first > 0 && first < 7)
 167:                     {
 168:                         input = (first + 12).ToString() + "00";
 169:                     }
 170:                     break;
 171:                 default:
 172:                     break;
 173:             }
 174:             DateTime.TryParseExact(input, "HHmm", null, DateTimeStyles.None, out date);
 175:             return date;
 176:         }
 177:  
 178:  
 179:     }
 180: }
 

DateTime Extension Method:

   1: /// <summary>
   2: /// Adds or subtracts a value to a date time rounding it to
   3: /// multiples of the entered value
   4: /// </summary>
   5: /// <param name="dt">this</param>
   6: /// <param name="value">Amount to add or subtract</param>
   7: /// <returns>Adjusted DateTime</returns>
   8: /// <history>
   9: ///     [Tim Hibbard]   04/08/2011  Created
  10: /// </history>
  11: public static DateTime AddMinutesRoundUp(this DateTime dt, int value)
  12: {
  13:     int delta = dt.Minute % value;
  14:     int subtractDeltaFrom = 0;
  15:     //since value to add is negative, we want to round down
  16:     //to nearest delta value
  17:     if (value < 0)
  18:     {
  19:         //but only if delta is more than 0. If delta is 0, 
  20:         //then we are already rounded down
  21:         if (delta > 0)
  22:         {
  23:             subtractDeltaFrom = Math.Abs(value); 
  24:         }
  25:         
  26:     }
  27:     return dt.AddMinutes(subtractDeltaFrom - delta).AddMinutes(value);
  28: }

Posted On Thursday, April 14, 2011 10:16 AM | Feedback (3) | Filed Under [ .NET Controls WPF ]

Powered by: