Adjust project structure

This commit is contained in:
NaBian committed 2019-04-05 22:23:19 +08:00
1 parent d38912f5da
commit e3c436f939
2111 files changed
+14028 -47199

No files matched your search

@@ -0,0 +1,112 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using HandyControl.Data;
using HandyControl.Interactivity;
namespace HandyControl.Controls
{
/// <inheritdoc cref="IDataInput" />
public class ComboBox : System.Windows.Controls.ComboBox, IDataInput
{
public ComboBox()
{
CommandBindings.Add(new CommandBinding(ControlCommands.Clear, (s, e) =>
{
ClearValue(SelectedValueProperty);
ClearValue(SelectedItemProperty);
ClearValue(SelectedIndexProperty);
ClearValue(TextProperty);
}));
}
protected override void OnSelectionChanged(SelectionChangedEventArgs e)
{
base.OnSelectionChanged(e);
VerifyData();
}
public Func<string, OperationResult<bool>> VerifyFunc { get; set; }
/// <summary>
/// 数据是否错误
/// </summary>
public static readonly DependencyProperty IsErrorProperty = DependencyProperty.Register(
"IsError", typeof(bool), typeof(ComboBox), new PropertyMetadata(ValueBoxes.FalseBox));
public bool IsError
{
get => (bool) GetValue(IsErrorProperty);
set => SetValue(IsErrorProperty, value);
}
/// <summary>
/// 错误提示
/// </summary>
public static readonly DependencyProperty ErrorStrProperty = DependencyProperty.Register(
"ErrorStr", typeof(string), typeof(ComboBox), new PropertyMetadata(default(string)));
public string ErrorStr
{
get => (string) GetValue(ErrorStrProperty);
set => SetValue(ErrorStrProperty, value);
}
public static readonly DependencyPropertyKey TextTypePropertyKey =
DependencyProperty.RegisterReadOnly("TextType", typeof(TextType), typeof(ComboBox),
new PropertyMetadata(default(TextType)));
/// <summary>
/// 文本类型
/// </summary>
public static readonly DependencyProperty TextTypeProperty = TextTypePropertyKey.DependencyProperty;
public TextType TextType
{
get => (TextType) GetValue(TextTypeProperty);
set => SetValue(TextTypeProperty, value);
}
/// <summary>
/// 是否显示清除按钮
/// </summary>
public static readonly DependencyProperty ShowClearButtonProperty = DependencyProperty.Register(
"ShowClearButton", typeof(bool), typeof(ComboBox), new PropertyMetadata(ValueBoxes.FalseBox));
public bool ShowClearButton
{
get => (bool) GetValue(ShowClearButtonProperty);
set => SetValue(ShowClearButtonProperty, value);
}
public virtual bool VerifyData()
{
OperationResult<bool> result;
if (VerifyFunc != null)
{
result = VerifyFunc.Invoke(Text);
}
else
{
if (!string.IsNullOrEmpty(Text))
{
result = OperationResult.Success();
}
else if (InfoElement.GetNecessary(this))
{
result = OperationResult.Failed(Properties.Langs.Lang.IsNecessary);
}
else
{
result = OperationResult.Success();
}
}
IsError = !result.Data;
ErrorStr = result.Message;
return result.Data;
}
}
}
@@ -0,0 +1,113 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using HandyControl.Data;
using HandyControl.Interactivity;
namespace HandyControl.Controls
{
/// <inheritdoc cref="IDataInput" />
[TemplatePart(Name = ElementTextBox, Type = typeof(DatePickerTextBox))]
public class DatePicker : System.Windows.Controls.DatePicker, IDataInput
{
private const string ElementTextBox = "PART_TextBox";
private System.Windows.Controls.TextBox _textBox;
public DatePicker()
{
CommandBindings.Add(new CommandBinding(ControlCommands.Clear, (s, e) =>
{
ClearValue(SelectedDateProperty);
ClearValue(TextProperty);
}));
}
public override void OnApplyTemplate()
{
if (_textBox != null)
{
_textBox.TextChanged -= TextBox_TextChanged;
}
base.OnApplyTemplate();
_textBox = GetTemplateChild(ElementTextBox) as System.Windows.Controls.TextBox;
if (_textBox != null)
{
_textBox.TextChanged += TextBox_TextChanged;
}
}
private void TextBox_TextChanged(object sender, TextChangedEventArgs e) => VerifyData();
public Func<string, OperationResult<bool>> VerifyFunc { get; set; }
public static readonly DependencyProperty IsErrorProperty = DependencyProperty.Register(
"IsError", typeof(bool), typeof(DatePicker), new PropertyMetadata(ValueBoxes.FalseBox));
public bool IsError
{
get => (bool) GetValue(IsErrorProperty);
set => SetValue(IsErrorProperty, value);
}
public static readonly DependencyProperty ErrorStrProperty = DependencyProperty.Register(
"ErrorStr", typeof(string), typeof(DatePicker), new PropertyMetadata(default(string)));
public string ErrorStr
{
get => (string) GetValue(ErrorStrProperty);
set => SetValue(ErrorStrProperty, value);
}
public static readonly DependencyProperty TextTypeProperty = DependencyProperty.Register(
"TextType", typeof(TextType), typeof(DatePicker), new PropertyMetadata(default(TextType)));
public TextType TextType
{
get => (TextType) GetValue(TextTypeProperty);
set => SetValue(TextTypeProperty, value);
}
public static readonly DependencyProperty ShowClearButtonProperty = DependencyProperty.Register(
"ShowClearButton", typeof(bool), typeof(DatePicker), new PropertyMetadata(ValueBoxes.FalseBox));
public bool ShowClearButton
{
get => (bool) GetValue(ShowClearButtonProperty);
set => SetValue(ShowClearButtonProperty, value);
}
public virtual bool VerifyData()
{
OperationResult<bool> result;
if (VerifyFunc != null)
{
result = VerifyFunc.Invoke(Text);
}
else
{
if (!string.IsNullOrEmpty(Text))
{
result = OperationResult.Success();
}
else if (InfoElement.GetNecessary(this))
{
result = OperationResult.Failed(Properties.Langs.Lang.IsNecessary);
}
else
{
result = OperationResult.Success();
}
}
IsError = !result.Data;
ErrorStr = result.Message;
return result.Data;
}
}
}
@@ -0,0 +1,690 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Threading;
using HandyControl.Data;
using HandyControl.Interactivity;
namespace HandyControl.Controls
{
/// <summary>
/// 时间日期选择器
/// </summary>
[TemplatePart(Name = ElementRoot, Type = typeof(Grid))]
[TemplatePart(Name = ElementTextBox, Type = typeof(WatermarkTextBox))]
[TemplatePart(Name = ElementButton, Type = typeof(Button))]
[TemplatePart(Name = ElementPopup, Type = typeof(Popup))]
public class DateTimePicker : Control, IDataInput
{
#region Constants
private const string ElementRoot = "PART_Root";
private const string ElementTextBox = "PART_TextBox";
private const string ElementButton = "PART_Button";
private const string ElementPopup = "PART_Popup";
#endregion Constants
#region Data
private CalendarWithClock _calendarWithClock;
private string _defaultText;
private ButtonBase _dropDownButton;
private Popup _popUp;
private bool _disablePopupReopen;
private WatermarkTextBox _textBox;
private IDictionary<DependencyProperty, bool> _isHandlerSuspended;
private DateTime? _originalSelectedDateTime;
#endregion Data
#region Public Events
public static readonly RoutedEvent SelectedDateTimeChangedEvent =
EventManager.RegisterRoutedEvent("SelectedDateTimeChanged", RoutingStrategy.Direct,
typeof(EventHandler<FunctionEventArgs<DateTime?>>), typeof(DateTimePicker));
public event EventHandler<FunctionEventArgs<DateTime?>> SelectedDateTimeChanged
{
add => AddHandler(SelectedDateTimeChangedEvent, value);
remove => RemoveHandler(SelectedDateTimeChangedEvent, value);
}
public event RoutedEventHandler PickerClosed;
public event RoutedEventHandler PickerOpened;
#endregion Public Events
static DateTimePicker()
{
EventManager.RegisterClassHandler(typeof(DateTimePicker), GotFocusEvent, new RoutedEventHandler(OnGotFocus));
KeyboardNavigation.TabNavigationProperty.OverrideMetadata(typeof(DateTimePicker), new FrameworkPropertyMetadata(KeyboardNavigationMode.Once));
KeyboardNavigation.IsTabStopProperty.OverrideMetadata(typeof(DateTimePicker), new FrameworkPropertyMetadata(false));
}
public DateTimePicker()
{
InitCalendarWithClock();
CommandBindings.Add(new CommandBinding(ControlCommands.Clear, (s, e) =>
{
ClearValue(SelectedDateTimeProperty);
ClearValue(TextProperty);
_textBox.Text = string.Empty;
}));
}
#region Public Properties
public static readonly DependencyProperty DateTimeFormatProperty = DependencyProperty.Register(
"DateTimeFormat", typeof(string), typeof(DateTimePicker), new PropertyMetadata("yyyy-MM-dd HH:mm:ss"));
public string DateTimeFormat
{
get => (string) GetValue(DateTimeFormatProperty);
set => SetValue(DateTimeFormatProperty, value);
}
public static readonly DependencyProperty ClockStyleProperty = DependencyProperty.Register(
"ClockStyle", typeof(Style), typeof(DateTimePicker), new PropertyMetadata(default(Style)));
public Style ClockStyle
{
get => (Style) GetValue(ClockStyleProperty);
set => SetValue(ClockStyleProperty, value);
}
public static readonly DependencyProperty CalendarStyleProperty = DependencyProperty.Register(
"CalendarStyle", typeof(Style), typeof(DateTimePicker), new PropertyMetadata(default(Style)));
public Style CalendarStyle
{
get => (Style) GetValue(CalendarStyleProperty);
set => SetValue(CalendarStyleProperty, value);
}
public static readonly DependencyProperty DisplayDateTimeProperty = DependencyProperty.Register(
"DisplayDateTime", typeof(DateTime), typeof(DateTimePicker), new FrameworkPropertyMetadata(DateTime.Now, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, null, CoerceDisplayDateTime));
private static object CoerceDisplayDateTime(DependencyObject d, object value)
{
var dp = (DateTimePicker)d;
dp._calendarWithClock.DisplayDateTime = (DateTime)value;
return dp._calendarWithClock.DisplayDateTime;
}
public DateTime DisplayDateTime
{
get => (DateTime) GetValue(DisplayDateTimeProperty);
set => SetValue(DisplayDateTimeProperty, value);
}
public static readonly DependencyProperty IsDropDownOpenProperty = DependencyProperty.Register(
"IsDropDownOpen", typeof(bool), typeof(DateTimePicker), new FrameworkPropertyMetadata(ValueBoxes.FalseBox, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnIsDropDownOpenChanged, OnCoerceIsDropDownOpen));
private static object OnCoerceIsDropDownOpen(DependencyObject d, object baseValue) => d is DateTimePicker dp && !dp.IsEnabled ? false : baseValue;
private static void OnIsDropDownOpenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var dp = d as DateTimePicker;
var newValue = (bool)e.NewValue;
if (dp?._popUp != null && dp._popUp.IsOpen != newValue)
{
dp._popUp.IsOpen = newValue;
if (newValue)
{
dp._originalSelectedDateTime = dp.SelectedDateTime;
dp.Dispatcher.BeginInvoke(DispatcherPriority.Input, (Action)delegate
{
dp._calendarWithClock.Focus();
});
}
}
}
public bool IsDropDownOpen
{
get => (bool) GetValue(IsDropDownOpenProperty);
set => SetValue(IsDropDownOpenProperty, value);
}
public static readonly DependencyProperty SelectedDateTimeProperty = DependencyProperty.Register(
"SelectedDateTime", typeof(DateTime?), typeof(DateTimePicker), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnSelectedDateTimeChanged, CoerceSelectedDateTime));
private static object CoerceSelectedDateTime(DependencyObject d, object value)
{
var dp = (DateTimePicker)d;
dp._calendarWithClock.SelectedDateTime = (DateTime?)value;
return dp._calendarWithClock.SelectedDateTime;
}
private static void OnSelectedDateTimeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (!(d is DateTimePicker dp)) return;
if (dp.SelectedDateTime.HasValue)
{
var time = dp.SelectedDateTime.Value;
dp.SetTextInternal(dp.DateTimeToString(time));
}
dp.RaiseEvent(new FunctionEventArgs<DateTime?>(SelectedDateTimeChangedEvent, dp)
{
Info = dp.SelectedDateTime
});
}
public DateTime? SelectedDateTime
{
get => (DateTime?) GetValue(SelectedDateTimeProperty);
set => SetValue(SelectedDateTimeProperty, value);
}
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
"Text", typeof(string), typeof(DateTimePicker), new FrameworkPropertyMetadata(string.Empty, OnTextChanged));
private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is DateTimePicker dp && !dp.IsHandlerSuspended(TextProperty))
{
if (e.NewValue is string newValue)
{
if (dp._textBox != null)
{
dp._textBox.Text = newValue;
}
else
{
dp._defaultText = newValue;
}
dp.SetSelectedDateTime();
}
else
{
dp.SetValueNoCallback(SelectedDateTimeProperty, null);
}
}
}
public string Text
{
get => (string) GetValue(TextProperty);
set => SetValue(TextProperty, value);
}
/// <summary>
/// Sets the local Text property without breaking bindings
/// </summary>
/// <param name="value"></param>
private void SetTextInternal(string value)
{
SetCurrentValue(TextProperty, value);
}
public Func<string, OperationResult<bool>> VerifyFunc { get; set; }
public static readonly DependencyProperty IsErrorProperty = DependencyProperty.Register(
"IsError", typeof(bool), typeof(DateTimePicker), new PropertyMetadata(ValueBoxes.FalseBox));
public bool IsError
{
get => (bool) GetValue(IsErrorProperty);
set => SetValue(IsErrorProperty, value);
}
public static readonly DependencyProperty ErrorStrProperty = DependencyProperty.Register(
"ErrorStr", typeof(string), typeof(DateTimePicker), new PropertyMetadata(default(string)));
public string ErrorStr
{
get => (string) GetValue(ErrorStrProperty);
set => SetValue(ErrorStrProperty, value);
}
public static readonly DependencyProperty TextTypeProperty = DependencyProperty.Register(
"TextType", typeof(TextType), typeof(DateTimePicker), new PropertyMetadata(default(TextType)));
public TextType TextType
{
get => (TextType) GetValue(TextTypeProperty);
set => SetValue(TextTypeProperty, value);
}
public static readonly DependencyProperty ShowClearButtonProperty = DependencyProperty.Register(
"ShowClearButton", typeof(bool), typeof(DateTimePicker), new PropertyMetadata(ValueBoxes.FalseBox));
public bool ShowClearButton
{
get => (bool) GetValue(ShowClearButtonProperty);
set => SetValue(ShowClearButtonProperty, value);
}
#endregion
#region Public Methods
public override void OnApplyTemplate()
{
if (DesignerProperties.GetIsInDesignMode(this)) return;
if (_popUp != null)
{
_popUp.PreviewMouseLeftButtonDown -= PopUp_PreviewMouseLeftButtonDown;
_popUp.Opened -= PopUp_Opened;
_popUp.Closed -= PopUp_Closed;
_popUp.Child = null;
}
if (_dropDownButton != null)
{
_dropDownButton.Click -= DropDownButton_Click;
_dropDownButton.MouseLeave -= DropDownButton_MouseLeave;
}
if (_textBox != null)
{
_textBox.KeyDown -= TextBox_KeyDown;
_textBox.TextChanged -= TextBox_TextChanged;
_textBox.LostFocus -= TextBox_LostFocus;
}
base.OnApplyTemplate();
_popUp = GetTemplateChild(ElementPopup) as Popup;
_dropDownButton = GetTemplateChild(ElementButton) as Button;
_textBox = GetTemplateChild(ElementTextBox) as WatermarkTextBox;
CheckNull();
_popUp.PreviewMouseLeftButtonDown += PopUp_PreviewMouseLeftButtonDown;
_popUp.Opened += PopUp_Opened;
_popUp.Closed += PopUp_Closed;
_popUp.Child = _calendarWithClock;
if (IsDropDownOpen)
{
_popUp.IsOpen = true;
}
_dropDownButton.Click += DropDownButton_Click;
_dropDownButton.MouseLeave += DropDownButton_MouseLeave;
if (SelectedDateTime == null)
{
_textBox.Text = DateTime.Now.ToString(DateTimeFormat);
}
_textBox.KeyDown += TextBox_KeyDown;
_textBox.TextChanged += TextBox_TextChanged;
_textBox.LostFocus += TextBox_LostFocus;
if (SelectedDateTime == null)
{
if (!string.IsNullOrEmpty(_defaultText))
{
_textBox.Text = _defaultText;
SetSelectedDateTime();
}
}
else
{
_textBox.Text = DateTimeToString(SelectedDateTime.Value);
}
if (_originalSelectedDateTime == null)
{
_originalSelectedDateTime = DateTime.Now;
}
SetCurrentValue(DisplayDateTimeProperty, _originalSelectedDateTime);
}
public virtual bool VerifyData()
{
OperationResult<bool> result;
if (VerifyFunc != null)
{
result = VerifyFunc.Invoke(Text);
}
else
{
if (!string.IsNullOrEmpty(Text))
{
result = OperationResult.Success();
}
else if (InfoElement.GetNecessary(this))
{
result = OperationResult.Failed(Properties.Langs.Lang.IsNecessary);
}
else
{
result = OperationResult.Success();
}
}
IsError = !result.Data;
ErrorStr = result.Message;
return result.Data;
}
public override string ToString() => SelectedDateTime?.ToString(DateTimeFormat) ?? string.Empty;
#endregion
#region Protected Methods
protected virtual void OnPickerClosed(RoutedEventArgs e)
{
var handler = PickerClosed;
handler?.Invoke(this, e);
}
protected virtual void OnPickerOpened(RoutedEventArgs e)
{
var handler = PickerOpened;
handler?.Invoke(this, e);
}
#endregion Protected Methods
#region Private Methods
private void CheckNull()
{
if (_dropDownButton == null || _popUp == null || _textBox == null)
throw new Exception();
}
private void InitCalendarWithClock()
{
_calendarWithClock = new CalendarWithClock
{
ShowConfirmButton = true
};
_calendarWithClock.SelectedDateTimeChanged += CalendarWithClock_SelectedDateTimeChanged;
_calendarWithClock.Confirmed += CalendarWithClock_Confirmed;
}
private void CalendarWithClock_Confirmed() => TogglePopUp();
private void CalendarWithClock_SelectedDateTimeChanged(object sender, FunctionEventArgs<DateTime?> e) => SelectedDateTime = e.Info;
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
SetSelectedDateTime();
}
private void SetIsHandlerSuspended(DependencyProperty property, bool value)
{
if (value)
{
if (_isHandlerSuspended == null)
{
_isHandlerSuspended = new Dictionary<DependencyProperty, bool>(2);
}
_isHandlerSuspended[property] = true;
}
else
{
_isHandlerSuspended?.Remove(property);
}
}
private void SetValueNoCallback(DependencyProperty property, object value)
{
SetIsHandlerSuspended(property, true);
try
{
SetCurrentValue(property, value);
}
finally
{
SetIsHandlerSuspended(property, false);
}
}
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
SetValueNoCallback(TextProperty, _textBox.Text);
VerifyData();
}
private bool ProcessDateTimePickerKey(KeyEventArgs e)
{
switch (e.Key)
{
case Key.System:
{
switch (e.SystemKey)
{
case Key.Down:
{
if ((Keyboard.Modifiers & ModifierKeys.Alt) == ModifierKeys.Alt)
{
TogglePopUp();
return true;
}
break;
}
}
break;
}
case Key.Enter:
{
SetSelectedDateTime();
return true;
}
}
return false;
}
private void TextBox_KeyDown(object sender, KeyEventArgs e)
{
e.Handled = ProcessDateTimePickerKey(e) || e.Handled;
}
private void DropDownButton_MouseLeave(object sender, MouseEventArgs e)
{
_disablePopupReopen = false;
}
private bool IsHandlerSuspended(DependencyProperty property)
{
return _isHandlerSuspended != null && _isHandlerSuspended.ContainsKey(property);
}
private void PopUp_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (sender is Popup popup && !popup.StaysOpen)
{
if (_dropDownButton?.InputHitTest(e.GetPosition(_dropDownButton)) != null)
{
_disablePopupReopen = true;
}
}
}
private void PopUp_Opened(object sender, EventArgs e)
{
if (!IsDropDownOpen)
{
SetCurrentValue(IsDropDownOpenProperty, ValueBoxes.TrueBox);
}
_calendarWithClock?.MoveFocus(new TraversalRequest(FocusNavigationDirection.First));
OnPickerOpened(new RoutedEventArgs());
}
private void PopUp_Closed(object sender, EventArgs e)
{
if (IsDropDownOpen)
{
SetCurrentValue(IsDropDownOpenProperty, ValueBoxes.FalseBox);
}
if (_calendarWithClock.IsKeyboardFocusWithin)
{
MoveFocus(new TraversalRequest(FocusNavigationDirection.First));
}
OnPickerClosed(new RoutedEventArgs());
}
private void DropDownButton_Click(object sender, RoutedEventArgs e) => TogglePopUp();
private void TogglePopUp()
{
if (IsDropDownOpen)
{
SetCurrentValue(IsDropDownOpenProperty, ValueBoxes.FalseBox);
}
else
{
if (_disablePopupReopen)
{
_disablePopupReopen = false;
}
else
{
SetSelectedDateTime();
SetCurrentValue(IsDropDownOpenProperty, ValueBoxes.TrueBox);
}
}
}
private void SafeSetText(string s)
{
if (string.Compare(Text, s, StringComparison.Ordinal) != 0)
{
SetCurrentValue(TextProperty, s);
}
}
private DateTime? ParseText(string text)
{
try
{
return DateTime.Parse(text);
}
catch
{
// ignored
}
return null;
}
private DateTime? SetTextBoxValue(string s)
{
if (string.IsNullOrEmpty(s))
{
SafeSetText(s);
return SelectedDateTime;
}
var d = ParseText(s);
if (d != null)
{
SafeSetText(DateTimeToString((DateTime)d));
return d;
}
if (SelectedDateTime != null)
{
var newtext = DateTimeToString(SelectedDateTime.Value);
SafeSetText(newtext);
return SelectedDateTime;
}
SafeSetText(DateTimeToString(DisplayDateTime));
return DisplayDateTime;
}
private void SetSelectedDateTime()
{
if (_textBox != null)
{
if (!string.IsNullOrEmpty(_textBox.Text))
{
var s = _textBox.Text;
if (SelectedDateTime != null)
{
var selectedTime = DateTimeToString(SelectedDateTime.Value);
if (string.Compare(selectedTime, s, StringComparison.Ordinal) == 0)
{
return;
}
}
var d = SetTextBoxValue(s);
if (!SelectedDateTime.Equals(d))
{
SetCurrentValue(SelectedDateTimeProperty, d);
SetCurrentValue(DisplayDateTimeProperty, d);
}
}
else
{
if (SelectedDateTime.HasValue)
{
SetCurrentValue(SelectedDateTimeProperty, null);
}
}
}
else
{
var d = SetTextBoxValue(_defaultText);
if (!SelectedDateTime.Equals(d))
{
SetCurrentValue(SelectedDateTimeProperty, d);
}
}
}
private string DateTimeToString(DateTime d) => d.ToString(DateTimeFormat);
private static void OnGotFocus(object sender, RoutedEventArgs e)
{
var picker = (DateTimePicker)sender;
if (!e.Handled && picker._textBox != null)
{
if (Equals(e.OriginalSource, picker))
{
picker._textBox.Focus();
e.Handled = true;
}
else if (Equals(e.OriginalSource, picker._textBox))
{
picker._textBox.SelectAll();
e.Handled = true;
}
}
}
#endregion
}
}
@@ -0,0 +1,43 @@
using System;
using HandyControl.Data;
namespace HandyControl.Controls
{
/// <summary>
/// 表示可以成为一个数据输入控件
/// </summary>
public interface IDataInput
{
/// <summary>
/// 验证数据
/// </summary>
/// <returns></returns>
bool VerifyData();
/// <summary>
/// 数据验证委托
/// </summary>
Func<string, OperationResult<bool>> VerifyFunc { get; set; }
/// <summary>
/// 数据是否错误
/// </summary>
bool IsError { get; set; }
/// <summary>
/// 错误提示
/// </summary>
string ErrorStr { get; set; }
/// <summary>
/// 文本类型
/// </summary>
TextType TextType { get; set; }
/// <summary>
/// 是否显示清除按钮
/// </summary>
bool ShowClearButton { get; set; }
}
}
@@ -0,0 +1,400 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using HandyControl.Data;
using HandyControl.Interactivity;
using HandyControl.Tools;
namespace HandyControl.Controls
{
/// <inheritdoc cref="IDataInput" />
/// <summary>
/// 数值选择控件
/// </summary>
[TemplatePart(Name = ElementTextBox, Type = typeof(TextBox))]
public class NumericUpDown : Control, IDataInput
{
#region Constants
private const string ElementTextBox = "PART_TextBox";
#endregion Constants
#region Data
private TextBox _textBox;
#endregion Data
public NumericUpDown()
{
CommandBindings.Add(new CommandBinding(ControlCommands.Prev, (s, e) =>
{
Value += Increment;
_textBox.Text = CurrentText;
_textBox.Select(_textBox.Text.Length, 0);
}));
CommandBindings.Add(new CommandBinding(ControlCommands.Next, (s, e) =>
{
Value -= Increment;
_textBox.Text = CurrentText;
_textBox.Select(_textBox.Text.Length, 0);
}));
CommandBindings.Add(new CommandBinding(ControlCommands.Clear, (s, e) =>
{
ClearValue(ValueProperty);
_textBox.Text = string.Empty;
}));
Loaded += (s, e) => OnApplyTemplate();
}
protected override void OnGotFocus(RoutedEventArgs e)
{
base.OnGotFocus(e);
if (_textBox != null)
{
_textBox?.Focus();
_textBox.Select(_textBox.Text.Length, 0);
}
}
public override void OnApplyTemplate()
{
if (_textBox != null)
{
_textBox.TextChanged -= TextBox_TextChanged;
_textBox.PreviewKeyDown -= TextBox_PreviewKeyDown;
_textBox.LostFocus -= TextBox_LostFocus;
}
base.OnApplyTemplate();
_textBox = GetTemplateChild(ElementTextBox) as TextBox;
if (_textBox != null)
{
_textBox.TextChanged += TextBox_TextChanged;
_textBox.PreviewKeyDown += TextBox_PreviewKeyDown;
_textBox.LostFocus += TextBox_LostFocus;
_textBox.Text = CurrentText;
}
}
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
if (IsError) return;
_textBox.Text = CurrentText;
}
private void TextBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Up)
{
Value += Increment;
_textBox.Text = CurrentText;
}
else if (e.Key == Key.Down)
{
Value -= Increment;
_textBox.Text = CurrentText;
}
}
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
if (!VerifyData()) return;
if (double.TryParse(_textBox.Text, out var value))
{
Value = value;
}
}
protected override void OnMouseWheel(MouseWheelEventArgs e)
{
base.OnMouseWheel(e);
if (_textBox.IsFocused)
{
Value += e.Delta > 0 ? Increment : -Increment;
_textBox.Text = CurrentText;
_textBox.Select(_textBox.Text.Length, 0);
e.Handled = true;
}
}
private string CurrentText => DecimalPlaces.HasValue
? Value.ToString($"#0.{new string('0', DecimalPlaces.Value)}")
: Value.ToString("#0");
protected virtual void OnValueChanged(FunctionEventArgs<double> e) => RaiseEvent(e);
/// <summary>
/// 值改变事件
/// </summary>
public static readonly RoutedEvent ValueChangedEvent =
EventManager.RegisterRoutedEvent("ValueChanged", RoutingStrategy.Bubble,
typeof(EventHandler<FunctionEventArgs<double>>), typeof(NumericUpDown));
/// <summary>
/// 值改变事件
/// </summary>
public event EventHandler<FunctionEventArgs<double>> ValueChanged
{
add => AddHandler(ValueChangedEvent, value);
remove => RemoveHandler(ValueChangedEvent, value);
}
/// <summary>
/// 当前值
/// </summary>
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
"Value", typeof(double), typeof(NumericUpDown),
new FrameworkPropertyMetadata(default(double), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
OnValueChanged, CoerceValue), ArithmeticHelper.IsValidDoubleValue);
private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (NumericUpDown) d;
var v = (double) e.NewValue;
ctl.OnValueChanged(new FunctionEventArgs<double>(ValueChangedEvent, ctl)
{
Info = v
});
}
private static object CoerceValue(DependencyObject d, object basevalue)
{
var ctl = (NumericUpDown)d;
var minimum = ctl.Minimum;
var num = (double)basevalue;
if (num < minimum)
{
ctl.Value = minimum;
return minimum;
}
var maximum = ctl.Maximum;
if (num > maximum)
{
ctl.Value = maximum;
}
var result = num > maximum ? maximum : num;
if (!ctl.DecimalPlaces.HasValue)
{
result = Math.Floor(result);
}
return result;
}
/// <summary>
/// 当前值
/// </summary>
public double Value
{
get => (double) GetValue(ValueProperty);
set => SetValue(ValueProperty, value);
}
/// <summary>
/// 最大值
/// </summary>
public static readonly DependencyProperty MaximumProperty = DependencyProperty.Register(
"Maximum", typeof(double), typeof(NumericUpDown), new PropertyMetadata(100.0, OnMaximumChanged, CoerceMaximum), ArithmeticHelper.IsValidDoubleValue);
private static void OnMaximumChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (NumericUpDown)d;
ctl.CoerceValue(MinimumProperty);
ctl.CoerceValue(ValueProperty);
}
private static object CoerceMaximum(DependencyObject d, object basevalue)
{
var minimum = ((NumericUpDown)d).Minimum;
return (double)basevalue < minimum ? minimum : basevalue;
}
/// <summary>
/// 最大值
/// </summary>
public double Maximum
{
get => (double) GetValue(MaximumProperty);
set => SetValue(MaximumProperty, value);
}
/// <summary>
/// 最小值
/// </summary>
public static readonly DependencyProperty MinimumProperty = DependencyProperty.Register(
"Minimum", typeof(double), typeof(NumericUpDown), new PropertyMetadata(default(double), OnMinimumChanged, CoerceMinimum), ArithmeticHelper.IsValidDoubleValue);
private static void OnMinimumChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (NumericUpDown)d;
ctl.CoerceValue(MaximumProperty);
ctl.CoerceValue(ValueProperty);
}
private static object CoerceMinimum(DependencyObject d, object basevalue)
{
var maximum = ((NumericUpDown)d).Maximum;
return (double)basevalue > maximum ? maximum : basevalue;
}
/// <summary>
/// 最小值
/// </summary>
public double Minimum
{
get => (double) GetValue(MinimumProperty);
set => SetValue(MinimumProperty, value);
}
/// <summary>
/// 指示每单击一下按钮时增加或减少的数量
/// </summary>
public static readonly DependencyProperty IncrementProperty = DependencyProperty.Register(
"Increment", typeof(double), typeof(NumericUpDown), new PropertyMetadata(ValueBoxes.Double1Box));
/// <summary>
/// 指示每单击一下按钮时增加或减少的数量
/// </summary>
public double Increment
{
get => (double) GetValue(IncrementProperty);
set => SetValue(IncrementProperty, value);
}
/// <summary>
/// 指示要显示的小数位数
/// </summary>
public static readonly DependencyProperty DecimalPlacesProperty = DependencyProperty.Register(
"DecimalPlaces", typeof(int?), typeof(NumericUpDown), new PropertyMetadata(default(int?)));
/// <summary>
/// 指示要显示的小数位数
/// </summary>
public int? DecimalPlaces
{
get => (int?) GetValue(DecimalPlacesProperty);
set => SetValue(DecimalPlacesProperty, value);
}
/// <summary>
/// 是否显示上下调值按钮
/// </summary>
internal static readonly DependencyProperty ShowUpDownButtonProperty = DependencyProperty.Register(
"ShowUpDownButton", typeof(bool), typeof(NumericUpDown), new PropertyMetadata(ValueBoxes.TrueBox));
/// <summary>
/// 是否显示上下调值按钮
/// </summary>
internal bool ShowUpDownButton
{
get => (bool) GetValue(ShowUpDownButtonProperty);
set => SetValue(ShowUpDownButtonProperty, value);
}
/// <summary>
/// 数据是否错误
/// </summary>
public static readonly DependencyProperty IsErrorProperty = DependencyProperty.Register(
"IsError", typeof(bool), typeof(NumericUpDown), new PropertyMetadata(ValueBoxes.FalseBox));
public bool IsError
{
get => (bool) GetValue(IsErrorProperty);
set => SetValue(IsErrorProperty, value);
}
/// <summary>
/// 错误提示
/// </summary>
public static readonly DependencyProperty ErrorStrProperty = DependencyProperty.Register(
"ErrorStr", typeof(string), typeof(NumericUpDown), new PropertyMetadata(default(string)));
public string ErrorStr
{
get => (string) GetValue(ErrorStrProperty);
set => SetValue(ErrorStrProperty, value);
}
public static readonly DependencyPropertyKey TextTypePropertyKey =
DependencyProperty.RegisterReadOnly("TextType", typeof(TextType), typeof(NumericUpDown),
new PropertyMetadata(default(TextType)));
/// <summary>
/// 文本类型
/// </summary>
public static readonly DependencyProperty TextTypeProperty = TextTypePropertyKey.DependencyProperty;
public TextType TextType
{
get => (TextType) GetValue(TextTypeProperty);
set => SetValue(TextTypeProperty, value);
}
/// <summary>
/// 是否显示清除按钮
/// </summary>
public static readonly DependencyProperty ShowClearButtonProperty = DependencyProperty.Register(
"ShowClearButton", typeof(bool), typeof(NumericUpDown), new PropertyMetadata(ValueBoxes.FalseBox));
public bool ShowClearButton
{
get => (bool) GetValue(ShowClearButtonProperty);
set => SetValue(ShowClearButtonProperty, value);
}
public Func<string, OperationResult<bool>> VerifyFunc { get; set; }
public virtual bool VerifyData()
{
OperationResult<bool> result;
if (VerifyFunc != null)
{
result = VerifyFunc.Invoke(_textBox.Text);
}
else
{
if (!string.IsNullOrEmpty(_textBox.Text))
{
if (double.TryParse(_textBox.Text, out var value))
{
if (value < Minimum || value > Maximum)
{
result = OperationResult.Failed(Properties.Langs.Lang.OutOfRange);
}
else
{
result = OperationResult.Success();
}
}
else
{
result = OperationResult.Failed(Properties.Langs.Lang.FormatError);
}
}
else if (InfoElement.GetNecessary(this))
{
result = OperationResult.Failed(Properties.Langs.Lang.IsNecessary);
}
else
{
result = OperationResult.Success();
}
}
IsError = !result.Data;
ErrorStr = result.Message;
return result.Data;
}
}
}
@@ -0,0 +1,170 @@
using System;
using System.ComponentModel;
using System.Security;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using HandyControl.Data;
using HandyControl.Interactivity;
using HandyControl.Properties.Langs;
namespace HandyControl.Controls
{
/// <inheritdoc cref="IDataInput" />
[TemplatePart(Name = ElementPasswordBox, Type = typeof(System.Windows.Controls.PasswordBox))]
public class PasswordBox : Control, IDataInput
{
private const string ElementPasswordBox = "PART_PasswordBox";
/// <summary>
/// 掩码字符
/// </summary>
public static readonly DependencyProperty PasswordCharProperty =
System.Windows.Controls.PasswordBox.PasswordCharProperty.AddOwner(typeof(PasswordBox),
new FrameworkPropertyMetadata('●'));
/// <summary>
/// 数据是否错误
/// </summary>
public static readonly DependencyProperty IsErrorProperty = DependencyProperty.Register(
"IsError", typeof(bool), typeof(PasswordBox), new PropertyMetadata(ValueBoxes.FalseBox));
/// <summary>
/// 错误提示
/// </summary>
public static readonly DependencyProperty ErrorStrProperty = DependencyProperty.Register(
"ErrorStr", typeof(string), typeof(PasswordBox), new PropertyMetadata(default(string)));
/// <summary>
/// 文本类型
/// </summary>
public static readonly DependencyProperty TextTypeProperty = DependencyProperty.Register(
"TextType", typeof(TextType), typeof(PasswordBox), new PropertyMetadata(default(TextType)));
/// <summary>
/// 是否显示清除按钮
/// </summary>
public static readonly DependencyProperty ShowClearButtonProperty = DependencyProperty.Register(
"ShowClearButton", typeof(bool), typeof(PasswordBox), new PropertyMetadata(ValueBoxes.FalseBox));
private string _password;
private char _passwordChar;
public PasswordBox()
{
CommandBindings.Add(new CommandBinding(ControlCommands.Clear, (s, e) => ActualPasswordBox.Clear()));
}
public System.Windows.Controls.PasswordBox ActualPasswordBox { get; set; }
[DefaultValue("")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public string Password
{
get => ActualPasswordBox?.Password;
set
{
if (ActualPasswordBox == null)
{
_password = value;
return;
}
ActualPasswordBox.Password = value;
}
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public SecureString SecurePassword => ActualPasswordBox?.SecurePassword;
public char PasswordChar
{
get => (char) GetValue(PasswordCharProperty);
set => SetValue(PasswordCharProperty, value);
}
public Func<string, OperationResult<bool>> VerifyFunc { get; set; }
public bool IsError
{
get => (bool) GetValue(IsErrorProperty);
set => SetValue(IsErrorProperty, value);
}
public string ErrorStr
{
get => (string) GetValue(ErrorStrProperty);
set => SetValue(ErrorStrProperty, value);
}
public TextType TextType
{
get => (TextType) GetValue(TextTypeProperty);
set => SetValue(TextTypeProperty, value);
}
public bool ShowClearButton
{
get => (bool) GetValue(ShowClearButtonProperty);
set => SetValue(ShowClearButtonProperty, value);
}
public virtual bool VerifyData()
{
OperationResult<bool> result;
if (VerifyFunc != null)
{
result = VerifyFunc.Invoke(ActualPasswordBox.Password);
}
else
{
if (!string.IsNullOrEmpty(ActualPasswordBox.Password))
result = OperationResult.Success();
else if (InfoElement.GetNecessary(this))
result = OperationResult.Failed(Lang.IsNecessary);
else
result = OperationResult.Success();
}
IsError = !result.Data;
ErrorStr = result.Message;
return result.Data;
}
public override void OnApplyTemplate()
{
if (ActualPasswordBox != null)
ActualPasswordBox.PasswordChanged -= PasswordBox_PasswordChanged;
base.OnApplyTemplate();
ActualPasswordBox = GetTemplateChild(ElementPasswordBox) as System.Windows.Controls.PasswordBox;
if (ActualPasswordBox != null)
{
ActualPasswordBox.PasswordChanged += PasswordBox_PasswordChanged;
if (!string.IsNullOrEmpty(_password))
{
ActualPasswordBox.Password = _password;
_password = null;
}
if (_passwordChar != default(char))
{
ActualPasswordBox.PasswordChar = _passwordChar;
_passwordChar = default(char);
}
}
}
public void Paste() => ActualPasswordBox.Paste();
public void SelectAll() => ActualPasswordBox.SelectAll();
public void Clear() => ActualPasswordBox.SelectAll();
private void PasswordBox_PasswordChanged(object sender, RoutedEventArgs e) => VerifyData();
}
}
@@ -0,0 +1,130 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using HandyControl.Data;
using HandyControl.Interactivity;
namespace HandyControl.Controls
{
public class SearchBar : TextBox, ICommandSource
{
public SearchBar()
{
CommandBindings.Add(new CommandBinding(ControlCommands.Search, (s, e) => OnSearchStarted()));
}
public static readonly RoutedEvent SearchStartedEvent =
EventManager.RegisterRoutedEvent("SearchStarted", RoutingStrategy.Bubble,
typeof(EventHandler<FunctionEventArgs<string>>), typeof(SearchBar));
public event EventHandler<FunctionEventArgs<string>> SearchStarted
{
add => AddHandler(SearchStartedEvent, value);
remove => RemoveHandler(SearchStartedEvent, value);
}
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
if (e.Key == Key.Enter)
{
OnSearchStarted();
}
}
protected override void OnTextChanged(TextChangedEventArgs e)
{
base.OnTextChanged(e);
if (IsRealTime)
{
OnSearchStarted();
}
VerifyData();
}
private void OnSearchStarted()
{
RaiseEvent(new FunctionEventArgs<string>(SearchStartedEvent, this)
{
Info = Text
});
if (Command == null) return;
if (Command is RoutedCommand command)
{
command.Execute(CommandParameter, CommandTarget);
}
else
{
Command.Execute(CommandParameter);
}
}
/// <summary>
/// 是否实时搜索
/// </summary>
public static readonly DependencyProperty IsRealTimeProperty = DependencyProperty.Register(
"IsRealTime", typeof(bool), typeof(SearchBar), new PropertyMetadata(ValueBoxes.FalseBox));
/// <summary>
/// 是否实时搜索
/// </summary>
public bool IsRealTime
{
get => (bool)GetValue(IsRealTimeProperty);
set => SetValue(IsRealTimeProperty, value);
}
public static readonly DependencyProperty CommandProperty = DependencyProperty.Register(
"Command", typeof(ICommand), typeof(SearchBar), new PropertyMetadata(default(ICommand), OnCommandChanged));
private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (SearchBar)d;
if (e.OldValue is ICommand oldCommand)
{
oldCommand.CanExecuteChanged -= ctl.CanExecuteChanged;
}
if (e.NewValue is ICommand newCommand)
{
newCommand.CanExecuteChanged += ctl.CanExecuteChanged;
}
}
public ICommand Command
{
get => (ICommand) GetValue(CommandProperty);
set => SetValue(CommandProperty, value);
}
public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register(
"CommandParameter", typeof(object), typeof(SearchBar), new PropertyMetadata(default(object)));
public object CommandParameter
{
get => GetValue(CommandParameterProperty);
set => SetValue(CommandParameterProperty, value);
}
public static readonly DependencyProperty CommandTargetProperty = DependencyProperty.Register(
"CommandTarget", typeof(IInputElement), typeof(SearchBar), new PropertyMetadata(default(IInputElement)));
public IInputElement CommandTarget
{
get => (IInputElement) GetValue(CommandTargetProperty);
set => SetValue(CommandTargetProperty, value);
}
private void CanExecuteChanged(object sender, EventArgs e)
{
if (Command == null) return;
IsEnabled = Command is RoutedCommand command
? command.CanExecute(CommandParameter, CommandTarget)
: Command.CanExecute(CommandParameter);
}
}
}
@@ -0,0 +1,114 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using HandyControl.Data;
using HandyControl.Interactivity;
using HandyControl.Tools;
namespace HandyControl.Controls
{
/// <inheritdoc cref="IDataInput" />
public class TextBox : System.Windows.Controls.TextBox, IDataInput
{
public TextBox()
{
CommandBindings.Add(new CommandBinding(ControlCommands.Clear, (s, e) =>
{
ClearValue(TextProperty);
}));
}
protected override void OnTextChanged(TextChangedEventArgs e)
{
base.OnTextChanged(e);
VerifyData();
}
public Func<string, OperationResult<bool>> VerifyFunc { get; set; }
/// <summary>
/// 数据是否错误
/// </summary>
public static readonly DependencyProperty IsErrorProperty = DependencyProperty.Register(
"IsError", typeof(bool), typeof(TextBox), new PropertyMetadata(ValueBoxes.FalseBox));
public bool IsError
{
get => (bool) GetValue(IsErrorProperty);
set => SetValue(IsErrorProperty, value);
}
/// <summary>
/// 错误提示
/// </summary>
public static readonly DependencyProperty ErrorStrProperty = DependencyProperty.Register(
"ErrorStr", typeof(string), typeof(TextBox), new PropertyMetadata(default(string)));
public string ErrorStr
{
get => (string) GetValue(ErrorStrProperty);
set => SetValue(ErrorStrProperty, value);
}
/// <summary>
/// 文本类型
/// </summary>
public static readonly DependencyProperty TextTypeProperty = DependencyProperty.Register(
"TextType", typeof(TextType), typeof(TextBox), new PropertyMetadata(default(TextType)));
public TextType TextType
{
get => (TextType) GetValue(TextTypeProperty);
set => SetValue(TextTypeProperty, value);
}
/// <summary>
/// 是否显示清除按钮
/// </summary>
public static readonly DependencyProperty ShowClearButtonProperty = DependencyProperty.Register(
"ShowClearButton", typeof(bool), typeof(TextBox), new PropertyMetadata(ValueBoxes.FalseBox));
public bool ShowClearButton
{
get => (bool) GetValue(ShowClearButtonProperty);
set => SetValue(ShowClearButtonProperty, value);
}
public virtual bool VerifyData()
{
OperationResult<bool> result;
if (VerifyFunc != null)
{
result = VerifyFunc.Invoke(Text);
}
else
{
if (!string.IsNullOrEmpty(Text))
{
if (TextType != TextType.Common)
{
result = Text.IsKindOf(TextType) ? OperationResult.Success() : OperationResult.Failed(Properties.Langs.Lang.FormatError);
}
else
{
result = OperationResult.Success();
}
}
else if (InfoElement.GetNecessary(this))
{
result = OperationResult.Failed(Properties.Langs.Lang.IsNecessary);
}
else
{
result = OperationResult.Success();
}
}
IsError = !result.Data;
ErrorStr = result.Message;
return result.Data;
}
}
}
@@ -0,0 +1,718 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Threading;
using HandyControl.Data;
using HandyControl.Interactivity;
namespace HandyControl.Controls
{
/// <summary>
/// 时间选择器
/// </summary>
[TemplatePart(Name = ElementRoot, Type = typeof(Grid))]
[TemplatePart(Name = ElementTextBox, Type = typeof(WatermarkTextBox))]
[TemplatePart(Name = ElementButton, Type = typeof(Button))]
[TemplatePart(Name = ElementPopup, Type = typeof(Popup))]
public class TimePicker : Control, IDataInput
{
#region Constants
private const string ElementRoot = "PART_Root";
private const string ElementTextBox = "PART_TextBox";
private const string ElementButton = "PART_Button";
private const string ElementPopup = "PART_Popup";
#endregion Constants
#region Data
private Clock _clock;
private string _defaultText;
private ButtonBase _dropDownButton;
private Popup _popUp;
private bool _disablePopupReopen;
private WatermarkTextBox _textBox;
private IDictionary<DependencyProperty, bool> _isHandlerSuspended;
private DateTime? _originalSelectedTime;
#endregion Data
#region Public Events
public static readonly RoutedEvent SelectedTimeChangedEvent =
EventManager.RegisterRoutedEvent("SelectedTimeChanged", RoutingStrategy.Direct,
typeof(EventHandler<FunctionEventArgs<DateTime?>>), typeof(TimePicker));
public event EventHandler<FunctionEventArgs<DateTime?>> SelectedTimeChanged
{
add => AddHandler(SelectedTimeChangedEvent, value);
remove => RemoveHandler(SelectedTimeChangedEvent, value);
}
public event RoutedEventHandler ClockClosed;
public event RoutedEventHandler ClockOpened;
#endregion Public Events
static TimePicker()
{
EventManager.RegisterClassHandler(typeof(TimePicker), GotFocusEvent, new RoutedEventHandler(OnGotFocus));
KeyboardNavigation.TabNavigationProperty.OverrideMetadata(typeof(TimePicker), new FrameworkPropertyMetadata(KeyboardNavigationMode.Once));
KeyboardNavigation.IsTabStopProperty.OverrideMetadata(typeof(TimePicker), new FrameworkPropertyMetadata(false));
}
public TimePicker()
{
InitClock();
CommandBindings.Add(new CommandBinding(ControlCommands.Clear, (s, e) =>
{
ClearValue(SelectedTimeProperty);
ClearValue(TextProperty);
_textBox.Text = string.Empty;
}));
}
#region Public Properties
#region TimeFormat
public static readonly DependencyProperty TimeFormatProperty = DependencyProperty.Register(
"TimeFormat", typeof(string), typeof(TimePicker), new PropertyMetadata("HH:mm:ss"));
public string TimeFormat
{
get => (string)GetValue(TimeFormatProperty);
set => SetValue(TimeFormatProperty, value);
}
#endregion TimeFormat
#region ClockStyle
public Style ClockStyle
{
get => (Style)GetValue(ClockStyleProperty);
set => SetValue(ClockStyleProperty, value);
}
public static readonly DependencyProperty ClockStyleProperty = DependencyProperty.Register("ClockStyle", typeof(Style), typeof(TimePicker));
#endregion CalendarStyle
#region DisplayTime
public DateTime DisplayTime
{
get => (DateTime)GetValue(DisplayTimeProperty);
set => SetValue(DisplayTimeProperty, value);
}
public static readonly DependencyProperty DisplayTimeProperty =
DependencyProperty.Register(
"DisplayTime",
typeof(DateTime),
typeof(TimePicker),
new FrameworkPropertyMetadata(DateTime.Now, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, null, CoerceDisplayTime));
private static object CoerceDisplayTime(DependencyObject d, object value)
{
var dp = (TimePicker)d;
dp._clock.DisplayTime = (DateTime)value;
return dp._clock.DisplayTime;
}
#endregion DisplayTime
#region IsDropDownOpen
public bool IsDropDownOpen
{
get => (bool)GetValue(IsDropDownOpenProperty);
set => SetValue(IsDropDownOpenProperty, value);
}
public static readonly DependencyProperty IsDropDownOpenProperty =
DependencyProperty.Register(
"IsDropDownOpen",
typeof(bool),
typeof(TimePicker),
new FrameworkPropertyMetadata(ValueBoxes.FalseBox, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnIsDropDownOpenChanged, OnCoerceIsDropDownOpen));
private static object OnCoerceIsDropDownOpen(DependencyObject d, object baseValue) => d is TimePicker dp && !dp.IsEnabled ? false : baseValue;
private static void OnIsDropDownOpenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var dp = d as TimePicker;
var newValue = (bool)e.NewValue;
if (dp?._popUp != null && dp._popUp.IsOpen != newValue)
{
dp._popUp.IsOpen = newValue;
if (newValue)
{
dp._originalSelectedTime = dp.SelectedTime;
dp.Dispatcher.BeginInvoke(DispatcherPriority.Input, (Action)delegate
{
dp._clock.Focus();
});
}
}
}
#endregion IsDropDownOpen
#region SelectedTime
public DateTime? SelectedTime
{
get => (DateTime?)GetValue(SelectedTimeProperty);
set => SetValue(SelectedTimeProperty, value);
}
public static readonly DependencyProperty SelectedTimeProperty =
DependencyProperty.Register(
"SelectedTime",
typeof(DateTime?),
typeof(TimePicker),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnSelectedTimeChanged, CoerceSelectedTime));
private static void OnSelectedTimeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (!(d is TimePicker dp)) return;
if (dp.SelectedTime.HasValue)
{
var time = dp.SelectedTime.Value;
dp.SetTextInternal(dp.DateTimeToString(time));
}
dp.RaiseEvent(new FunctionEventArgs<DateTime?>(SelectedTimeChangedEvent, dp)
{
Info = dp.SelectedTime
});
}
private static object CoerceSelectedTime(DependencyObject d, object value)
{
var dp = (TimePicker)d;
dp._clock.SelectedTime = (DateTime?)value;
return dp._clock.SelectedTime;
}
#endregion SelectedDate
#region Text
public string Text
{
get => (string)GetValue(TextProperty);
set => SetValue(TextProperty, value);
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register(
"Text",
typeof(string),
typeof(TimePicker),
new FrameworkPropertyMetadata(string.Empty, OnTextChanged));
private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is TimePicker dp && !dp.IsHandlerSuspended(TextProperty))
{
if (e.NewValue is string newValue)
{
if (dp._textBox != null)
{
dp._textBox.Text = newValue;
}
else
{
dp._defaultText = newValue;
}
dp.SetSelectedTime();
}
else
{
dp.SetValueNoCallback(SelectedTimeProperty, null);
}
}
}
/// <summary>
/// Sets the local Text property without breaking bindings
/// </summary>
/// <param name="value"></param>
private void SetTextInternal(string value)
{
SetCurrentValue(TextProperty, value);
}
#endregion Text
public Func<string, OperationResult<bool>> VerifyFunc { get; set; }
public static readonly DependencyProperty IsErrorProperty = DependencyProperty.Register(
"IsError", typeof(bool), typeof(TimePicker), new PropertyMetadata(ValueBoxes.FalseBox));
public bool IsError
{
get => (bool) GetValue(IsErrorProperty);
set => SetValue(IsErrorProperty, value);
}
public static readonly DependencyProperty ErrorStrProperty = DependencyProperty.Register(
"ErrorStr", typeof(string), typeof(TimePicker), new PropertyMetadata(default(string)));
public string ErrorStr
{
get => (string) GetValue(ErrorStrProperty);
set => SetValue(ErrorStrProperty, value);
}
public static readonly DependencyProperty TextTypeProperty = DependencyProperty.Register(
"TextType", typeof(TextType), typeof(TimePicker), new PropertyMetadata(default(TextType)));
public TextType TextType
{
get => (TextType) GetValue(TextTypeProperty);
set => SetValue(TextTypeProperty, value);
}
public static readonly DependencyProperty ShowClearButtonProperty = DependencyProperty.Register(
"ShowClearButton", typeof(bool), typeof(TimePicker), new PropertyMetadata(ValueBoxes.FalseBox));
public bool ShowClearButton
{
get => (bool) GetValue(ShowClearButtonProperty);
set => SetValue(ShowClearButtonProperty, value);
}
#endregion Public Properties
#region Public Methods
public virtual bool VerifyData()
{
OperationResult<bool> result;
if (VerifyFunc != null)
{
result = VerifyFunc.Invoke(Text);
}
else
{
if (!string.IsNullOrEmpty(Text))
{
result = OperationResult.Success();
}
else if (InfoElement.GetNecessary(this))
{
result = OperationResult.Failed(Properties.Langs.Lang.IsNecessary);
}
else
{
result = OperationResult.Success();
}
}
IsError = !result.Data;
ErrorStr = result.Message;
return result.Data;
}
public override void OnApplyTemplate()
{
if (DesignerProperties.GetIsInDesignMode(this)) return;
if (_popUp != null)
{
_popUp.PreviewMouseLeftButtonDown -= PopUp_PreviewMouseLeftButtonDown;
_popUp.Opened -= PopUp_Opened;
_popUp.Closed -= PopUp_Closed;
_popUp.Child = null;
}
if (_dropDownButton != null)
{
_dropDownButton.Click -= DropDownButton_Click;
_dropDownButton.MouseLeave -= DropDownButton_MouseLeave;
}
if (_textBox != null)
{
_textBox.KeyDown -= TextBox_KeyDown;
_textBox.TextChanged -= TextBox_TextChanged;
_textBox.LostFocus -= TextBox_LostFocus;
}
base.OnApplyTemplate();
_popUp = GetTemplateChild(ElementPopup) as Popup;
_dropDownButton = GetTemplateChild(ElementButton) as Button;
_textBox = GetTemplateChild(ElementTextBox) as WatermarkTextBox;
CheckNull();
_popUp.PreviewMouseLeftButtonDown += PopUp_PreviewMouseLeftButtonDown;
_popUp.Opened += PopUp_Opened;
_popUp.Closed += PopUp_Closed;
_popUp.Child = _clock;
if (IsDropDownOpen)
{
_popUp.IsOpen = true;
}
_dropDownButton.Click += DropDownButton_Click;
_dropDownButton.MouseLeave += DropDownButton_MouseLeave;
if (SelectedTime == null)
{
_textBox.Text = DateTime.Now.ToString(TimeFormat);
}
_textBox.KeyDown += TextBox_KeyDown;
_textBox.TextChanged += TextBox_TextChanged;
_textBox.LostFocus += TextBox_LostFocus;
if (SelectedTime == null)
{
if (!string.IsNullOrEmpty(_defaultText))
{
_textBox.Text = _defaultText;
SetSelectedTime();
}
}
else
{
_textBox.Text = DateTimeToString((DateTime)SelectedTime);
}
if (_originalSelectedTime == null)
{
_originalSelectedTime = DateTime.Now;
}
SetCurrentValue(DisplayTimeProperty, _originalSelectedTime);
}
public override string ToString() => SelectedTime?.ToString(TimeFormat) ?? string.Empty;
#endregion
#region Protected Methods
protected virtual void OnClockClosed(RoutedEventArgs e)
{
var handler = ClockClosed;
handler?.Invoke(this, e);
}
protected virtual void OnClockOpened(RoutedEventArgs e)
{
var handler = ClockOpened;
handler?.Invoke(this, e);
}
#endregion Protected Methods
#region Private Methods
private void CheckNull()
{
if (_dropDownButton == null || _popUp == null || _textBox == null)
throw new Exception();
}
private void InitClock()
{
_clock = new Clock
{
ShowConfirmButton = true
};
_clock.SelectedTimeChanged += Clock_SelectedTimeChanged;
_clock.Confirmed += Clock_Confirmed;
}
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
SetSelectedTime();
}
private void SetIsHandlerSuspended(DependencyProperty property, bool value)
{
if (value)
{
if (_isHandlerSuspended == null)
{
_isHandlerSuspended = new Dictionary<DependencyProperty, bool>(2);
}
_isHandlerSuspended[property] = true;
}
else
{
_isHandlerSuspended?.Remove(property);
}
}
private void SetValueNoCallback(DependencyProperty property, object value)
{
SetIsHandlerSuspended(property, true);
try
{
SetCurrentValue(property, value);
}
finally
{
SetIsHandlerSuspended(property, false);
}
}
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
SetValueNoCallback(TextProperty, _textBox.Text);
VerifyData();
}
private bool ProcessTimePickerKey(KeyEventArgs e)
{
switch (e.Key)
{
case Key.System:
{
switch (e.SystemKey)
{
case Key.Down:
{
if ((Keyboard.Modifiers & ModifierKeys.Alt) == ModifierKeys.Alt)
{
TogglePopUp();
return true;
}
break;
}
}
break;
}
case Key.Enter:
{
SetSelectedTime();
return true;
}
}
return false;
}
private void TextBox_KeyDown(object sender, KeyEventArgs e)
{
e.Handled = ProcessTimePickerKey(e) || e.Handled;
}
private void DropDownButton_MouseLeave(object sender, MouseEventArgs e)
{
_disablePopupReopen = false;
}
private bool IsHandlerSuspended(DependencyProperty property)
{
return _isHandlerSuspended != null && _isHandlerSuspended.ContainsKey(property);
}
private void PopUp_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (sender is Popup popup && !popup.StaysOpen)
{
if (_dropDownButton?.InputHitTest(e.GetPosition(_dropDownButton)) != null)
{
_disablePopupReopen = true;
}
}
}
private void Clock_SelectedTimeChanged(object sender, FunctionEventArgs<DateTime?> e) => SelectedTime = e.Info;
private void Clock_Confirmed() => TogglePopUp();
private void PopUp_Opened(object sender, EventArgs e)
{
if (!IsDropDownOpen)
{
SetCurrentValue(IsDropDownOpenProperty, ValueBoxes.TrueBox);
}
_clock?.MoveFocus(new TraversalRequest(FocusNavigationDirection.First));
OnClockOpened(new RoutedEventArgs());
}
private void PopUp_Closed(object sender, EventArgs e)
{
if (IsDropDownOpen)
{
SetCurrentValue(IsDropDownOpenProperty, ValueBoxes.FalseBox);
}
if (_clock.IsKeyboardFocusWithin)
{
MoveFocus(new TraversalRequest(FocusNavigationDirection.First));
}
OnClockClosed(new RoutedEventArgs());
}
private void DropDownButton_Click(object sender, RoutedEventArgs e) => TogglePopUp();
private void TogglePopUp()
{
if (IsDropDownOpen)
{
SetCurrentValue(IsDropDownOpenProperty, ValueBoxes.FalseBox);
}
else
{
if (_disablePopupReopen)
{
_disablePopupReopen = false;
}
else
{
SetSelectedTime();
SetCurrentValue(IsDropDownOpenProperty, ValueBoxes.TrueBox);
}
}
}
private void SafeSetText(string s)
{
if (string.Compare(Text, s, StringComparison.Ordinal) != 0)
{
SetCurrentValue(TextProperty, s);
}
}
private DateTime? ParseText(string text)
{
try
{
return DateTime.Parse(text);
}
catch
{
// ignored
}
return null;
}
private DateTime? SetTextBoxValue(string s)
{
if (string.IsNullOrEmpty(s))
{
SafeSetText(s);
return SelectedTime;
}
var d = ParseText(s);
if (d != null)
{
SafeSetText(DateTimeToString((DateTime)d));
return d;
}
if (SelectedTime != null)
{
var newtext = DateTimeToString((DateTime) SelectedTime);
SafeSetText(newtext);
return SelectedTime;
}
SafeSetText(DateTimeToString(DisplayTime));
return DisplayTime;
}
private void SetSelectedTime()
{
if (_textBox != null)
{
if (!string.IsNullOrEmpty(_textBox.Text))
{
var s = _textBox.Text;
if (SelectedTime != null)
{
var selectedTime = DateTimeToString(SelectedTime.Value);
if (string.Compare(selectedTime, s, StringComparison.Ordinal) == 0)
{
return;
}
}
var d = SetTextBoxValue(s);
if (!SelectedTime.Equals(d))
{
SetCurrentValue(SelectedTimeProperty, d);
SetCurrentValue(DisplayTimeProperty, d);
}
}
else
{
if (SelectedTime.HasValue)
{
SetCurrentValue(SelectedTimeProperty, null);
}
}
}
else
{
var d = SetTextBoxValue(_defaultText);
if (!SelectedTime.Equals(d))
{
SetCurrentValue(SelectedTimeProperty, d);
}
}
}
private string DateTimeToString(DateTime d) => d.ToString(TimeFormat);
private static void OnGotFocus(object sender, RoutedEventArgs e)
{
var picker = (TimePicker)sender;
if (!e.Handled && picker._textBox != null)
{
if (Equals(e.OriginalSource, picker))
{
picker._textBox.Focus();
e.Handled = true;
}
else if (Equals(e.OriginalSource, picker._textBox))
{
picker._textBox.SelectAll();
e.Handled = true;
}
}
}
#endregion Private Methods
}
}
@@ -0,0 +1,36 @@
using System.Windows;
namespace HandyControl.Controls
{
internal class WatermarkTextBox : TextBox
{
#region Public Properties
#region Watermark
public static readonly DependencyProperty WatermarkProperty = DependencyProperty.Register(
"Watermark", typeof(object), typeof(WatermarkTextBox), new PropertyMetadata(default(object)));
public object Watermark
{
get => GetValue(WatermarkProperty);
set => SetValue(WatermarkProperty, value);
}
#endregion Watermark
#endregion Public Properties
protected override void OnGotFocus(RoutedEventArgs e)
{
base.OnGotFocus(e);
if (IsEnabled)
{
if (!string.IsNullOrEmpty(Text))
{
Select(0, Text.Length);
}
}
}
}
}