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>

Код на ViewModel:

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
Струва си да се научите да накарате обектите на модела на view да комуникират помежду си чрез събития. Използването на събития наистина опростява много проблеми с потребителския интерфейс. - 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
Разбрах, че е така, защото моите типове Entity не прилагат INotifyPropertyChanged, My ViewModels са. Ето защо непрекъснато получавах тази грешка. - 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 changes се задейства, когато даден елемент е добавен, премахнат, променен, преместен или целият списък е обновен. 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