为什么ItemsControl中的所有TextBox都使用最后动态添加的ValidationRule?

问题描述 投票:0回答:1

我有一个自定义文本框,当设置

MinMaxValidationRule
Min
属性时,它会添加
Max

public class TextBox2 : TextBox
{
    public static readonly DependencyProperty MinProperty = DependencyProperty.Register(
        nameof(Min), typeof(double?), typeof(TextBox2),
        new PropertyMetadata(null, MinMaxChangeCallback));

    public double? Min
    {
        get => (double?)GetValue(MinProperty);
        set => SetValue(MinProperty, value);
    }

    public static readonly DependencyProperty MaxProperty = DependencyProperty.Register(
        nameof(Max), typeof(double?), typeof(TextBox2),
        new PropertyMetadata(null, MinMaxChangeCallback));

    public double? Max
    {
        get => (double?)GetValue(MaxProperty);
        set => SetValue(MaxProperty, value);
    }

    private static void MinMaxChangeCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var textBox2 = (TextBox2)d;
        if (textBox2.Min == null || textBox2.Max == null)
            return;
        var binding = textBox2.GetBindingExpression(TextProperty);
        var validationRules = binding
            .ParentBinding
            .ValidationRules;
        validationRules.Clear();
        var minMaxValidationRule = new MinMaxValidationRule((double)textBox2.Min, (double)textBox2.Max)
        {
            ValidatesOnTargetUpdated = true
        };
        validationRules.Add(minMaxValidationRule);
        binding.UpdateTarget(); // triggers the validation rule
    }
}

验证规则定义如下

public class MinMaxValidationRule : ValidationRule
{
    public double Min { get; }
    public double Max { get; }

    public MinMaxValidationRule(double min, double max)
    {
        Min = min;
        Max = max;
    }

    public override ValidationResult Validate(object value, CultureInfo cultureInfo)
    {
        if (double.TryParse((string)value, out var d)
            && d <= Max
            && d >= Min)
            return ValidationResult.ValidResult;

        return new ValidationResult(false, $"Value must be in [{Min},{Max}]");
    }
}

我创建了一个 ItemsControl 来显示

ItemInfo
对象列表

<ItemsControl
    Margin="8"
    ItemsSource="{Binding ItemInfos}">
    <ItemsControl.ItemTemplate>
        <DataTemplate DataType="{x:Type testApp:ItemInfo}">
            <testApp:TextBox2
                Style="{StaticResource MaterialDesignOutlinedTextBox}"
                Width="400"
                Margin="12"
                Padding="8"
                Min="{Binding Min}"
                Max="{Binding Max}"
                Text="{Binding Value, UpdateSourceTrigger=PropertyChanged}" />
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>
public partial class MainWindow
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = this;
        ItemInfos = new()
        {
            new()
            {
                Min = 0,
                Max = 100,
                Value = 50
            },
            new()
            {
                Min = 200,
                Max = 300,
                Value = 250
            },
        };
    }

    public List<ItemInfo> ItemInfos { get; }
}

public class ItemInfo
{
    public double Min { get; set; }
    public double Max { get; set; }
    public double Value { get; set; }
}

在第一个 TextBox 中写入“2”时(将值从 50 更改为 502),WPF 使用第二个 TextBox 的验证规则,但我希望每个 TextBox 使用自己的 ValidationRule。

知道如何解决这个问题吗?

可以在这里找到示例项目。

c# wpf data-binding dependency-properties itemscontrol
1个回答
0
投票

这似乎是使用模板时的已知限制。 我的链接中问题的评论中建议了一种解决方法 科恩J : 使用 ValidationRule 的

Validate
的另一个覆盖,以便您可以访问该控件。

您可以按如下方式编辑 MinMaxValidationRule 类

public class MinMaxValidationRule : ValidationRule
{

    public MinMaxValidationRule()
    {
    }
    public override ValidationResult Validate(object value, CultureInfo cultureInfo)
    {
        //Must implement since the base method is abstract
        return ValidationResult.ValidResult;
    }

    public override ValidationResult Validate(object value, CultureInfo cultureInfo, BindingExpressionBase owner)
    {
        //This will be the actual validation (use the BindingExpressionBase to get acess to the TextBox2 control)
        if (owner.Target is TextBox2 textBox2)
        {
            if (double.TryParse((string)value, out var d)
            && d <= textBox2.Max
            && d >= textBox2.Min)
                return ValidationResult.ValidResult;

            return new ValidationResult(false, $"Value must be in [{textBox2.Min},{textBox2.Max}]");
        }
        else
        {
            return ValidationResult.ValidResult;
        }
        
    }
}

现在,由于我们读取了 Min 和 Max 属性的当前值,因此您不需要

MinMaxChangeCallback
,因此您只需在加载的事件上添加验证规则即可。

    public TextBox2()
    {
        int i = 0;
        i++;
        this.Loaded += TextBox2_Loaded;
    }

    private void TextBox2_Loaded(object sender, RoutedEventArgs e)
    {
        if (this.Min == null || this.Max == null)
            return;
        var binding = this.GetBindingExpression(TextProperty);
        var validationRules = binding
            .ParentBinding
            .ValidationRules;
        validationRules.Clear();
        var minMaxValidationRule = new MinMaxValidationRule()
        {
            ValidatesOnTargetUpdated = true
        };
        validationRules.Add(minMaxValidationRule);
        binding.UpdateTarget(); // triggers the validation rule
    }
© www.soinside.com 2019 - 2024. All rights reserved.