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

一个极其简单的 WPF TimeSpan 控件

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.77/5 (3投票s)

2012年1月11日

CPOL

3分钟阅读

viewsIcon

27248

downloadIcon

501

使用滑块实现的TimeSpan控件。

UserControlExample

引言

这篇文章的起因是我最近尝试创建一个允许你选择TimeSpan的控件。本文面向初学者,快速介绍了WPF的一些常见方面。本文将从基本的WPF控件构建一个UserControl。WPF (4.0) 缺乏数字上下控件(例如,参见StackOverflow上的这个问题),所以我们将使用滑块。

像往常一样,请下载源代码以更详细地了解发生了什么。

一些基础知识

在任何XAML中,我都会给根元素添加一个名称,当然,叫做root

<UserControl x:Class="Btl.Controls.ShortTimeSpanControl" x:Name="root" >

这使我们可以在数据绑定时轻松引用相关根元素的属性。

此外,我进一步使用了WPF的数据绑定特性,这样我就不必在我的XAML中创建不必要的事件,并将它们连接到我的代码隐藏中,因此我的UserControl实现了INotifyPropertyChanged

控件:ShortTimeSpanControl

根据我的要求,我只对创建一个从00:00:00到23:59:59的正TimeSpan感兴趣。由于出于只有微软才知道的原因,默认的WPF工具包中省略了数字上下控件或微调器,并且用户通常容易出错,因此控制数字的最简单方法是使用Slider

我将属性SecondsMinutesHours实现为int,如果它们的值发生更改,则引发PropertyChanged。然后,这允许我将每个滑块绑定到Seconds/Minutes/Hours属性。稍后我们将重新讨论这些内容。

使用DependencyProperty

我们希望允许ShortTimeSpanControl的消费者只使用一个返回TimeSpan的属性。因此,虽然我们已经实现了Seconds/Minutes/Hours属性,但我们希望实现一个DependencyProperty,允许控件的消费者绑定到它。这通过注册名为Value的属性来实现

public partial class ShortTimeSpanControl : UserControl, INotifyPropertyChanged
{        
    public TimeSpan Value
    {
        get { return (TimeSpan)GetValue(ValueProperty); }
        set { SetValue(ValueProperty, value); }
    }
    
    private static readonly DependencyProperty ValueProperty =
        DependencyProperty.Register("Value", typeof(TimeSpan), typeof(ShortTimeSpanControl),
        new FrameworkPropertyMetadata(TimeSpan.Zero, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnValueChanged));
    
    //......
}

值得注意的是,Value是一个非常常见的属性名称,但对于此示例来说已经足够了,并且希望是显而易见的。我们在这里做了一些值得解释的事情

  • 我们使用将属性后缀为Property的标准约定。
  • 我们使用默认值 (TimeSpan.Zero) 注册该属性。
  • Value属性是简单定义的;我们没有在setter中添加任何内容:如果在XAML中设置了该属性,WPF会直接调用SetValue,并且在你的setter中的任何自定义代码都**不会被调用**。
  • 我们声明我们的属性**默认双向绑定**。

最后一点值得重申:在你可能在网络上找到的大多数简单示例中,你可能会看到这个

private static readonly DependencyProperty ValueProperty =
    DependencyProperty.Register("Value", typeof(TimeSpan), typeof(ShortTimeSpanControl),
    new PropertyMetadata(TimeSpan.Zero, OnValueChanged));

默认情况下,这意味着绑定是**单向的**,即,如果消费者绑定到你的DependencyProperty,他们必须在他们的绑定/XAML标记中添加Mode=TwoWay。如果有人知道为什么这是默认值,请在下面留言,因为我发现默认WPF控件上的所有属性都是双向的有点奇怪。

整合

看看三个滑块中的一个

<Slider Name="SecondsSlider"
    Grid.Row="2"
    IsDirectionReversed="True"
    LargeChange="5"
    Maximum="59"
    SmallChange="1"
    Value="{Binding ElementName=root, Path=Seconds}" />

我们将Slider绑定到我们root元素的Seconds属性,即我们的UserControl代码隐藏

private int _seconds;
public int Seconds
{
    get
    {
        return _seconds;
    }
    set
    {
        if (value == _seconds)
            return;
        _seconds = value;
        RaisePropertyChanged("Seconds");
        var v = Value;
        Value = new TimeSpan(v.Hours, v.Minutes, _seconds);
    }
}

private static void OnValueChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
    ShortTimeSpanControl control = obj as ShortTimeSpanControl;
    var newValue = (TimeSpan)e.NewValue;

    //  we update the Control properties to set the sliders.
    control.Seconds = newValue.Seconds;
    control.Minutes = newValue.Minutes;
    control.Hours = newValue.Hours;
}

由于我们现在有两种更新控件的方式:通过设置Value或设置SecondsMinutesHours,我们需要确保UserControl显示正确的值。通过使用_seconds作为该属性的后备存储,我们使用setter保护来防止我们进入循环:如果设置了Value,我们然后在OnValueChanged回调中设置Seconds属性。一旦设置了_seconds,我们检查以确保我们不会再次设置它,然后再次设置Value

最后,我们将控件添加到我们的主项目窗口中,并使用数据绑定和INotifyPropertyChanged将其连接起来以演示该控件。图像以灰色显示了UserControl,而数据绑定的值在白色的MainWindow中显示。

结束语

此控件的下一步是将其中的滑块替换为数字上下/微调器控件,并使其易于设置主题。

如果你觉得这有帮助,请在下面留言。如果你觉得这没有帮助,或者发现了错误,也请在下面留言说明原因!

© . All rights reserved.