65.9K
CodeProject 正在变化。 阅读更多。
Home

用于 WPF 蒙版 TextBox 的 double 或 TimeSpan 值 TextBox 的行为

starIconstarIconstarIconstarIconstarIcon

5.00/5 (7投票s)

2016年10月26日

CPOL

4分钟阅读

viewsIcon

15704

downloadIcon

415

提出了一种创建掩码文本框的概念,并实现了TimeSpan和双精度值的功能。

引言

我长期以来一直在使用一个应用程序,该应用程序有一个TextBox,用于指定音乐曲目上的时间,我对此一直很不满意。我终于有时间研究一下我能实现什么。我主要使用WPF,所以我想我应该用这项技术来实现。我可以将其实现为一个控件,但决定将其实现为一个行为。这确实有优点,因为TextBox控件的所有样式都可以用于此控件,并且可能以其他方式使用。我最近添加了一个用于双精度值的控件。

注释

这是进行中的工作,随着时间的推移,将添加更多功能。特别是,我计划处理粘贴功能,但当前版本中没有。TimeSpan行为有一些改进。双精度行为设计更简洁。它可能是创建新行为的更易于使用的模板。

DoubleTextBoxBehavior有一个format DependencyProperty,应该进行设置。它与Visual Studio中其他地方使用的格式字符串有些相似,但并非完全相同,这是故意的。当前,最右边的句点或逗号用作小数点。任何零字符都将用于数字,无论其位置如何,最右边的加号或减号(破折号)将用于符号。任何其他字符都将按格式字符串显示,这提供了很大的灵活性。如果使用加号,则正数将有符号,否则只有负数。使用减号键可以使数字为正,而使用加号键可以使其为正。当光标位于符号位置时,使用向上和向下箭头键可以在正数和负数之间切换。但是,如果指定的最小值大于零或最大值小于零,则符号不会改变。如果更改了符号,则数字将立即调整以保持在指定的最小和最大值之内。

设计

该设计只有一个class,但该class有两个非常不同的部分,分别作为static和实例代码实现。通常,对于行为,我只会实现static部分,并且只使用static event处理程序。我认为在这种情况下,将代码分开会更好。

静态部分如下:

                public static readonly DependencyProperty MaxTimeProperty =
                        DependencyProperty.RegisterAttached("MaxTime", typeof(string),
                                typeof(TimeSpanTextBoxBehaviour), new UIPropertyMetadata(null, OnMaxTimeChanged));

                public static string GetMaxTime(Control o)
                {
                        return (string)o.GetValue(MaxTimeProperty);
                }

                public static void SetMaxTime(Control o, string value)
                {
                        o.SetValue(MaxTimeProperty, value);
                }

                private static void OnMaxTimeChanged(DependencyObject dependencyObject,
                        DependencyPropertyChangedEventArgs e)
                {
                        var timeTextBoxBehaviour = GetTimeTextBoxBehaviour(dependencyObject);
                        var timeString = (string)e.NewValue;
                        var timeSpan = TimeSpanParse(timeString, false);
                        timeTextBoxBehaviour.MaxTimeSpanChanged(timeSpan);
                }

                public static readonly DependencyProperty ValueProperty =
                        DependencyProperty.RegisterAttached("Value", typeof(TimeSpan),
                                typeof(TimeSpanTextBoxBehaviour), new FrameworkPropertyMetadata(TimeSpan.Zero, 
                                        FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnValueChanged));

                public static TimeSpan GetValue(Control o)
                {
                        return (TimeSpan)o.GetValue(ValueProperty);
                }

                public static void SetValue(Control o, TimeSpan value)
                {
                        o.SetValue(ValueProperty, value);
                }

                public static readonly DependencyProperty TimeFormatProperty =
                        DependencyProperty.RegisterAttached("TimeFormat", typeof(TimerFormats),
                                typeof(TimerFormats), new UIPropertyMetadata(TimerFormats.Seconds10Ths, OnTimeFormatChanged));

                public static TimerFormats GetTimeFormat(Control o)
                {
                        return (TimerFormats)o.GetValue(TimeFormatProperty);
                }

                public static void SetTimeFormat(Control o, TimerFormats timeFormat)
                {
                        o.SetValue(TimeFormatProperty, timeFormat);
                }

                private static void OnTimeFormatChanged(DependencyObject dependencyObject,
                        DependencyPropertyChangedEventArgs e)
                {
                        var timeTextBoxBehaviour = GetTimeTextBoxBehaviour(dependencyObject);
                        timeTextBoxBehaviour.TimeFormatChanged((TimerFormats)e.NewValue);
                }

                public static TimeSpanTextBoxBehaviour GetTimeTextBoxBehaviour(object textBox)
                {
                        var castTextBox = (TextBox)textBox;
                        var control = GetTimeTextBoxBehaviour(castTextBox);
                        if (control == null)
                        {
                                control = new TimeSpanTextBoxBehaviour(castTextBox);
                                SetTimeTextBoxBehaviour(castTextBox, control);
                        }
                        return control;
                }

                private static void OnValueChanged(DependencyObject dependencyObject,
                        DependencyPropertyChangedEventArgs e)
                {
                        var timeTextBoxBehaviour = GetTimeTextBoxBehaviour(dependencyObject);
                        var timeSpan = (TimeSpan)e.NewValue;
                        timeTextBoxBehaviour.UpdateText(timeSpan);
                }

                /// <summary>
                /// This is the private DependencyProperty where the instance to handle the time behaviour is kept
                /// </summary>
                private static readonly DependencyProperty TimeTextBoxBehaviourProperty =
                        DependencyProperty.RegisterAttached("TimeTextBoxBehaviour", typeof(TimeSpanTextBoxBehaviour),
                                typeof(TimeSpanTextBoxBehaviour), new UIPropertyMetadata(null));

                private static TimeSpanTextBoxBehaviour GetTimeTextBoxBehaviour(TextBox o)
                {
                        return (TimeSpanTextBoxBehaviour)o.GetValue(TimeTextBoxBehaviourProperty);
                }

                private static void SetTimeTextBoxBehaviour(TextBox o, TimeSpanTextBoxBehaviour value)
                {
                        o.SetValue(TimeTextBoxBehaviourProperty, value);
                }
                #endregion

修改

计时器是一个更复杂的情况。如果提供格式的数据是社会安全号码之类的,那么它可能会简单得多。只要在保存的社会安全号码中需要"-",就不需要ValueProperty

输入错误

我还没有对向用户报告错误做任何事情,特别是当按下无效键时。只支持数字键和其他一些键,代码会忽略输入。通常,在无效输入时会发出哔哔声,但这可能会让一些用户反感。我曾考虑过可能提出一个接口来绑定到一个处理这些输入问题的命令。希望得到反馈。

历史

  • 2016/10/26:初始版本
  • 2016/11/03:修复了箭头键上下行为和光标选择的一些问题,并增加了时间格式的灵活性。
  • 2016/11/04:更新了行为,允许输入比正常允许的更大的数字,如果该数字左边的所有位置都是零(例如,如果您有“0:00:00”并在秒的十分位输入9,则值为“0:01:30:00”)。
  • 2016/12/13:修复了在第一个位置输入过大值时的一个小错误,并且还支持在第一个位置输入过大值,并将该值移到第一个允许的位置。
  • 2017/09/15:修复了Value DependencyProperty,使其默认值为Mode=TwoWay。还修复了光标位于文本末尾时的情况。
  • 2020/07/25:原始版本中存在一个严重错误,该错误无法从ViewModel属性绑定。这意味着没有反馈。此外,更改为双向绑定未包含在DependencyProperty定义中。将示例更改为通过ViewModel进行绑定,而不是直接绑定。
  • 2020/07/26:修复了由于舍入误差导致的一个问题,该问题发生在以十分之一秒为单位递增时,并且可能在显示小数秒的任何时候都导致问题。
  • 2020/08/07:改进了TimeSpan行为,并包含了一个双精度值行为。
  • 2020/08/09:修复了一个小错误,该错误阻止了使用箭头键移动到最右侧位置。
  • 2020/08/15:错误修复
© . All rights reserved.