6.4 Binding对数据的转换与校验
Binding用于数据有效性校验的关卡是它的ValidationRules属性,用于数据类型转换的关卡是它的Converter属性。
6.4.1 Binding的数据校验
ValidationRule类是个抽象类,在使用的时候我们需要创建他的派生类并实现它的Validate方法。Validate方法的返回值是ValidationRule类型对象,如果校验通过,就把validationResult对象的IsValid属性设为True。反之,需要把IsValid属性设为false并为其ErrorContent属性设置一个合适的消息内容(一般是个字符串)。
using System.Windows.Controls;
namespace FirstWpfApplication.SourceRules
{
class RangeValidationRule : ValidationRule
{
public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
{
double b = 0;
if (double.TryParse(value.ToString(), out b))
if (b >= 0 && b <= 100)
return new ValidationResult(true, null);
return new ValidationResult(false, "Validation failed...");
}
}
}
<Grid x:Name="g1" Background="Orange" Margin="10">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBox x:Name="textBox1" Margin="5" Grid.Row="0"/>
<Slider x:Name="slider1" Margin="6" Maximum="110" Grid.Row="1"/>
</Grid>
Binding只在Target被外部方法更新时检索数据,或者说当来自Source的数据也有可能出现问题时,我们就需要检索条件的ValidatesOnTargetUpdated属性设为true。
首先创建Binding时要把Binding对象的NotifyOnValidationError属性设为true,这样当数据校验失败的时候 binding会像报警器一样发出一个信号,这个信号会以Binding对象的Target为起点在UI元素树上传播。信号每到达一点,如果这个节点上设置有对这种信号的侦听器(事件处理器),那么这个侦听器就会被触发用以处理这个信号。信号处理完后,程序员还可以选择让信号继续向下传播还是就此终止——这就是路由事件,信号在UI元素树上的传递过程称为路由(Route)。
public MainWindow()
{
InitializeComponent();
var source=new Binding("Value")
{
ElementName = "slider1",
UpdateSourceTrigger=UpdateSourceTrigger.PropertyChanged
};
var rar = new RangeValidationRule();
rar.ValidatesOnTargetUpdated = true; //1#
source.ValidationRules.Add(rar);
source.NotifyOnValidationError = true; //2#
textBox1.SetBinding(TextBox.TextProperty, source);
//5#路由事件
this.textBox1.AddHandler(Validation.ErrorEvent, new RoutedEventHandler(this.ValidationError)); //3# 加载路由委托
}
void ValidationError(object sender, RoutedEventArgs e)
{
foreach (var error in Validation.GetErrors(this.textBox1)) //4#
this.textBox1.ToolTip = error.ErrorContent.ToString();
}
}
XAML实现:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
listBox1.ItemsSource = LoadData().DefaultView;
}
public DataTable LoadData()
{
DataTable dt = new DataTable();
string connStr = "Data Source=.;Initial Catalog=mydb;Integrated Security=True";
SqlCommand sqlCmd = new SqlCommand("select * from dbo.Person", new SqlConnection(connStr));
SqlDataAdapter adapter = new SqlDataAdapter(sqlCmd);
adapter.Fill(dt);
return dt;
}
}
public class NameValidationRule : ValidationRule
{
public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
{
string name = value as string;
if (string.IsNullOrEmpty(name) || name.Length > 10)
return new ValidationResult(false, "bad name...");
else
return new ValidationResult(true, null);
}
}
<ListBox x:Name="listBox1">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Label Content="{Binding Path=ID}" Width="40"/>
<TextBox Width="100">
<TextBox.Text>
<Binding>
<Binding.Path>Name</Binding.Path>
<Binding.NotifyOnValidationError>True</Binding.NotifyOnValidationError>
<Binding.ValidationRules>
<proj:NameValidationRule ValidatesOnTargetUpdated="True"/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
<TextBox Text="{Binding Path=Age}" Width="100"/>
<TextBox Text="{Binding Path=Comment}" Width="120"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
XAML实现:
<StackPanel>
<TextBox x:Name="textBox2" Margin="5"/>
<TextBox x:Name="textBox1" Margin="5"/>
<TextBox x:Name="textBox3" Margin="5">
<TextBox.Text>
<Binding ElementName="textBox1" UpdateSourceTrigger="PropertyChanged">
<Binding.Path>.Text</Binding.Path>
<Binding.NotifyOnValidationError>True</Binding.NotifyOnValidationError>
<Binding.ValidationRules>
<sys:ValidateLengthError ValidatesOnTargetUpdated="True"/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</StackPanel>
public MainWindow()
{
InitializeComponent();
textBox1.TextChanged += new TextChangedEventHandler(this.ValidationError);
}
void ValidationError(object sender, RoutedEventArgs e)
{
this.textBox2.Text = "";
foreach (var error in Validation.GetErrors(this.textBox3))
{
this.textBox1.ToolTip = error.ErrorContent.ToString();
this.textBox2.Text = error.ErrorContent.ToString();
}
}
class ValidateLengthError : ValidationRule
{
public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
{
if (value is string)
{
string str = value as string;
if (str.Length > 5)
return new ValidationResult(false, "more than 5");
else
return new ValidationResult(true, null);
}
else
return new ValidationResult(false, "bad type");
}
}