From cf4a8d4201c31ca669121747c4fa592a66089187 Mon Sep 17 00:00:00 2001 From: brianlagunas_cp Date: Thu, 11 Nov 2010 00:27:25 +0000 Subject: [PATCH] added new DateTimeUpDown control. Aloows you to use spin buttons or the mouse wheel to increment/decrement individual sections of a DateTime value. There are currently 10 supported formats: FullDateTime, LongDate, LongTime, MonthDay, RFC1123, ShortDate, ShortTime, SortableDateTime, UniversalSortableDateTime, and YearMonth. Use the control as follows: You cannot type in the Value yet because I haven't figured that part out yet. --- .../DateTimeUpDown/DateTimeFormat.cs | 18 + .../DateTimeUpDown/DateTimeInfo.cs | 16 + .../DateTimeUpDown/DateTimePart.cs | 22 + .../DateTimeUpDown/DateTimeUpDown.cs | 565 ++++++++++++++++++ .../WPFToolkit.Extended/Themes/Generic.xaml | 26 + .../WPFToolkit.Extended.csproj | 4 + 6 files changed, 651 insertions(+) create mode 100644 ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/DateTimeUpDown/DateTimeFormat.cs create mode 100644 ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/DateTimeUpDown/DateTimeInfo.cs create mode 100644 ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/DateTimeUpDown/DateTimePart.cs create mode 100644 ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/DateTimeUpDown/DateTimeUpDown.cs diff --git a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/DateTimeUpDown/DateTimeFormat.cs b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/DateTimeUpDown/DateTimeFormat.cs new file mode 100644 index 00000000..64591695 --- /dev/null +++ b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/DateTimeUpDown/DateTimeFormat.cs @@ -0,0 +1,18 @@ +using System; + +namespace Microsoft.Windows.Controls +{ + public enum DateTimeFormat + { + FullDateTime, + LongDate, + LongTime, + MonthDay, + RFC1123, + ShortDate, + ShortTime, + SortableDateTime, + UniversalSortableDateTime, + YearMonth + } +} diff --git a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/DateTimeUpDown/DateTimeInfo.cs b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/DateTimeUpDown/DateTimeInfo.cs new file mode 100644 index 00000000..dab48148 --- /dev/null +++ b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/DateTimeUpDown/DateTimeInfo.cs @@ -0,0 +1,16 @@ +using System; + +namespace Microsoft.Windows.Controls +{ + internal class DateTimeInfo + { + public string Content { get; set; } + public bool? IsReadOnly { get; set; } + public int KeyPressCount { get; set; } + public int Length { get; set; } + public string MonthName { get; set; } + public string Pattern { get; set; } + public int StartPosition { get; set; } + public DateTimePart Type { get; set; } + } +} \ No newline at end of file diff --git a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/DateTimeUpDown/DateTimePart.cs b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/DateTimeUpDown/DateTimePart.cs new file mode 100644 index 00000000..d4ec352c --- /dev/null +++ b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/DateTimeUpDown/DateTimePart.cs @@ -0,0 +1,22 @@ +using System; + +namespace Microsoft.Windows.Controls +{ + public enum DateTimePart + { + Day, + DayName, + AmPmDesignator, + Millisecond, + Hour12, + Hour24, + Minute, + Month, + MonthName, + Other, + Period, + TimeZone, + Second, + Year + } +} diff --git a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/DateTimeUpDown/DateTimeUpDown.cs b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/DateTimeUpDown/DateTimeUpDown.cs new file mode 100644 index 00000000..3696dcd1 --- /dev/null +++ b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/DateTimeUpDown/DateTimeUpDown.cs @@ -0,0 +1,565 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Windows; + +namespace Microsoft.Windows.Controls +{ + public class DateTimeUpDown : UpDownBase + { + #region Members + + private List _dateTimeInfoList = new List(); + internal bool _selectionChanged = true; + internal int _selectedDateTimeInfoIndex; + + #endregion //Members + + #region Properties + + private DateTimeFormatInfo DateTimeFormatInfo { get; set; } + + #region Format + + public static readonly DependencyProperty FormatProperty = DependencyProperty.Register("Format", typeof(DateTimeFormat), typeof(DateTimeUpDown), new UIPropertyMetadata(DateTimeFormat.ShortTime, OnFormatChanged)); + public DateTimeFormat Format + { + get { return (DateTimeFormat)GetValue(FormatProperty); } + set { SetValue(FormatProperty, value); } + } + + private static void OnFormatChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) + { + DateTimeUpDown dateTimeUpDown = o as DateTimeUpDown; + if (dateTimeUpDown != null) + dateTimeUpDown.OnFormatChanged((DateTimeFormat)e.OldValue, (DateTimeFormat)e.NewValue); + } + + protected virtual void OnFormatChanged(DateTimeFormat oldValue, DateTimeFormat newValue) + { + InitializeDateTimeInfoList(); + } + + #endregion //Format + + #endregion //Properties + + #region Constructors + + static DateTimeUpDown() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(DateTimeUpDown), new FrameworkPropertyMetadata(typeof(DateTimeUpDown))); + ValueTypeProperty.OverrideMetadata(typeof(DateTimeUpDown), new FrameworkPropertyMetadata(typeof(Nullable))); + } + + public DateTimeUpDown() + { + DateTimeFormatInfo = DateTimeFormatInfo.GetInstance(CultureInfo.CurrentCulture); + } + + protected override void OnValueChanged(object oldValue, object newValue) + { + //whenever the value changes we need to parse out the value into out DateTimeInfo segments so we can keep track of the individual pieces + ParseValueIntoDateTimeInfo(); + } + + #endregion //Constructors + + #region Base Class Overrides + + public override void OnApplyTemplate() + { + base.OnApplyTemplate(); + TextBox.SelectionChanged += new RoutedEventHandler(TextBox_SelectionChanged); + } + + #endregion //Base Class Overrides + + #region Event Hanlders + + void TextBox_SelectionChanged(object sender, RoutedEventArgs e) + { + if (_selectionChanged) + SelectDateTimePart(); + else + _selectionChanged = true; + } + + #endregion //Event Hanlders + + #region Methods + + private void InitializeDateTimeInfoList() + { + string format = GetFormatString(Format); + while (format.Length > 0) + { + int elementLength = GetGroupLengthByMask(format); + + switch (format[0]) + { + case '"': + case '\'': + { + int closingQuotePosition = format.IndexOf(format[0], 1); + _dateTimeInfoList.Add(new DateTimeInfo() + { + IsReadOnly = true, + Type = DateTimePart.Other, + Length = 1, + Content = format.Substring(1, Math.Max(1, closingQuotePosition - 1)).ToString(), + }); + elementLength = Math.Max(1, closingQuotePosition + 1); + break; + } + + case 'D': + case 'd': + { + string d = format.Substring(0, elementLength); + if (elementLength == 1) + d = "%" + d; + + if (elementLength > 2) + { + _dateTimeInfoList.Add(new DateTimeInfo() + { + IsReadOnly = true, + Type = DateTimePart.DayName, + Pattern = d + }); + } + else + { + _dateTimeInfoList.Add(new DateTimeInfo() + { + IsReadOnly = false, + Type = DateTimePart.Day, + Pattern = d + }); + } + break; + } + case 'F': + case 'f': + { + string f = format.Substring(0, elementLength); + if (elementLength == 1) + f = "%" + f; + + _dateTimeInfoList.Add(new DateTimeInfo() + { + IsReadOnly = false, + Type = DateTimePart.Millisecond, + Pattern = f + }); + break; + } + case 'h': + { + string h = format.Substring(0, elementLength); + if (elementLength == 1) + h = "%" + h; + + _dateTimeInfoList.Add(new DateTimeInfo() + { + IsReadOnly = false, + Type = DateTimePart.Hour12, + Pattern = h + }); + break; + } + case 'H': + { + string H = format.Substring(0, elementLength); + if (elementLength == 1) + H = "%" + H; + + _dateTimeInfoList.Add(new DateTimeInfo() + { + IsReadOnly = false, + Type = DateTimePart.Hour24, + Pattern = H + }); + break; + } + case 'M': + { + string M = format.Substring(0, elementLength); + if (elementLength == 1) + M = "%" + M; + + if (elementLength >= 3) + { + _dateTimeInfoList.Add(new DateTimeInfo() + { + IsReadOnly = false, + Type = DateTimePart.MonthName, + Pattern = M + }); + } + else + { + _dateTimeInfoList.Add(new DateTimeInfo() + { + IsReadOnly = false, + Type = DateTimePart.Month, + Pattern = M + }); + } + break; + } + case 'S': + case 's': + { + string s = format.Substring(0, elementLength); + if (elementLength == 1) + s = "%" + s; + _dateTimeInfoList.Add(new DateTimeInfo() + { + IsReadOnly = false, + Type = DateTimePart.Second, + Pattern = s + }); + break; + } + case 'T': + case 't': + { + string t = format.Substring(0, elementLength); + if (elementLength == 1) + { + t = "%" + t; + } + _dateTimeInfoList.Add(new DateTimeInfo() + { + IsReadOnly = false, + Type = DateTimePart.AmPmDesignator, + Pattern = t + }); + break; + } + case 'Y': + case 'y': + { + string y = format.Substring(0, elementLength); + if (elementLength == 1) + { + y = "%" + y; + } + _dateTimeInfoList.Add(new DateTimeInfo() + { + IsReadOnly = false, + Type = DateTimePart.Year, + Pattern = y + }); + break; + } + case '\\': + { + if (format.Length >= 2) + { + _dateTimeInfoList.Add(new DateTimeInfo() + { + IsReadOnly = true, + Content = format.Substring(1, 1), + Length = 1, + Type = DateTimePart.Other + }); + elementLength = 2; + } + break; + } + case 'g': + { + string g = format.Substring(0, elementLength); + if (elementLength == 1) + { + g = "%" + g; + } + _dateTimeInfoList.Add(new DateTimeInfo() + { + IsReadOnly = true, + Type = DateTimePart.Period, + Pattern = format.Substring(0, elementLength) + }); + break; + } + case 'm': + { + string m = format.Substring(0, elementLength); + if (elementLength == 1) + { + m = "%" + m; + } + _dateTimeInfoList.Add(new DateTimeInfo() + { + IsReadOnly = false, + Type = DateTimePart.Minute, + Pattern = m + }); + break; + } + case 'z': + { + string z = format.Substring(0, elementLength); + if (elementLength == 1) + { + z = "%" + z; + } + _dateTimeInfoList.Add(new DateTimeInfo() + { + IsReadOnly = true, + Type = DateTimePart.TimeZone, + Pattern = z + }); + break; + } + default: + { + elementLength = 1; + _dateTimeInfoList.Add(new DateTimeInfo() + { + IsReadOnly = true, + Length = 1, + Content = format[0].ToString(), + Type = DateTimePart.Other + }); + break; + } + } + + format = format.Substring(elementLength); + } + } + + private static int GetGroupLengthByMask(string mask) + { + for (int i = 1; i < mask.Length; i++) + { + if (String.Compare(mask[i].ToString(), mask[0].ToString(), false) != 0) + { + return i; + } + } + return mask.Length; + } + + public void ParseValueIntoDateTimeInfo() + { + string displayText = string.Empty; + + for (int i = 0; i < _dateTimeInfoList.Count; i++) + { + var charProperty = _dateTimeInfoList[i]; + + if (charProperty.Pattern == null) + { + _dateTimeInfoList[i].StartPosition = displayText.Length; + _dateTimeInfoList[i].Length = charProperty.Content.Length; + displayText += charProperty.Content; + } + else + { + DateTime date = DateTime.Parse(Value.ToString()); + + _dateTimeInfoList[i].StartPosition = displayText.Length; + + charProperty.Content = date.ToString(charProperty.Pattern, DateTimeFormatInfo); + + _dateTimeInfoList[i].Length = charProperty.Content.Length; + + displayText += charProperty.Content; + } + } + } + + private void SelectDateTimePart() + { + for (int i = 0; i < _dateTimeInfoList.Count; i++) + { + var charProperty = _dateTimeInfoList[i]; + + if ((charProperty.StartPosition <= TextBox.SelectionStart) && (TextBox.SelectionStart < (charProperty.StartPosition + charProperty.Length))) + { + if (charProperty.IsReadOnly == false) + { + _selectionChanged = false; + + for (int j = 0; j < _dateTimeInfoList.Count; j++) + { + _dateTimeInfoList[j].KeyPressCount = 0; + } + + TextBox.Select(charProperty.StartPosition, charProperty.Length); + _selectionChanged = true; + _selectedDateTimeInfoIndex = i; + + return; + } + else + { + _selectionChanged = false; + + for (int j = 0; j < _dateTimeInfoList.Count; j++) + { + _dateTimeInfoList[j].KeyPressCount = 0; + } + + TextBox.Select(charProperty.StartPosition, charProperty.Length); + _selectionChanged = true; + _selectedDateTimeInfoIndex = i; + return; + } + } + } + _selectionChanged = false; + TextBox.Select(TextBox.SelectionStart, 0); + _selectionChanged = true; + _selectedDateTimeInfoIndex = -1; + } + + private string GetFormatString(DateTimeFormat dateTimeFormat) + { + switch (dateTimeFormat) + { + case DateTimeFormat.ShortDate: + return DateTimeFormatInfo.ShortDatePattern; + case DateTimeFormat.LongDate: + return DateTimeFormatInfo.LongDatePattern; + case DateTimeFormat.ShortTime: + return DateTimeFormatInfo.ShortTimePattern; + case DateTimeFormat.LongTime: + return DateTimeFormatInfo.LongTimePattern; + case DateTimeFormat.FullDateTime: + return DateTimeFormatInfo.FullDateTimePattern; + case DateTimeFormat.MonthDay: + return DateTimeFormatInfo.MonthDayPattern; + case DateTimeFormat.RFC1123: + return DateTimeFormatInfo.RFC1123Pattern; + case DateTimeFormat.SortableDateTime: + return DateTimeFormatInfo.SortableDateTimePattern; + case DateTimeFormat.UniversalSortableDateTime: + return DateTimeFormatInfo.UniversalSortableDateTimePattern; + case DateTimeFormat.YearMonth: + return DateTimeFormatInfo.YearMonthPattern; + default: + throw new ArgumentException("Not a supported format"); + } + } + + #region Abstract + + protected override void OnIncrement() + { + DateTimeInfo info = _dateTimeInfoList[_selectedDateTimeInfoIndex]; + + _selectionChanged = false; + + //TODO: refactor + if (info.Type == DateTimePart.Year) + { + Value = ((DateTime)Value).AddYears(1); + } + else if (info.Type == DateTimePart.Month || info.Type == DateTimePart.MonthName) + { + Value = ((DateTime)Value).AddMonths(1); + } + else if (info.Type == DateTimePart.Day || info.Type == DateTimePart.DayName) + { + Value = ((DateTime)Value).AddDays(1); + } + else if (info.Type == DateTimePart.Hour12 || info.Type == DateTimePart.Hour24) + { + Value = ((DateTime)Value).AddHours(1); + } + else if (info.Type == DateTimePart.Minute) + { + Value = ((DateTime)Value).AddMinutes(1); + } + else if (info.Type == DateTimePart.Second) + { + Value = ((DateTime)Value).AddSeconds(1); + } + else if (info.Type == DateTimePart.Millisecond) + { + Value = ((DateTime)Value).AddMilliseconds(1); + } + else if (info.Type == DateTimePart.AmPmDesignator) + { + Value = ((DateTime)Value).AddHours(12); + } + + //we loose our selection when the Value is set so we need to reselect it without firing the selection changed event + TextBox.Select(info.StartPosition, info.Length); + + _selectionChanged = true; + } + + protected override void OnDecrement() + { + DateTimeInfo info = _dateTimeInfoList[_selectedDateTimeInfoIndex]; + + _selectionChanged = false; + + //TODO: refactor + if (info.Type == DateTimePart.Year) + { + Value = ((DateTime)Value).AddYears(-1); + } + else if (info.Type == DateTimePart.Month || info.Type == DateTimePart.MonthName) + { + Value = ((DateTime)Value).AddMonths(-1); + } + else if (info.Type == DateTimePart.Day || info.Type == DateTimePart.DayName) + { + Value = ((DateTime)Value).AddDays(-1); + } + else if (info.Type == DateTimePart.Hour12 || info.Type == DateTimePart.Hour24) + { + Value = ((DateTime)Value).AddHours(-1); + } + else if (info.Type == DateTimePart.Minute) + { + Value = ((DateTime)Value).AddMinutes(-1); + } + else if (info.Type == DateTimePart.Second) + { + Value = ((DateTime)Value).AddSeconds(-1); + } + else if (info.Type == DateTimePart.Millisecond) + { + Value = ((DateTime)Value).AddMilliseconds(-1); + } + else if (info.Type == DateTimePart.AmPmDesignator) + { + Value = ((DateTime)Value).AddHours(-12); + } + + //we loose our selection when the Value is set so we need to reselect it without firing the selection changed event + TextBox.Select(info.StartPosition, info.Length); + + _selectionChanged = true; + } + + protected override object ConvertTextToValue(string text) + { + return text; + } + + protected override string ConvertValueToText(object value) + { + if (value == null) return string.Empty; + + DateTime? dt = DateTime.Parse(value.ToString(), CultureInfo.CurrentCulture); + + if (dt.HasValue) + return dt.Value.ToString(GetFormatString(Format), CultureInfo.CurrentCulture); + else + return string.Empty; + } + + #endregion //Abstract + #endregion //Methods + } +} diff --git a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/Themes/Generic.xaml b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/Themes/Generic.xaml index 37351fc4..fe7cb49a 100644 --- a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/Themes/Generic.xaml +++ b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/Themes/Generic.xaml @@ -1111,6 +1111,32 @@ + + + + + + diff --git a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/WPFToolkit.Extended.csproj b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/WPFToolkit.Extended.csproj index 2441b591..c254d8e1 100644 --- a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/WPFToolkit.Extended.csproj +++ b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/WPFToolkit.Extended.csproj @@ -77,6 +77,10 @@ + + + +