0

这是直接来自网站 ( http://jobijoy.blogspot.com/2007/10/time-picker-user-control.html )的 C# 代码,当有人询问 WPF 的 TimePicker 时,每个人都会参考这些代码,尽管我将它移动了有点更有条理。(请注意,如果您尝试运行此代码以使用它:您必须在显示小时、分钟和秒的 3 个网格上将此站点上的 XAML 代码从 KeyDown 更改为 PreviewKeyDown,并将 TextBlocks 更改为每个网格到文本框)

public partial class TimeControl : UserControl
{
    public TimeControl()
    {
        InitializeComponent();
    }

    public TimeSpan Value
    {
        get { return (TimeSpan)GetValue(ValueProperty); }
        set { SetValue(ValueProperty, value); }
    }
    public static readonly DependencyProperty ValueProperty =
    DependencyProperty.Register("Value", typeof(TimeSpan), typeof(TimeControl),
    new UIPropertyMetadata(DateTime.Now.TimeOfDay, new PropertyChangedCallback(OnValueChanged)));

    public int Hours
    {
        get { return (int)GetValue(HoursProperty); }
        set { SetValue(HoursProperty, value); }
    }
    public static readonly DependencyProperty HoursProperty =
    DependencyProperty.Register("Hours", typeof(int), typeof(TimeControl),
    new UIPropertyMetadata(0, new PropertyChangedCallback(OnTimeChanged)));

    public int Minutes
    {
        get { return (int)GetValue(MinutesProperty); }
        set { SetValue(MinutesProperty, value); }
    }
    public static readonly DependencyProperty MinutesProperty =
    DependencyProperty.Register("Minutes", typeof(int), typeof(TimeControl),
    new UIPropertyMetadata(0, new PropertyChangedCallback(OnTimeChanged)));

    public int Seconds
    {
        get { return (int)GetValue(SecondsProperty); }
        set { SetValue(SecondsProperty, value); }
    }
    public static readonly DependencyProperty SecondsProperty =
    DependencyProperty.Register("Seconds", typeof(int), typeof(TimeControl),
    new UIPropertyMetadata(0, new PropertyChangedCallback(OnTimeChanged)));

    private static void OnValueChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
    {
        TimeControl control = obj as TimeControl;
        control.Hours = ((TimeSpan)e.NewValue).Hours;
        control.Minutes = ((TimeSpan)e.NewValue).Minutes;
        control.Seconds = ((TimeSpan)e.NewValue).Seconds;
    }

    private static void OnTimeChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
    {
            TimeControl control = obj as TimeControl;
            control.Value = new TimeSpan(control.Hours, control.Minutes, control.Seconds);
    }

    private void Down(object sender, KeyEventArgs args)
    {
        switch (((Grid)sender).Name)
        {
            case "sec":
                if (args.Key == Key.Up)
                    this.Seconds++;
                if (args.Key == Key.Down)
                    this.Seconds--;
                break;

            case "min":
                if (args.Key == Key.Up)
                    this.Minutes++;
                if (args.Key == Key.Down)
                    this.Minutes--;
                break;

            case "hour":
                if (args.Key == Key.Up)
                    this.Hours++;
                if (args.Key == Key.Down)
                    this.Hours--;
                break;
        }
    }
}

我对依赖或绑定还不是很好,我只是在学习它,这就是我无法弄清楚的原因。但问题是:当 Minutes 或 Seconds 超过 59/-59 时,就会出现无限循环。我将解释它的流程(至少我在这里学到了很多东西!):

假设 TimeControl 对象位于 0:59:00,我们在关注分钟 TextBox 时按下向上键。因此,按照逻辑,它转到 PreviewKeyDown 事件,switch 语句将我们带到 this.Minutes++,它获取 Minutes 并看到 59,因此将分钟设置为 60。

这会触发 OnTimeChanged for Minutes,它会获取 Hours (0) Minutes (60) Seconds (0) 并将 Value 设置为该值。由于 Value 是 TimeSpan,因此它将其解释为 1:00:00,这很好。

因此,一旦设置好,它就会触发 OnValueChanged,它将 Hours 设置为 1,这会立即回调 OnTimeChanged for Hours。此时,它获取小时 (1) 分钟 (60) 秒 (0) 并将 Value 设置为该值(解释为 2:00:00)。

现在我们有一个无限循环,直到 Hours 变得太大并引发异常。了解如何解决它有点过头了。什么是“适当的”修复?我知道可以使用 switch 语句中的 if 语句,甚至 OnTimeChanged/OnValueChanged 方法来修复它,但我确信有更好的方法来处理依赖项。

4

2 回答 2

0

简单修复:更改它,使其首先重置分钟,然后更新小时。

// 免责声明:尚未阅读代码,所以我可能错了

于 2010-01-29T14:04:47.147 回答
0

如果它们没有不同,则无需设置属性,请尝试以下操作:

private static void OnValueChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
    TimeControl control = obj as TimeControl;
    var ts =  (TimeSpan)e.NewValue;
    if(ts.Hours != control.Hours) control.Hours = ts.Hours;
    if(ts.Minutes != control.Minutes) control.Minutes = ts.Minutes;
    if(ts.Seconds != control.Seconds) control.Seconds = ts.Seconds;
}

通常我会将这个逻辑放在设置器中,这是您在数据访问层中看到的常见内容......但我认为您的依赖调用仍然会在那里发生,所以最好在代码中的这个事件处理程序中进行。

于 2010-01-29T14:10:17.757 回答