我有一个自定义文本框,当设置
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。
知道如何解决这个问题吗?
可以在这里找到示例项目。
这似乎是使用模板时的已知限制。 我的链接中问题的评论中建议了一种解决方法 科恩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
}