Browse Source
<extToolkit:DateTimeUpDown Value="{Binding LastUpdated}" Format="FullDateTime" />
You cannot type in the Value yet because I haven't figured that part out yet.
pull/1645/head
6 changed files with 651 additions and 0 deletions
@ -0,0 +1,18 @@ |
|||
using System; |
|||
|
|||
namespace Microsoft.Windows.Controls |
|||
{ |
|||
public enum DateTimeFormat |
|||
{ |
|||
FullDateTime, |
|||
LongDate, |
|||
LongTime, |
|||
MonthDay, |
|||
RFC1123, |
|||
ShortDate, |
|||
ShortTime, |
|||
SortableDateTime, |
|||
UniversalSortableDateTime, |
|||
YearMonth |
|||
} |
|||
} |
|||
@ -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; } |
|||
} |
|||
} |
|||
@ -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 |
|||
} |
|||
} |
|||
@ -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<DateTimeInfo> _dateTimeInfoList = new List<DateTimeInfo>(); |
|||
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<DateTime>))); |
|||
} |
|||
|
|||
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
|
|||
} |
|||
} |
|||
Loading…
Reference in new issue