Проверка дочерней записи в многоуровневом представлении wpf - PullRequest
4 голосов
/ 08 апреля 2011

Допустим, у меня есть набор классов моделей, подобных этому:

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,0,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>

DataContext для всего окна - это ObservbleCollection of People.Кажется, это работает довольно хорошо.Теперь я хотел бы добавить некоторую проверку и понятия не имею, с чего начать.

Я бы хотел проверить поля заданий, чтобы пользователь не мог выбрать другое задание (или лицо), если заголовок пуст илидублировать в том же человеке, или если даты не в порядке.Кроме того, персона не может быть изменена, если имя пусто.

Я немного прочитал о проверке в WPF, но не нашел какого-либо четкого решения для этого сценария.Как лучше всего делать что-то подобное?

Кроме того, моя привязка в порядке, как я установил DataContext на последней панели, как это?Это работает, но кажется немного отрывочным.Альтернативой является объявление ресурсов для CollectionDataSources, и я не мог понять этот метод очень легко.

1 Ответ

2 голосов
/ 23 апреля 2011

Для части проверки вы можете начать здесь: 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, оно будет выглядеть как 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 {};
    }

////////////////////////

Я хотел бы проверить поля заданий, чтобы пользователь не мог выбрать другое задание (или лицо), если заголовок пуст или дубликат в том же человеке, или если даты выходят запорядок.Кроме того, Person нельзя изменить, если имя пустое.

Одно простое решение - отключить списки, если в формах есть ошибки (это то, что я делал в приведенном выше коде), и включить их, когданет ошибок.Также вам, вероятно, следует поставить красивую красную рамку с всплывающей подсказкой, объясняющей пользователю, почему он больше не может выбирать.

Для проверки, если заголовок дублируется в одном и том же человеке, вы могли бы расширить механизм проверки, описанный выше, чтобы иметь класс ValidationService, который получает Person и проверяет, есть ли у человека, который имеет работу, еще один с таким жеname.

открытый класс 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;
     }
...
 }

Кроме того, нормально ли мое связывание, как я установил DataContext на последней панели таким образом?Это работает, но кажется немного отрывочным.Альтернатива - объявлять ресурсы для CollectionDataSources, и я не мог понять этот метод очень легко.

Это не в духе 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 будет содержать список людей, и вы будете привязываться к нему. А для выбора у вас будет свойство CurrentJob в вашем 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,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> 

И ваша MainWindowViewModel:

public class MainWindowViewModel : 
{
...
   //Public Properties
   public ObservableCollection<Person> PersonList ....
   public Job CurrentJob ....
....
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...