绑定滑块值,在XAML中设置最小值

时间:2015-02-06 12:32:37

标签: c# wpf binding

我对WPF绑定行为感到有些困惑。我在代码隐藏中绑定了一个依赖属性的滑块值(仅用于示例)。滑块的最小值在XAML中设置。加载窗口后,滑块的值设置为最小值,但依赖属性仍然具有默认值0.但是,正在调用滑块的ValueChanged回调,所以我实际上会期望更新绑定。

所以我有以下窗口,一个标签显示滑块值,另一个标签显示值绑定的属性。

    <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);
            }
        }
    }

一旦移动滑块,两个值都相等。

我的问题是,当滑块值设置为最小值时,为什么依赖属性在启动时不会更新?

see here 以斜体编辑。

2 个答案:

答案 0 :(得分:2)

这里发生的是滑块控件的Value属性强制,因此它位于MinimumMaximum定义的范围内属性。

当加载XAML并且绑定绑定时,将读取SliderValue零,并且WPF尝试设置Value属性。但是,因为零不在其他属性定义的范围内,所以提供的值会被强制转换为该范围,因此Value属性保持为10({{1}时给出的值}属性已设置)。因此,Minimum的值保持为0,滑块的SliderValue保持为10,正如您可以从标签绑定中看到的那样。

拖动滑块时,滑块的Value属性会发生变化。这会导致双向绑定更新,但方向相反(从控件到依赖属性)。由于您的DP没有强制,其值将设置为滑块的值。从那时起,两者是同步的。

您可以通过向窗口添加按钮来验证此行为,然后在Value处理程序中将OnClick设置为滑块范围之外的内容。您将看到SliderValue更改,但由于强制,滑块的值将设置为其最小值或最大值。

值强制是可以为依赖项属性配置的功能之一。阅读更多相关信息at MSDN

答案 1 :(得分:1)

如果您查看Slider的ValueProperty的源代码,则会在其DP注册中提供 CoerceValueCallback ,以确保值始终保持在最小值和最大值范围内滑块的值。因此,每当绑定的source属性传递一些不在范围内的值时,它会根据源的传递值传递最小/最大值。(但它不会将值设置为source属性)。

    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 for 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);
}