Browse Source

DateTimeUpDown: working on making the control editable (enter values with keyboard). Only have the date editing working. Still working on the time editing.

pull/1645/head
brianlagunas_cp 15 years ago
parent
commit
c4b66dce21
  1. 178
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/DateTimeUpDown/Implementation/DateTimeParser.cs
  2. 130
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/DateTimeUpDown/Implementation/DateTimeUpDown.cs
  3. 7
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/DateTimeUpDown/Themes/Generic.xaml
  4. 1
      ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/WPFToolkit.Extended.csproj

178
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/DateTimeUpDown/Implementation/DateTimeParser.cs

@ -0,0 +1,178 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;
using System.Text.RegularExpressions;
namespace Microsoft.Windows.Controls
{
internal class DateTimeParser
{
#region Properties
private DateTimeFormatInfo DateTimeFormatInfo { get; set; }
public string Format { get; set; }
private IEnumerable<string> MonthNames { get { return DateTimeFormatInfo.AbbreviatedMonthNames.Union(DateTimeFormatInfo.MonthNames); } }
#endregion //Properties
#region Constructors
public DateTimeParser(DateTimeFormatInfo dateTimeFormatInfo)
{
DateTimeFormatInfo = dateTimeFormatInfo;
}
public DateTimeParser(DateTimeFormatInfo dateTimeFormatInfo, string format)
{
DateTimeFormatInfo = dateTimeFormatInfo;
Format = format;
}
#endregion //Constructors
#region Methods
public bool TryParse(string value, out DateTime result, DateTime currentDate)
{
bool success = false;
result = currentDate;
if (string.IsNullOrEmpty(value))
return false;
//parse date
DateTime date;
success = TryParseDate(value, out date, currentDate);
//parse time
DateTime time;
success = TryParseTime(value, out time, currentDate);
//merge the two
result = MergeDateAndTime(date, time);
return success;
}
#region Parse Date
public bool TryParseDate(string value, out DateTime result, DateTime currentDate)
{
bool success = false;
result = currentDate;
if (string.IsNullOrEmpty(value))
return false;
var dateParts = GetDateParts(ResolveDateString(value)).ToArray();
if (dateParts.Length > 0)
{
var dateFormatParts = DateTimeFormatInfo.ShortDatePattern.Split(new string[] { DateTimeFormatInfo.DateSeparator }, StringSplitOptions.RemoveEmptyEntries).ToList();
int yearIndex = dateFormatParts.IndexOf(dateFormatParts.FirstOrDefault(e => e.Contains("y") || e.Contains("Y")));
if (yearIndex >= 0 && yearIndex < dateParts.Length && dateParts[yearIndex].Length <= 2 && !dateParts.Any(dp => dp.Length > 2 && dp != dateParts[yearIndex]))
{
if (dateParts[yearIndex].Length == 0)
{
dateParts[yearIndex] = "00";
}
else if (dateParts[yearIndex].Length == 1)
{
dateParts[yearIndex] = "0" + dateParts[yearIndex];
}
dateParts[yearIndex] = string.Format("{0}{1}", currentDate.Year / 100, dateParts[yearIndex]);
}
success = DateTime.TryParse(string.Join(DateTimeFormatInfo.DateSeparator, dateParts), DateTimeFormatInfo, DateTimeStyles.None, out result);
if (!success)
result = currentDate;
}
return success;
}
private string ResolveDateString(string date)
{
string[] dateParts = new string[3]; // Month/Day/Year
string[] dateSeparators = new string[] { ",", " ", "/", "-", "T" };
var dates = date.Split(dateSeparators, StringSplitOptions.RemoveEmptyEntries).ToList();
var formats = Format.Split(dateSeparators, StringSplitOptions.RemoveEmptyEntries).ToList();
//strip out the date pieces
for (int i = 0; i < formats.Count; i++)
{
var format = formats[i];
if (!format.Equals("dddd") && !format.Contains(DateTimeFormatInfo.AMDesignator) && !format.Contains(DateTimeFormatInfo.PMDesignator))
{
if (format.Contains("M"))
dateParts[0] = dates[i];
else if (format.Contains("d"))
dateParts[1] = dates[i];
else if (format.Contains("y"))
dateParts[2] = dates[i];
}
}
return string.Join(DateTimeFormatInfo.DateSeparator, dateParts);
}
protected List<string> GetDateParts(string date)
{
var months = new Regex(GetDatePattern(), RegexOptions.IgnoreCase);
var dateParts = months.Matches(date)
.OfType<Match>()
.Select(match => match.Value)
.Where(s => !string.IsNullOrEmpty(s))
.ToList();
return dateParts;
}
protected string GetDatePattern()
{
var pattern = new StringBuilder(@"[0-9]+");
foreach (var m in MonthNames.Where(m => !string.IsNullOrEmpty(m)))
{
pattern.AppendFormat(@"|(?<=\b|\W|[0-9_]){0}(?=\b|\W|[0-9_])", m);
}
return pattern.ToString();
}
#endregion //Parse Date
#region Parse Time
public bool TryParseTime(string value, out DateTime result, DateTime fallback)
{
bool success = false;
result = fallback;
if (string.IsNullOrEmpty(value))
return false;
return success;
}
private string ResolveTimeString(string time)
{
return string.Empty;
}
#endregion //Parse Time
public static DateTime MergeDateAndTime(DateTime date, DateTime time)
{
return new DateTime(date.Year, date.Month, date.Day, time.Hour, time.Minute, time.Second, time.Millisecond);
}
#endregion // Methods
}
}

130
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/DateTimeUpDown/Implementation/DateTimeUpDown.cs

@ -4,6 +4,7 @@ using System.Globalization;
using System.Windows;
using System.Windows.Input;
using Microsoft.Windows.Controls.Primitives;
using System.Windows.Data;
namespace Microsoft.Windows.Controls
{
@ -16,6 +17,8 @@ namespace Microsoft.Windows.Controls
private bool _fireSelectionChangedEvent = true;
private bool _isSyncingTextAndValueProperties;
private DateTimeParser _dateParser;
#endregion //Members
#region Properties
@ -83,7 +86,7 @@ namespace Microsoft.Windows.Controls
get { return (DateTime)GetValue(NullValueProperty); }
set { SetValue(NullValueProperty, value); }
}
#endregion //NullValue
@ -162,6 +165,24 @@ namespace Microsoft.Windows.Controls
{
base.OnApplyTemplate();
TextBox.SelectionChanged += TextBox_SelectionChanged;
_dateParser = new DateTimeParser(DateTimeFormatInfo, GetFormatString(Format));
}
protected override void OnIncrement()
{
if (Value.HasValue)
UpdateDateTime(1);
else
Value = NullValue;
}
protected override void OnDecrement()
{
if (Value.HasValue)
UpdateDateTime(-1);
else
Value = NullValue;
}
protected override void OnPreviewKeyDown(KeyEventArgs e)
@ -170,25 +191,18 @@ namespace Microsoft.Windows.Controls
{
case Key.Enter:
{
if (IsEditable)
{
_fireSelectionChangedEvent = false;
BindingExpression binding = BindingOperations.GetBindingExpression(TextBox, System.Windows.Controls.TextBox.TextProperty);
binding.UpdateSource();
_fireSelectionChangedEvent = true;
}
return;
}
case Key.Delete:
{
Value = null;
break;
}
case Key.Left:
{
PerformKeyboardSelection(-1);
break;
}
case Key.Right:
{
PerformKeyboardSelection(1);
break;
}
default:
{
_fireSelectionChangedEvent = false;
break;
}
}
@ -196,13 +210,29 @@ namespace Microsoft.Windows.Controls
base.OnPreviewKeyDown(e);
}
protected override void OnTextChanged(string previousValue, string currentValue)
{
if (String.IsNullOrEmpty(currentValue))
{
Value = null;
return;
}
//TODO: clean this up and make sure it doesn't fire recursively
DateTime current = Value.HasValue ? Value.Value : DateTime.Now;
DateTime result;
var success = _dateParser.TryParse(currentValue, out result, current);
SyncTextAndValueProperties(InputBase.TextProperty, result);
}
#endregion //Base Class Overrides
#region Event Hanlders
void TextBox_SelectionChanged(object sender, RoutedEventArgs e)
{
if (_fireSelectionChangedEvent)
if (_fireSelectionChangedEvent)
PerformMouseSelection();
else
_fireSelectionChangedEvent = true;
@ -223,28 +253,6 @@ namespace Microsoft.Windows.Controls
#region Methods
#region Abstract
protected override void OnIncrement()
{
if (Value.HasValue)
UpdateDateTime(1);
else
Value = NullValue;
}
protected override void OnDecrement()
{
if (Value.HasValue)
UpdateDateTime(-1);
else
Value = NullValue;
}
#endregion //Abstract
#region Private
private void InitializeDateTimeInfoListAndParseValue()
{
InitializeDateTimeInfoList();
@ -268,7 +276,7 @@ namespace Microsoft.Windows.Controls
case '\'':
{
int closingQuotePosition = format.IndexOf(format[0], 1);
info = new DateTimeInfo { IsReadOnly = true, Type = DateTimePart.Other, Length = 1, Content = format.Substring(1, Math.Max(1, closingQuotePosition - 1)).ToString() };
info = new DateTimeInfo { IsReadOnly = true, Type = DateTimePart.Other, Length = 1, Content = format.Substring(1, Math.Max(1, closingQuotePosition - 1)) };
elementLength = Math.Max(1, closingQuotePosition + 1);
break;
}
@ -451,34 +459,6 @@ namespace Microsoft.Windows.Controls
});
}
/// <summary>
/// Performs the keyboard selection.
/// </summary>
/// <param name="direction">The direction.</param>
/// <remarks>-1 = Left, 1 = Right</remarks>
private void PerformKeyboardSelection(int direction)
{
DateTimeInfo info;
int index = _dateTimeInfoList.IndexOf(_selectedDateTimeInfo);
//make sure we stay within the selection ranges
if ((index == 0 && direction == -1) || (index == _dateTimeInfoList.Count - 1 && direction == 1))
return;
//get the DateTimeInfo at the next position
index += direction;
info = _dateTimeInfoList[index];
//we don't care about spaces and commas, only select valid DateTimeInfos
while (info.Type == DateTimePart.Other)
{
info = _dateTimeInfoList[index += direction];
}
//perform selection
Select(info);
}
private void Select(DateTimeInfo info)
{
_fireSelectionChangedEvent = false;
@ -527,7 +507,6 @@ namespace Microsoft.Windows.Controls
if (info == null)
info = _dateTimeInfoList[0];
switch (info.Type)
{
case DateTimePart.Year:
@ -584,14 +563,15 @@ namespace Microsoft.Windows.Controls
_fireSelectionChangedEvent = true;
}
#endregion //Private
protected object ConvertTextToValue(string text)
private DateTime? ConvertTextToValue(string text)
{
throw new NotImplementedException("ConvertTextToValue");
if (string.IsNullOrEmpty(text))
return null;
return DateTime.Parse(text, CultureInfo.CurrentCulture);
}
protected string ConvertValueToText(object value)
private string ConvertValueToText(object value)
{
if (value == null) return string.Empty;
@ -599,7 +579,7 @@ namespace Microsoft.Windows.Controls
return dt.ToString(GetFormatString(Format), CultureInfo.CurrentCulture);
}
protected void SyncTextAndValueProperties(DependencyProperty p, object newValue)
private void SyncTextAndValueProperties(DependencyProperty p, object newValue)
{
//prevents recursive syncing properties
if (_isSyncingTextAndValueProperties)

7
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/DateTimeUpDown/Themes/Generic.xaml

@ -1,6 +1,9 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Microsoft.Windows.Controls">
xmlns:local="clr-namespace:Microsoft.Windows.Controls"
xmlns:coreConverters="clr-namespace:Microsoft.Windows.Controls.Core.Converters">
<coreConverters:InverseBoolConverter x:Key="InverseBoolConverter" />
<DataTemplate x:Key="DefaultWatermarkTemplate">
<ContentControl Content="{Binding}" Foreground="Gray" Focusable="False" />
@ -30,7 +33,7 @@
FontWeight="{TemplateBinding FontWeight}"
Foreground="{TemplateBinding Foreground}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
IsReadOnly="True"
IsReadOnly="{Binding IsEditable, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource InverseBoolConverter}}"
MinWidth="20" AcceptsReturn="False"
TextWrapping="NoWrap"
TabIndex="{TemplateBinding TabIndex}"

1
ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/WPFToolkit.Extended.csproj

@ -176,6 +176,7 @@
<Compile Include="DateTimeUpDown\Implementation\DateTimeInfo.cs" />
<Compile Include="DateTimeUpDown\Implementation\DateTimePart.cs" />
<Compile Include="DateTimeUpDown\Implementation\DateTimeUpDown.cs" />
<Compile Include="DateTimeUpDown\Implementation\DateTimeParser.cs" />
<Compile Include="Magnifier\Implementation\Converters\BorderThicknessToStrokeThicknessConverter.cs" />
<Compile Include="Magnifier\Implementation\Converters\RadiusConverter.cs" />
<Compile Include="Magnifier\Implementation\Magnifier.cs" />

Loading…
Cancel
Save