For the verification part, you can start here: http://codeblitz.wordpress.com/2009/05/08/wpf-validation-made-easy-with-idataerrorinfo/
After you understand the article, you can change your classes as follows: For example, add a non-empty title confirmation for the job title and start date:
Class of work:
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; } } }
////////// Suppose we used the MainWindow window for your xaml code, it would look like this: 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,0,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,0,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, ValidatesOnDataErrors=true, 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, End = DateTime.Now.AddDays(1), Title = "Mechanic"} }; var jobs2 = new ObservableCollection<Job>() { new Job() {Start = DateTime.Now, End = DateTime.Now.AddDays(1), Title = "Doctor"}, new Job() {Start = DateTime.Now, End = DateTime.Now.AddDays(1), 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 {}; }
//////////////////////
I would like to confirm the work of the fields so that the user cannot select another task (or person) if the title is empty or a duplicate within the same person, or if the dates are missing order. In addition, the face cannot be changed if the name is empty.
One simple solution is to disable the lists if the forms have errors (what I did in the code above) and allow them when there are no errors. Also, you should probably add a beautiful red frame with a hint on them, explaining to the user why he can no longer choose.
To check if the title is a duplicate within the same person, you can extend the check mechanism above to have the ValidationService class that the Face receives and looks to see if the person who has the task has another one with the same name.
public class Person: IDataErrorInfo {
public string this[string columnName] { get {
Also, my binding is good, how did I set the DataContext in the last panel, for example, what? It works, but it feels a little superficial. An alternative is declaring resources for CollectionDataSources and I could not really, this method is very easy.
This is not in the spirit of MVVM, and if what you are doing is more than a small test project, you should try exploring MVVM (you can start here: http://fernandomachadopirizen.wordpress.com/2010/06/ 10 / a-simple-introduction-to-the-model-view-viewmodel-pattern-for-building-silverlight-and-windows-presentation-foundation-applications / )
Then you are not attached directly to the list of persons, but instead you will have the MainWindowViewModel class to which you are attached. This MainWindowViewModel class would contain a list of faces, and you would be attached to it. And for selection, you will have the CurrentJob property in your MainWindowViewModel, that is, the currently selected task, and you are bound to this:
Basically something like this:
<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,0"> <TextBox Text="{Binding Name}" DockPanel.Dock="Top" Margin="0,0,0,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>
And your MainWindowViewModel:
public class MainWindowViewModel : { ...