c# - 为什么我的数据绑定(bind)看到的是真实值而不是强制值?

标签 c# wpf xaml data-binding dependency-properties

我正在写一个真正的 NumericUpDown/Spinner控件作为学习自定义控件创作的练习。我有我正在寻找的大部分行为,包括适当的强制。然而,我的一项测试发现了一个缺陷。

我的控件有 3 个依赖属性:Value , MaximumValue , 和 MinimumValue .我使用强制来确保 Value保持在最小值和最大值之间,包括在内。例如。:

// In NumericUpDown.cs

public static readonly DependencyProperty ValueProperty =
    DependencyProperty.Register("Value", typeof(int), typeof(NumericUpDown), 
    new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.Journal, HandleValueChanged, HandleCoerceValue));

[Localizability(LocalizationCategory.Text)]
public int Value
{
    get { return (int)this.GetValue(ValueProperty); }
    set { this.SetCurrentValue(ValueProperty, value); }
}

private static object HandleCoerceValue(DependencyObject d, object baseValue)
{
    NumericUpDown o = (NumericUpDown)d;
    var v = (int)baseValue;

    if (v < o.MinimumValue) v = o.MinimumValue;
    if (v > o.MaximumValue) v = o.MaximumValue;

    return v;
}

我的测试只是为了确保数据绑定(bind)按我的预期工作。我创建了一个默认的 wpf windows 应用程序并输入了以下 xaml:
<Window x:Class="WpfApplication.MainWindow" x:Name="This"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:nud="clr-namespace:WpfCustomControlLibrary;assembly=WpfCustomControlLibrary"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        <nud:NumericUpDown Value="{Binding ElementName=This, Path=NumberValue}"/>
        <TextBox Grid.Row="1" Text="{Binding ElementName=This, Path=NumberValue, Mode=OneWay}" />
    </Grid>
</Window>

使用非常简单的代码隐藏:
public partial class MainWindow : Window
{
    public int NumberValue
    {
        get { return (int)GetValue(NumberValueProperty); }
        set { SetCurrentValue(NumberValueProperty, value); }
    }

    // Using a DependencyProperty as the backing store for NumberValue.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty NumberValueProperty =
        DependencyProperty.Register("NumberValue", typeof(int), typeof(MainWindow), new UIPropertyMetadata(0));     

    public MainWindow()
    {
        InitializeComponent();
    }
}

(我省略了控件演示的 xaml)

现在,如果我运行它,我会看到 NumericUpDown 中的值适本地反射(reflect)在文本框中,但如果我输入超出范围的值,则超出范围的值将显示在测试文本框中,而 NumericUpDown显示正确的值。

这就是强制值(value)观应该如何行动吗?在 ui 中强制它很好,但我希望强制值也能贯穿数据绑定(bind)。

最佳答案

哇,这很令人惊讶。当您在依赖属性上设置值时,绑定(bind)表达式会在值强制运行之前更新!

如果您查看 Reflector 中的 DependencyObject.SetValueCommon,您可以在方法的中途看到对 Expression.SetValue 的调用。在绑定(bind)已经更新之后,对将调用 CoerceValueCallback 的 UpdateEffectiveValue 的调用在最后。

您也可以在框架类上看到这一点。在新的 WPF 应用程序中,添加以下 XAML:

<StackPanel>
    <Slider Name="Slider" Minimum="10" Maximum="20" Value="{Binding Value, 
        RelativeSource={RelativeSource AncestorType=Window}}"/>
    <Button Click="SetInvalid_Click">Set Invalid</Button>
</StackPanel>

和以下代码:
private void SetInvalid_Click(object sender, RoutedEventArgs e)
{
    var before = this.Value;
    var sliderBefore = Slider.Value;
    Slider.Value = -1;
    var after = this.Value;
    var sliderAfter = Slider.Value;
    MessageBox.Show(string.Format("Value changed from {0} to {1}; " + 
        "Slider changed from {2} to {3}", 
        before, after, sliderBefore, sliderAfter));
}

public int Value { get; set; }

如果拖动 slider 然后单击按钮,您将收到类似“值从 11 更改为 -1; slider 从 11 更改为 10”的消息。

关于c# - 为什么我的数据绑定(bind)看到的是真实值而不是强制值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50159187/

相关文章:

c# - 程序集内的多个系统

c# - 切换枪支

wpf - 使 DatePicker 在禁用时更易于阅读

c# - 双向绑定(bind)简单的 WPF TextBox 文本

java - SOAP 信封与 Java 客户端调用 .NET SOAP Web 服务的区别

c# - 需要 .Net 的公式解释器

c# - WPF DataGrid 的单元格颜色恢复为默认值

wpf - 退出休眠模式时 WPF 应用程序失败

c# - 如何按wpf中的后续数字对数组列表进行排序?

c# - WPF 列表框项位图图像更新