Geeks With Blogs

News



Add to Google

Tim Hibbard CEO for EnGraph software

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 .NET , Controls , WPF | Back to top


Comments on this post: WPF TimeSpinner Control

# re: WPF TimeSpinner Control
Requesting Gravatar...
This is a very good article! You did a very good job!
Left by y8 on Sep 04, 2011 1:28 AM

# re: WPF TimeSpinner Control
Requesting Gravatar...
I have problems signing on the Live Messenger and the only reason is related to the host file PLSSS I do, if I can reinstall the program
Left by Pacquiao vs Marquez 3 on Sep 29, 2011 12:47 AM

# cubicle curtains
Requesting Gravatar...
Thanks for reading mate. Well, this is my first visit to your blog! But I admire the valuable time and effort you put into it, especially in the interesting articles you share here!
Left by hospital curtains on Oct 17, 2011 9:53 AM

# re: WPF TimeSpinner Control
Requesting Gravatar...
Thanks for this post folk. I have been searching for the details about time spinner controls but as per the steps provided here I couldn’t create it. The codes are little bit confusing for me. Could anyone please help me in this? Regards
Left by androgel pills on May 24, 2013 2:37 AM

Your comment:
 (will show your gravatar)


Copyright © Tim Hibbard | Powered by: GeeksWithBlogs.net