c# – 在多级wpf视图中验证子记录
可以说我有一组这样的模型类:
public class Person { public string Name { get; set; } public ObservableCollection<Job> Jobs { get; private set; } } public class Job { public string Title { get; set; } public DateTime? Start { get; set; } public DateTime? End { get; set; } } 我像这样设置我的xaml来显示一个人的列表和一个视图的所有细节: <StackPanel Orientation="Horizontal"> <ListView ItemsSource="{Binding}" Width="200" DisplayMemberPath="Name" IsSynchronizedWithCurrentItem="True" /> <DockPanel Width="200" Margin="10,0"> <TextBox Text="{Binding Name}" DockPanel.Dock="Top" Margin="0,10"/> <ListView ItemsSource="{Binding Jobs}" Name="_jobList" DisplayMemberPath="Title" IsSynchronizedWithCurrentItem="True"/> </DockPanel> <StackPanel Width="200" DataContext="{Binding ElementName=_jobList,Path=SelectedItem}"> <TextBox Text="{Binding Title}"/> <DatePicker SelectedDate="{Binding Start}"/> <DatePicker SelectedDate="{Binding End}"/> </StackPanel> </StackPanel> 整个Window的DataContext是People的ObservbleCollection.这似乎运作得相当好.我现在想添加一些验证,我不知道从哪里开始. 我想验证作业字段,以便用户无法选择另一个作业(或人员),如果标题为空或同一个人内的副本,或者日期不按顺序.此外,如果名称为空,则无法更改Person. 我已经阅读了一些关于WPF验证的内容,但是没有找到任何明确的解决方案.做这样的事情的最佳做法是什么? 另外,我的绑定是否正如我在最后一个面板上设置DataContext那样?它有效,但感觉有点粗略.另一种方法是为CollectionDataSources声明资源,我也不能很容易地想出这个方法. 解决方法
对于验证部分,您可以从这里开始:
http://codeblitz.wordpress.com/2009/05/08/wpf-validation-made-easy-with-idataerrorinfo/
理解了文章后,您可以像这样修改类: 工作类: public class Job : IDataErrorInfo { public string Title { get; set; } public DateTime? Start { get; set; } public DateTime? End { get; set; } public string Error { get { throw new NotImplementedException(); } } public string this[string columnName] { get { string result = null; if (columnName == "Title") { if (string.IsNullOrEmpty(Title)) result = "Please enter a Title "; } if (columnName == "Start") { if (Start == null) result = "Please enter a Start Date"; else if (Start > End) { result = "Start Date must be less than end date"; } } return result; } } } //////////假设我们使用一个名为MainWindow的窗口为你的xaml代码看起来像 <Window x:Class="WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525" x:Name="mainWindow"> <Window.Resources> <ControlTemplate x:Key="errorTemplate"> <DockPanel LastChildFill="true"> <Border Background="Red" DockPanel.Dock="right" Margin="5,0" Width="20" Height="20" CornerRadius="10" ToolTip="{Binding ElementName=customAdorner,Path=AdornedElement.(Validation.Errors)[0].ErrorContent}"> <TextBlock Text="!" VerticalAlignment="center" HorizontalAlignment="center" FontWeight="Bold" Foreground="white"> </TextBlock> </Border> <AdornedElementPlaceholder Name="customAdorner" VerticalAlignment="Center" > <Border BorderBrush="red" BorderThickness="1" /> </AdornedElementPlaceholder> </DockPanel> </ControlTemplate> </Window.Resources> <Grid> <StackPanel Orientation="Horizontal"> <ListView ItemsSource="{Binding}" Width="200" DisplayMemberPath="Name" IsSynchronizedWithCurrentItem="True" IsEnabled="{Binding ElementName=mainWindow,Path=FormHasNoNoErrors}"/> <DockPanel Width="200" Margin="10,0"> <TextBox Text="{Binding Name}" DockPanel.Dock="Top" Margin="0,10"/> <ListView ItemsSource="{Binding Jobs}" Name="_jobList" DisplayMemberPath="Title" IsSynchronizedWithCurrentItem="True" IsEnabled="{Binding ElementName=mainWindow,Path=FormHasNoNoErrors}" /> </DockPanel> <StackPanel Width="200" DataContext="{Binding ElementName=_jobList,Path=SelectedItem}"> <TextBox Text="{Binding Title,ValidatesOnDataErrors=true,NotifyOnValidationError=true}" Validation.Error="Validation_OnError" Validation.ErrorTemplate="{StaticResource errorTemplate}"/> <DatePicker SelectedDate="{Binding Start,NotifyOnValidationError=true}" Validation.Error="Validation_OnError" Validation.ErrorTemplate="{StaticResource errorTemplate}"/> <DatePicker SelectedDate="{Binding End}"/> </StackPanel> </StackPanel> </Grid> </Window> ////////MainWindow.xaml.cs public partial class MainWindow : Window,INotifyPropertyChanged { public MainWindow() { var jobs1 = new ObservableCollection<Job>() { new Job() {Start = DateTime.Now,End = DateTime.Now.AddDays(1),Title = "Physical Enginer"},new Job() {Start = DateTime.Now,Title = "Mechanic"} }; var jobs2 = new ObservableCollection<Job>() { new Job() {Start = DateTime.Now,Title = "Doctor"},Title = "Programmer"} }; var personList = new ObservableCollection<Person>() { new Person() {Name = "john",Jobs = jobs1},new Person() {Name="alan",Jobs=jobs2} }; this.DataContext = personList; InitializeComponent(); } private void Validation_OnError(object sender,ValidationErrorEventArgs e) { if (e.Action == ValidationErrorEventAction.Added) NoOfErrorsOnScreen++; else NoOfErrorsOnScreen--; } public bool FormHasNoNoErrors { get { return _formHasNoErrors; } set { if (_formHasNoErrors != value) { _formHasNoErrors = value; PropertyChanged(this,new PropertyChangedEventArgs("FormHasNoErrors")); } } } public int NoOfErrorsOnScreen { get { return _noOfErrorsOnScreen; } set { _noOfErrorsOnScreen = value; FormHasNoNoErrors = _noOfErrorsOnScreen == 0 ? true : false; } } private int _noOfErrorsOnScreen = 0; private bool _formHasNoErrors = true; public event PropertyChangedEventHandler PropertyChanged = delegate {}; } //////////////////////
一个简单的解决方案是,如果表单有错误(这就是我在上面的代码中所做的那样),则禁用列表框,并在没有错误时启用它们.你也应该在它们上面放一个带有工具提示的漂亮红色边框,向用户解释为什么他不能再选择了. 为了验证标题是否在同一个人中是重复的,您可以将上面的验证机制扩展为具有接收Person的ValidationService类,并查看具有该作业的人是否还有另一个具有相同名称的人. public class Person:IDataErrorInfo public string this[string columnName] { get { //look inside person to see if it has two jobs with the same title string result = ValidationService.GetPersonErrors(this); return result; } ... }
它不是MVVM的精神,如果你所做的不仅仅是一个小测试项目,你应该尝试学习MVVM(你可以从这里开始:http://fernandomachadopirizen.wordpress.com/2010/06/10/a-simple-introduction-to-the-model-view-viewmodel-pattern-for-building-silverlight-and-windows-presentation-foundation-applications/) 然后你不会直接绑定到一个人列表,而是你会绑定一些MainWindowViewModel类.此MainWindowViewModel类将包含人员列表,您将绑定到该列表. 基本上是这样的: <StackPanel Orientation="Horizontal"> <ListView ItemsSource="{Binding PersonList}" SelectedItem="{Binding CurrentPerson,Mode=TwoWay}" Width="200" DisplayMemberPath="Name" IsSynchronizedWithCurrentItem="True" IsEnabled="{Binding ElementName=mainWindow,Path=FormHasNoNoErrors}"/> <DockPanel Width="200" Margin="10,10"/> <ListView ItemsSource="{Binding Jobs}" SelectedItem="{Binding CurrentJob,Mode=TwoWay}",DisplayMemberPath="Title" IsSynchronizedWithCurrentItem="True" /> </DockPanel> <StackPanel Width="200"> <TextBox Text="{Binding CurrentJob.Title}"/> ..... </StackPanel> </StackPanel> 和你的MainWindowViewModel: public class MainWindowViewModel : { ... //Public Properties public ObservableCollection<Person> PersonList .... public Job CurrentJob .... .... } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |