一个极其简单的 WPF TimeSpan 控件






3.77/5 (3投票s)
使用滑块实现的TimeSpan控件。
引言
这篇文章的起因是我最近尝试创建一个允许你选择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
。
我将属性Seconds
、Minutes
和Hours
实现为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
或设置Seconds
、Minutes
、Hours
,我们需要确保UserControl
显示正确的值。通过使用_seconds
作为该属性的后备存储,我们使用setter保护来防止我们进入循环:如果设置了Value
,我们然后在OnValueChanged
回调中设置Seconds
属性。一旦设置了_seconds
,我们检查以确保我们不会再次设置它,然后再次设置Value
。
最后,我们将控件添加到我们的主项目窗口中,并使用数据绑定和INotifyPropertyChanged
将其连接起来以演示该控件。图像以灰色显示了UserControl
,而数据绑定的值在白色的MainWindow
中显示。
结束语
此控件的下一步是将其中的滑块替换为数字上下/微调器控件,并使其易于设置主题。
如果你觉得这有帮助,请在下面留言。如果你觉得这没有帮助,或者发现了错误,也请在下面留言说明原因!