使用 DependencyProperty 绑定 InputValidation 附加数据
Binding InputValidation additional data using DependencyProperty
这里缺少一些东西:我在数据模板中使用输入验证(有效)并且需要为验证提供额外的数据(无效)。使用 "hard-coded" 值(请参阅下面代码中的注释)有效,使用绑定则无效。
数据模板应用于列表视图中显示的元素(具有 "value"、"low_lim" 和 "high_lim" 属性)(这也有效)。我想我就是无法理解绑定。
可能是我对这个问题看得太久了。如果我是对的,这就是 DP 的某种 "visual-tree" 问题...
感谢任何帮助。
<DataTemplate DataType="{x:Type OptionA}" >
<TextBox Width="100" Validation.ErrorTemplate="{StaticResource validationErrorTemplate}">
<TextBox.Text>
<Binding Path="value" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<val:NumberOptionValidator>
<val:NumberOptionValidator.validRange>
<val:Range low_lim="{Binding low_lim}" high_lim="{Binding high_lim}"/>
<!--<val:Range low_lim="1" high_lim="2"/>-->
</val:NumberOptionValidator.validRange>
</val:NumberOptionValidator>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</DataTemplate>
我建议你在 OptionA 上实现 IDataErrorInfo 而不要使用验证规则。
根据 "When to use Validation Rules vs. IDataErrorInfo" 部分中的 https://blogs.msdn.microsoft.com/wpfsdk/2007/10/02/data-validation-in-3-5/,对于 "UI or business-layer validation logic":
使用验证规则:
验证逻辑与数据源分离,可以在控件之间重复使用。
使用IDataErrorInfo:验证逻辑更接近源。
这里有一些代码可以帮助您入门。
XAML:
<Window x:Class="WpfApplication33.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication33"
xmlns:val="clr-namespace:WpfApplication33"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:VM />
</Window.DataContext>
<Window.Resources>
<ControlTemplate x:Key="validationErrorTemplate">
<StackPanel Orientation="Horizontal">
<AdornedElementPlaceholder x:Name="textBox"/>
<TextBlock Text="{Binding [0].ErrorContent}" Foreground="Red"/>
</StackPanel>
</ControlTemplate>
<DataTemplate DataType="{x:Type local:OptionA}">
<TextBox Width="100"
Validation.ErrorTemplate="{StaticResource validationErrorTemplate}"
Text="{Binding value, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" />
</DataTemplate>
</Window.Resources>
<Grid>
<ListView ItemsSource="{Binding Items}" />
</Grid>
</Window>
CS:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
namespace WpfApplication33
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
public class VM
{
public List<OptionA> Items { get; set; }
public VM()
{
Items = new List<OptionA>() {
new OptionA() {value=1, low_lim = 0, high_lim = 2 },
new OptionA() {value=2, low_lim = 3, high_lim = 4 }, // too low
new OptionA() {value=3, low_lim = 2, high_lim = 5 },
new OptionA() {value=4, low_lim = 6, high_lim = 9 }, // too low
new OptionA() {value=5, low_lim = 0, high_lim = 4 }, // too high
};
}
}
public class OptionA : INotifyPropertyChanged, IDataErrorInfo
{
public event PropertyChangedEventHandler PropertyChanged;
private void OPC(string name) { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(name)); }
private int _value, _low_lim, _high_lim;
public int value { get { return _value; } set { if (_value != value) { _value = value; OPC("value"); } } }
public int low_lim { get { return _low_lim; } set { if (_low_lim != value) { _low_lim = value; OPC("low_lim"); } } }
public int high_lim { get { return _high_lim; } set { if (_high_lim != value) { _high_lim = value; OPC("high_lim"); } } }
#region IDataErrorInfo
public string Error
{
get
{
return null;
}
}
public string this[string columnName]
{
get
{
string err = null;
if (columnName == "value")
{
if (value < low_lim || value > high_lim)
err = string.Format("Value is out of the range of {0} to {1}.", low_lim, high_lim);
}
return err;
}
}
#endregion
}
}
截图:
这里缺少一些东西:我在数据模板中使用输入验证(有效)并且需要为验证提供额外的数据(无效)。使用 "hard-coded" 值(请参阅下面代码中的注释)有效,使用绑定则无效。
数据模板应用于列表视图中显示的元素(具有 "value"、"low_lim" 和 "high_lim" 属性)(这也有效)。我想我就是无法理解绑定。
可能是我对这个问题看得太久了。如果我是对的,这就是 DP 的某种 "visual-tree" 问题...
感谢任何帮助。
<DataTemplate DataType="{x:Type OptionA}" >
<TextBox Width="100" Validation.ErrorTemplate="{StaticResource validationErrorTemplate}">
<TextBox.Text>
<Binding Path="value" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<val:NumberOptionValidator>
<val:NumberOptionValidator.validRange>
<val:Range low_lim="{Binding low_lim}" high_lim="{Binding high_lim}"/>
<!--<val:Range low_lim="1" high_lim="2"/>-->
</val:NumberOptionValidator.validRange>
</val:NumberOptionValidator>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</DataTemplate>
我建议你在 OptionA 上实现 IDataErrorInfo 而不要使用验证规则。
根据 "When to use Validation Rules vs. IDataErrorInfo" 部分中的 https://blogs.msdn.microsoft.com/wpfsdk/2007/10/02/data-validation-in-3-5/,对于 "UI or business-layer validation logic":
使用验证规则: 验证逻辑与数据源分离,可以在控件之间重复使用。
使用IDataErrorInfo:验证逻辑更接近源。
这里有一些代码可以帮助您入门。
XAML:
<Window x:Class="WpfApplication33.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication33"
xmlns:val="clr-namespace:WpfApplication33"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:VM />
</Window.DataContext>
<Window.Resources>
<ControlTemplate x:Key="validationErrorTemplate">
<StackPanel Orientation="Horizontal">
<AdornedElementPlaceholder x:Name="textBox"/>
<TextBlock Text="{Binding [0].ErrorContent}" Foreground="Red"/>
</StackPanel>
</ControlTemplate>
<DataTemplate DataType="{x:Type local:OptionA}">
<TextBox Width="100"
Validation.ErrorTemplate="{StaticResource validationErrorTemplate}"
Text="{Binding value, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" />
</DataTemplate>
</Window.Resources>
<Grid>
<ListView ItemsSource="{Binding Items}" />
</Grid>
</Window>
CS:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
namespace WpfApplication33
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
public class VM
{
public List<OptionA> Items { get; set; }
public VM()
{
Items = new List<OptionA>() {
new OptionA() {value=1, low_lim = 0, high_lim = 2 },
new OptionA() {value=2, low_lim = 3, high_lim = 4 }, // too low
new OptionA() {value=3, low_lim = 2, high_lim = 5 },
new OptionA() {value=4, low_lim = 6, high_lim = 9 }, // too low
new OptionA() {value=5, low_lim = 0, high_lim = 4 }, // too high
};
}
}
public class OptionA : INotifyPropertyChanged, IDataErrorInfo
{
public event PropertyChangedEventHandler PropertyChanged;
private void OPC(string name) { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(name)); }
private int _value, _low_lim, _high_lim;
public int value { get { return _value; } set { if (_value != value) { _value = value; OPC("value"); } } }
public int low_lim { get { return _low_lim; } set { if (_low_lim != value) { _low_lim = value; OPC("low_lim"); } } }
public int high_lim { get { return _high_lim; } set { if (_high_lim != value) { _high_lim = value; OPC("high_lim"); } } }
#region IDataErrorInfo
public string Error
{
get
{
return null;
}
}
public string this[string columnName]
{
get
{
string err = null;
if (columnName == "value")
{
if (value < low_lim || value > high_lim)
err = string.Format("Value is out of the range of {0} to {1}.", low_lim, high_lim);
}
return err;
}
}
#endregion
}
}
截图: