WPF: использование интерфейса в качестве DataContext вместо объекта, реализующего INotifyPropertyChanged - PullRequest
0 голосов
/ 26 марта 2019

Я реорганизовал свое приложение WPF, чтобы DataContexts больше не были экземплярами какого-то класса, который реализует INotifyPropertyChanged, а скорее мои собственные интерфейсы. Объект, который реализует каждый пользовательский интерфейс, также должен реализовывать INotifyPropertyChanged. Но я больше не могу применять это требование во время сборки. Я не уверен, что это хорошая идея.

Например, рассмотрим следующую простую иерархию классов

// Base view model class has stock implementation of INotifyPropertyChanged.

public BaseViewModel : INotifyPropertyChanged
{
   // Assume INotifyPropertyChanged implementation with this utility to raise the event

   void RaisePropertyChanged(string property) { // ...blah blah blah ... }
}

// Interface for nameable object

public interface INameable
{
    string Name { get; set; }
}

// View model for nameable object

public class NameableViewModel : BaseViewModel, INameable
{
    private string _name;
    public string Name
    {
        get => _name;
        set
        {
            if (value == _name) return;
            _name = value
            RaisePropertyChanged();
    }
}

// Main view model exposes named object by interface, NOT by view-model

public class MainViewModel : BaseViewModel
{
    public INameable NameableObject { get; set; } 
}

Вот привязка вида к INameable вместо NameableViewModel

<UserControl x:Class="MyView" 
             (... blah blah blah... namespace declarations...)
             d:DataContext="{d:DesignInstance MainViewModel}">

    <TextBox DataContext={Binding NameableObject.Name, Mode=TwoWay}"/>
</UserControl>

Это прекрасно работает в WPF, потому что он запрашивает любой контекст данных для INotifyPropertyChanged. Он получает уведомления PropertyChanged, хотя все, что он получил, было INameable. Но если я забуду правильно настроить свои классы, WPF просто молча никогда не получит уведомления об изменении свойств. Я мог бы в конечном итоге иметь ошибки, которые нужно заметить или отследить (ресурсы тестирования здесь ограничены).

Кроме того, в своем коде я иногда вручную наблюдаю за некоторыми объектами через INotifyPropertyChanged, используя превосходный PropertyObserver Джоша Смита.

public class PropertyObserver<TPropertySource> 
    : IWeakEventListener
    where TPropertySource : INotifyPropertyChanged
{
   ... blah blah blah..
}

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

Что приводит меня к моим вопросам. Один общий и один очень конкретный:

  1. Является ли этот дизайн разумным? Это то, что обычно делают люди, работающие в WPF? Или вообще всегда лучше быть уверенным, что любой тип, который я использую в качестве контекста данных, уже объявлен для реализации INotifyPropertyChanged? (Я понимаю, что, возможно, я слишком много думаю, как программист C ++ с этим вопросом)
  2. Предположим, в моем коде я приведу объект, и он НЕ поддерживает интерфейс, который мне требуется, и я хочу вызвать исключение. Какое конкретное исключение .NET я должен выбросить? Что наиболее подходит? NotSupportedException? Что-то еще?

1 Ответ

1 голос
/ 28 марта 2019

Я не совсем уверен, что такое «дизайн» или «практика», на которую вы ссылаетесь. Я не вижу общего шаблона, только конкретное использование интерфейса, для которого его назначение мне неясно.

По моему опыту, лучше всего сохранять модели представлений максимально простыми. В некоторых случаях им даже не нужно реализовывать INotifyPropertyChanged (подумайте о модели представления только для чтения). Как правило, я использую инфраструктуру MVVM (например, MVVM Light Toolkit), которая обеспечивает ViewModelBase. Почти каждая модель представления, которую я создаю, происходит от ViewModelBase, а модель представления просто содержит сеттеры, геттеры и команды.

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

...