WPF DataGrid: как привязать свойства SelectedItem к данным для запуска INotifyPropertyChangedEvents?

Я пытаюсь сделать это как MVVM, насколько это возможно:
Моя модель (InterestTypeEntity) реализует INotifyPropertyChanged.
Моя ViewModel (InterestTypeAllViewModel) имеет ObservableCollection который привязывается к DataGrid. Когда в него вносятся изменения, он отправляет эти изменения (добавление / удаление) в базу данных.

проблема в том, что я хочу также иметь возможность обновлять базу данных при изменении свойств объектов в коллекции. Я не знаю, как это сделать? Вот мой код пока ...

XAML:

<DataGrid Name="TestGrid" Grid.Row="3" Grid.ColumnSpan="2" AutoGenerateColumns="False"
          ItemsSource="{Binding IntTypes}" SelectedItem="{Binding CurrentIntType}">
    <DataGrid.Columns>
        <DataGridTextColumn Header="Interest ID" Binding="{Binding IntType}" />
        <DataGridTextColumn Header="Interested Parties Description" Binding="{Binding Description}" MaxWidth="500" />
    </DataGrid.Columns>
</DataGrid>

Код модели просмотра:

public ObservableCollection<InterestTypeEntity> IntTypes 
{
    get { return DataRepository.InterestTypeEntities; }
}

public InterestTypeEntity CurrentIntType { get; set; }

public Int16 IntType
{
    get { return CurrentIntType.IntType; }
    set
    {
        if (value != CurrentIntType.IntType)
        {
            CurrentIntType.IntType = value;
            OnPropertyChanged("IntType");
        }
    }
}

public String Description
{
    get { return CurrentIntType.Description; }
    set
    {
        if (value != CurrentIntType.Description)
        {
            CurrentIntType.Description = value;
            OnPropertyChanged("Description");
        }
    }
}

person myermian    schedule 07.10.2010    source источник


Ответы (3)


Не создавайте коллекцию объектов модели и не реализуйте свойства IntType и Description в вашей (текущей) модели представления. И если у вас нет других причин для этого, не внедряйте уведомление об изменении свойств в своей модели.

Вместо этого сделайте IntTypes коллекцией InterestTypeEntityViewModel объектов.

Этот класс является оболочкой для InterestTypeEntity. Он предоставляет свойства IntType и Description, которые а) заключают в оболочку базовые свойства InterestTypeEntity и б) отправляют уведомление об изменении свойства. Если вы заставите его конструктор принимать аргумент InterestTypeEntity, его легко заполнить в вашей модели представления:

IntTypes = new ObservableCollection<InterestTypeEntityViewModel>(
   DataRepository.InterestTypeEntities.Select(x => new InterestTypeEntityViewModel(x));

Привяжите ItemsSource к этой коллекции. (Кроме того, сделайте CurrentIntType свойством типа InterestTypeEntityViewModel и поднимите PropertyChanged при его изменении.)

Изменить:

Если модель представления-владельца должна быть уведомлена при изменении свойств элементов в ее коллекции, довольно просто заставить ее обрабатывать PropertyChanged события, которые они вызывают. В конструкторе добавьте:

foreach (InterestTypeEntityViewModel vm in IntTypes)
{
  vm.PropertyChanged += InterestTypeEntityViewModel_PropertyChanged;
}

и этот метод:

private void InterestTypeEntityViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
   InterestTypeEntityViewModel vm = (InterestTypeEntityViewModel) sender;
   // check e.PropertyName and do whatever you need to do here.
}

Не забудьте отменить регистрацию обработчика событий, если вы удалите объект из своей коллекции; в противном случае дочерние объекты модели представления не будут удалены, пока не будет удален родительский объект.

Кстати, обратите внимание, что, реализуя модели представления таким образом, вы можете в значительной степени контролировать свои обновления базовой модели сущностей. Например, вы можете реализовать команду в своей родительской модели представления, которая выполняет все обновления за одну операцию, и другую, которая позволяет пользователю отменить все изменения, внесенные в пользовательский интерфейс, без выполнения обновления. И вся эта логика очень хорошо отделена от реального уровня представления.

person Robert Rossney    schedule 07.10.2010
comment
Что ж, я внес необходимые изменения, и я могу заставить мою InterestTypeViewModel принимать изменения, но как этот всплывает до InterestTypeAllViewModel, который содержит эту ObservableCollection моделей просмотра? - person myermian; 08.10.2010
comment
Не все само по себе, но сделать это просто. Смотрите мою правку. - person Robert Rossney; 08.10.2010
comment
Получил, что это работает таким образом. Я действительно надеялся, что эта ItemObservableCollection будет работать, поскольку это означало бы, что мне не нужно связывать дополнительные события и тому подобное. Но нужно использовать то, что работает. И, если я когда-нибудь решу создать представление, в котором используется InterestTypeViewModel (а не версия All), оно мне пригодится. - person myermian; 08.10.2010
comment
Стоит научиться связывать объекты модели представления друг с другом посредством событий. Использование событий действительно упрощает множество проблем с пользовательским интерфейсом. - person Robert Rossney; 08.10.2010

См. Мой ответ здесь. Это даст вам наблюдаемую коллекцию, которая сообщает вам, когда коллекция изменяется или когда изменяется элемент в коллекции.

person Kent Boogaart    schedule 07.10.2010
comment
Кроме того, я получаю странную ошибку при попытке использовать класс ItemObservableCollection<T>. Я продолжаю получать эту ошибку компилятора CS0311: тип «type1» не может использоваться в качестве параметра типа «T» в универсальном типе или методе «‹name›». Неявное преобразование ссылки из type1 в type2 отсутствует. Это говорит о том, что не существует неявного преобразования моего InterestTypeEntity в System.ComponentModel.INotifyPropertyChanged. - person myermian; 08.10.2010
comment
@myermian: это не странная ошибка - это результат общего ограничения. Тип T должен реализовывать INotifyPropertyChanged, чтобы ItemObservableCollection<T> мог его использовать. Следовательно, ваш InterestTypeEntity должен реализовывать INotifyPropertyChanged. Кроме того, я обновил другой пост, включив в него класс ItemPropertyChangedEventArgs. - person Kent Boogaart; 08.10.2010
comment
Поверьте, это странно, потому что у меня есть InterestTypeEntity как дочерний класс AbstractEntity. AbstractEntity реализует INotifyPropertyChanged. Фактически, даже если у меня есть InterestTypeEntity, реализующий INotifyPropertyChanged напрямую, он все равно дает мне эту ошибку ...: - / - person myermian; 08.10.2010
comment
Я узнал, что это произошло потому, что мои типы сущностей не реализуют INotifyPropertyChanged, а мои модели просмотра. Вот почему я продолжал получать эту ошибку. - person myermian; 08.10.2010

Общая стратегия:

Добавьте в привязку TwoWay:

SelectedItem="{Binding CurrentIntType,Mode=TwoWay}"

А затем подпишитесь на измененное событие наблюдаемой коллекции в вашей ViewModel. Когда коллекция изменится, отправьте ее в свою модель / DAL и сохраните.

person Jason    schedule 07.10.2010
comment
Что ж, единственная проблема, которую я вижу в этом, заключается в том, что я на самом деле не меняю коллекцию ... вместо этого я меняю свойства объекта УЖЕ В коллекции. Это означает, что событие для изменения коллекции никогда не запускается. - person myermian; 07.10.2010
comment
Согласно документации, событие ObservableCollection changed срабатывает, когда элемент добавляется, удаляется, изменен, перемещается или обновляется весь список. msdn.microsoft.com/en-us/library/ms653375.aspx - person Jason; 07.10.2010
comment
Но это ссылочный тип, поэтому ссылка не меняется. Значения в справке меняются. - person myermian; 07.10.2010
comment
Ну вроде как. foo[i] = new Foo() изменяет i-й элемент foo. foo[i].Bar = 'x' нет. - person Robert Rossney; 07.10.2010