最小值设置为 XAML 的绑定滑块值
Binding slider Value with Minimum set in XAML
我对 WPF 绑定行为有些困惑。我在代码隐藏中有一个滑块值绑定到 dependency 属性(仅作为示例)。滑块的最小值设置在 XAML 中。 window加载后,滑块的值设置为最小值,但是dependency属性仍然是默认值0。但是,正在调用滑块的 ValueChanged 回调,因此我实际上希望更新绑定。
所以我有以下 window,一个标签显示滑块值,另一个 属性 值绑定到。
<Window x:Class="SliderBinding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" x:Name="window">
<StackPanel DataContext="{Binding ElementName=window}">
<Slider Minimum="10" Maximum="100" x:Name="slider" Value="{Binding SliderValue, Mode=TwoWay}" ValueChanged="Slider_OnValueChanged"/>
<Label Content="{Binding Value, ElementName=slider}"></Label>
<Label Content="{Binding SliderValue}"></Label>
</StackPanel>
</Window>
和包含 依赖项的代码隐藏 属性 和当滑块值更改时仅打印跟踪消息的事件回调。
using System;
using System.Diagnostics;
using System.Windows;
namespace SliderBinding
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
/* Sorry, wrong code
public double SliderValue
{
get; set;
}*/
public double SliderValue
{
get { return (double)GetValue(SliderValueProperty); }
set { SetValue(SliderValueProperty, value); }
}
public static readonly DependencyProperty SliderValueProperty =
DependencyProperty.Register("SliderValue", typeof(double), typeof(MainWindow), new PropertyMetadata(default(double)));
private void Slider_OnValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
Trace.TraceInformation("Slider value changed to {0}", e.NewValue);
}
}
}
我一移动滑块,两个值就相等了。
我的问题是,当滑块值设置为最小值时,为什么 dependency 属性 在启动时没有更新?
以斜体字编辑。
这里发生的事情是滑块控件的 Value
属性 被 强制 因此它位于 [= 定义的范围内11=] 和 Maximum
属性。
加载 XAML 并连接绑定后,将读取您的 SliderValue
零,并且 WPF 会尝试设置 Value
属性。但是,因为零不在其他属性定义的范围内,所以提供的值被强制转换到该范围内,因此 Value
属性 保持为 10(当Minimum
属性 已设置)。因此 SliderValue
的值保持为 0,滑块的 Value
保持为 10,正如您从标签绑定中看到的那样。
当您拖动滑块滑块时,滑块的 Value
属性 会发生变化。这会导致双向绑定更新,但方向相反(从控件到您的依赖项 属性)。由于您的 DP 没有强制转换,因此它的值被设置为滑块的值。从那时起,两者就同步了。
您可以通过向 window 添加一个按钮来验证此行为,然后在 OnClick
处理程序中将 SliderValue
设置为滑块范围之外的内容。您会看到 SliderValue
发生变化,但由于强制,滑块的值被设置为其最小值或最大值。
值强制是可以为依赖项配置的功能之一 属性。阅读更多相关信息 at MSDN。
如果您查看 Slider 的 ValueProperty 的源代码,在其 DP 注册中提供了 CoerceValueCallback
,确保 value 始终保持不变在滑块的最小值和最大值范围内。因此,每当绑定源 属性 传递一些不在范围内的值时,它会根据从源传递的值传递 min/max 值。 (但它不会设置回值来源 属性).
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register(
"Value",
typeof(double),
typeof(RangeBase),
new FrameworkPropertyMetadata(
0.0d,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault |
FrameworkPropertyMetadataOptions.Journal,
new PropertyChangedCallback(OnValueChanged),
new CoerceValueCallback(ConstrainToRange)),
new ValidateValueCallback(IsValidDoubleValue));
internal static object ConstrainToRange(DependencyObject d, object value)
{
RangeBase ctrl = (RangeBase) d;
double min = ctrl.Minimum;
double v = (double) value;
if (v < min)
{
return min;
}
double max = ctrl.Maximum;
if (v > max)
{
return max;
}
return value;
}
所以,您可以做的是在 CoerceValueCallback 中为 SliderValue 属性 使用相同的 逻辑,这样它就始终与滑块值同步,但为此您必须从值更改事件中手动强制滑块值。
public static readonly DependencyProperty SliderValueProperty =
DependencyProperty.Register("SliderValue", typeof(double), typeof(MainWindow),
new PropertyMetadata(default(double), null, CoerceSliderValue));
private static object CoerceSliderValue(DependencyObject d, object value)
{
// Of course good idea would be to be have min/max as CLR properies
// in your class and bind slider with those values.
// So, that you can have refer to those values directly here.
double min = ((MainWindow)d).slider.Minimum;
double max = ((MainWindow)d).slider.Maximum;
double passedValue = (double)value;
if (passedValue < min)
{
return min;
}
else if (passedValue > max)
{
return max;
}
return passedValue;
}
private void Slider_OnValueChanged(object sender,
RoutedPropertyChangedEventArgs<double> e)
{
CoerceValue(SliderValueProperty);
}
我对 WPF 绑定行为有些困惑。我在代码隐藏中有一个滑块值绑定到 dependency 属性(仅作为示例)。滑块的最小值设置在 XAML 中。 window加载后,滑块的值设置为最小值,但是dependency属性仍然是默认值0。但是,正在调用滑块的 ValueChanged 回调,因此我实际上希望更新绑定。
所以我有以下 window,一个标签显示滑块值,另一个 属性 值绑定到。
<Window x:Class="SliderBinding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" x:Name="window">
<StackPanel DataContext="{Binding ElementName=window}">
<Slider Minimum="10" Maximum="100" x:Name="slider" Value="{Binding SliderValue, Mode=TwoWay}" ValueChanged="Slider_OnValueChanged"/>
<Label Content="{Binding Value, ElementName=slider}"></Label>
<Label Content="{Binding SliderValue}"></Label>
</StackPanel>
</Window>
和包含 依赖项的代码隐藏 属性 和当滑块值更改时仅打印跟踪消息的事件回调。
using System;
using System.Diagnostics;
using System.Windows;
namespace SliderBinding
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
/* Sorry, wrong code
public double SliderValue
{
get; set;
}*/
public double SliderValue
{
get { return (double)GetValue(SliderValueProperty); }
set { SetValue(SliderValueProperty, value); }
}
public static readonly DependencyProperty SliderValueProperty =
DependencyProperty.Register("SliderValue", typeof(double), typeof(MainWindow), new PropertyMetadata(default(double)));
private void Slider_OnValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
Trace.TraceInformation("Slider value changed to {0}", e.NewValue);
}
}
}
我一移动滑块,两个值就相等了。
我的问题是,当滑块值设置为最小值时,为什么 dependency 属性 在启动时没有更新?
这里发生的事情是滑块控件的 Value
属性 被 强制 因此它位于 [= 定义的范围内11=] 和 Maximum
属性。
加载 XAML 并连接绑定后,将读取您的 SliderValue
零,并且 WPF 会尝试设置 Value
属性。但是,因为零不在其他属性定义的范围内,所以提供的值被强制转换到该范围内,因此 Value
属性 保持为 10(当Minimum
属性 已设置)。因此 SliderValue
的值保持为 0,滑块的 Value
保持为 10,正如您从标签绑定中看到的那样。
当您拖动滑块滑块时,滑块的 Value
属性 会发生变化。这会导致双向绑定更新,但方向相反(从控件到您的依赖项 属性)。由于您的 DP 没有强制转换,因此它的值被设置为滑块的值。从那时起,两者就同步了。
您可以通过向 window 添加一个按钮来验证此行为,然后在 OnClick
处理程序中将 SliderValue
设置为滑块范围之外的内容。您会看到 SliderValue
发生变化,但由于强制,滑块的值被设置为其最小值或最大值。
值强制是可以为依赖项配置的功能之一 属性。阅读更多相关信息 at MSDN。
如果您查看 Slider 的 ValueProperty 的源代码,在其 DP 注册中提供了 CoerceValueCallback
,确保 value 始终保持不变在滑块的最小值和最大值范围内。因此,每当绑定源 属性 传递一些不在范围内的值时,它会根据从源传递的值传递 min/max 值。 (但它不会设置回值来源 属性).
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register(
"Value",
typeof(double),
typeof(RangeBase),
new FrameworkPropertyMetadata(
0.0d,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault |
FrameworkPropertyMetadataOptions.Journal,
new PropertyChangedCallback(OnValueChanged),
new CoerceValueCallback(ConstrainToRange)),
new ValidateValueCallback(IsValidDoubleValue));
internal static object ConstrainToRange(DependencyObject d, object value)
{
RangeBase ctrl = (RangeBase) d;
double min = ctrl.Minimum;
double v = (double) value;
if (v < min)
{
return min;
}
double max = ctrl.Maximum;
if (v > max)
{
return max;
}
return value;
}
所以,您可以做的是在 CoerceValueCallback 中为 SliderValue 属性 使用相同的 逻辑,这样它就始终与滑块值同步,但为此您必须从值更改事件中手动强制滑块值。
public static readonly DependencyProperty SliderValueProperty =
DependencyProperty.Register("SliderValue", typeof(double), typeof(MainWindow),
new PropertyMetadata(default(double), null, CoerceSliderValue));
private static object CoerceSliderValue(DependencyObject d, object value)
{
// Of course good idea would be to be have min/max as CLR properies
// in your class and bind slider with those values.
// So, that you can have refer to those values directly here.
double min = ((MainWindow)d).slider.Minimum;
double max = ((MainWindow)d).slider.Maximum;
double passedValue = (double)value;
if (passedValue < min)
{
return min;
}
else if (passedValue > max)
{
return max;
}
return passedValue;
}
private void Slider_OnValueChanged(object sender,
RoutedPropertyChangedEventArgs<double> e)
{
CoerceValue(SliderValueProperty);
}