2013-11-08 2 views
1

Использование WPF моя команда пытается отделить дизайн и код с использованием шаблонов проектирования MVVM. Чтобы достичь этого, мы постепенно отходим от подхода UserControl, поскольку он имеет высокий уровень сцепления между дизайном и кодом. Мы изучаем использование шаблонов управления, стилей и DataTemplates в сочетании с классом ViewModel. На сегодняшний день мы получили большую часть этой работы.WPF View-ViewModel Loosely connected communication

Проблемы, которые возникают у нас, связаны с сообщениями/уведомлениями между View и ViewModel. В настоящее время мы «решили» проблемы связи View -> Viewmodel с помощью ICommand. т. е. мы создаем кнопку и привязываем ее параметр «Команда» к имени RelayCommand, определенному в ViewModel. Таким образом, нажатие кнопки или другое командное событие, поднятое из представления, вызывает функцию, определенную в ViewModel. Это работает.

В нашей основной проблеме заключается в том, чтобы уведомление было запущено в обратном порядке: например, изменение данных в ViewModel должно инициировать обновление в представлении. Мы делали это с использованием NotifyPropertyChanged и DataTriggers, однако это не удовлетворяет наши потребности. Нам нужно иметь возможность поднять какое-то событие в Viewmodel и подписаться на это событие. Мы искали ответ на этот вопрос и узнали об обоих RoutedEvents и AttachedBehaviors. RoutedEvents походил на победителя решения, однако из нашего исследования RoutedEvents не могут быть зарегистрированы в ViewModel, который не распространяется на UIElement, и мы особенно стараемся, чтобы наш код был отделен от дизайна.

В конечном итоге мы пытаемся создать ViewModel, где можно задать параметр или вызвать функцию, которая приведет к возникновению события или поведения в представлении и последующему запуску анимации. Мы работали с DataTriggers, однако мы пытаемся отделить наши анимации от ControlTemplate, и это вызывает проблемы, поскольку DataTemplate, который содержит DataTriggers, не имеет доступа к Storyboards, определенным в ControlTemplate.

Может кто-нибудь указать нам в правильном направлении? В частности, создание события в ViewModel (которое не требует расширения UIElement) и подписки на это событие в представлении и активации раскадровки.

Благодаря

+0

Удалось заставить его работать, определяя раскадровку как ресурс, а затем привязывая этот ресурс раскадровки к ControlTemplate.Triggers Datatrigger. Таким образом, событие NotifyPropertyChanged запускает обновление, и ControlTemplate отвечает за обработку его собственных анимаций. Выполняя все это, ControlTemplate отвечает за поддержание состояния анимации и анимации на основе свойства, определенного в ViewModel. – user2961221

ответ

0

Самый лучший способ, чтобы создать представление базы, которая автоматически подписывается на INotifyPropertyChanged на виртуальной машине, как только свойство установлено (не забудьте отписаться!). Держитесь подальше от RoutedEvents (или любых событий, связанных с пользовательским интерфейсом) в модели представления. Они должны реализовывать только INotifyPropertyChanged.

Катель обеспечивает решение вне коробки для вас для такого рода вещей:

https://catelproject.atlassian.net/wiki/display/CTL/Mapping+properties+from+view+to+view+model

Если вы не можете использовать Catel, просто копировать/вставить детали, необходимые вам в ваше собственные рамки. Однако не тратьте время на повторное использование колеса снова и снова ;-)

+0

Какова цель этой библиотеки Catel? Насколько я могу судить, это просто альтернативный способ сделать привязку. Конкретный случай, который мы ищем, - запустить анимацию в представлении, когда ViewModel имеет изменение свойства. Я не вижу, как Catel помогает в этом. – user2961221

+0

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

1

Я использую MVVM самостоятельно, и если я хочу что-то изменить на экране, я использую интерфейс INotifyPropertyChanged.

Когда какой-либо элемент пользовательского интерфейса должен быть изменен, вызывается событие OnPropertyChanged и обновляется пользовательский интерфейс.

+0

Да, я упомянул в исходном вопросе, что я использовал события NotifyPropertyChanged. Однако использование NotifyPropertyChanged для запуска анимации требует использования DataTrigger, и вы не можете использовать «TargetName» в раскадровке в стиле. И чтобы уточнить, мы не хотим встраивать анимации в DataTemplate. – user2961221

0

Вы также можете использовать шаблон посредника для запуска анимации из режима просмотра. Просто отправьте сообщение с viewmodel и подпишитесь на это сообщение в представлении.

0

Я использую делегата для связи с ViewModel для просмотра. Позволь мне объяснить.В моих рамках, у меня есть абстрактный класс:

public abstract class NotificationObject : INotifyPropertyChanged 
{ 
    // very usefull to order view (if exist) to close 
    public delegate void ActionClose(Boolean closeResult); 

    public delegate void ActionUpdate(); 

    public event PropertyChangedEventHandler PropertyChanged; 

    protected virtual void RaisePropertyChanged(string propertyName) 
    { 
     // take a copy to prevent thread issues 
     PropertyChangedEventHandler handler = this.PropertyChanged; 
     if (handler != null) 
     { 
      handler(this, new PropertyChangedEventArgs(propertyName)); 
     } 
    } 

    // other implementation for RaisePropertyChange (not the point here) 
    // protected virtual void RaisePropertyChanged(params string[] propertyNames) 
    // protected virtual void RaisePropertyChanged<T>(Expression<Func<T>> propertyExpression) 
} 

Теперь все мои ViewModel могут наследовать от NotificationObject

public class MyViewModel : NotificationObject 
{ 
    public MyViewModel() 
    { 
     // initialisation 
    } 

    // bindable action UI -> VM 
    public ICommand Command1 { get; set; } 
    public ICommand Command2 { get; set; } 

    // VM -> View 
    public ActionUpdate Action1 { get; set; } 
    public ActionUpdate Action2 { get; set; } 

    // in this method, we need to "notify" view that it must do something (refresh, close, modify UI ...) 
    private void MyMethod() 
    { 
     // never forget that UI can not exist (testing for exemple), or not UI (command-line process), so Action1 can be null 
     if (this.Action1 != null) 
     { 
      this.Action1(); 
     }   
    } 
} 

Теперь, в представлении, мы можем объявить делегат (в Window_Loaded, то ViewModel загружается):

private void Window_Loaded(object sender, RoutedEventArgs e) 
{ 
    MyViewModel context = this.DataContext as MyViewModel ; 

    if (context != null) 
    { 
     NotificationObject.ActionUpdate myAction1 = new NotificationObject.ActionUpdate(this.MyAction1); 
     context.Action1 = myAction1 ; 
     NotificationObject.ActionUpdate myAction2 = new NotificationObject.ActionUpdate(this.MyAction2); 
     context.Action2 = myAction2 ; 
    } 
} 

private void MyAction1() 
{ 
    // do something 
} 

private void MyAction2() 
{ 
    // do something 
} 

Это все. ^^

+0

Хотя я могу понять, что вы предлагаете, акт привязки делегатов в коде, который находится за точкой зрения, прямо противоречит подходу, который мы пытаемся сделать. – user2961221

-1

Я обнаружил, что MVVM не очень хороший выбор для ослабления сцепления. По умолчанию Microsoft Model-View - любые шаблоны решений бросают все в один проект. Чтобы быть по-настоящему свободным, попробуйте переместить все, кроме элементов GUI, в свои собственные библиотеки и избегать ссылок на конкретную цель. Свободно связанная система будет иметь ссылки System.Windows или System.Web только на целевую платформу (клиент/консоль/веб-службу) и иметь повторно используемые компоненты в библиотеках моделей и бизнес-слоев. Вероятно, в конечном итоге вы получите некоторые классы фасадов и адаптеров, которые упростят общие реализации. Я полагаю, это зависит от того, что вы пытаетесь отделить. Рамки вообще добавляют больше ограничений. Я знаю, что это не очень популярная точка зрения, но это мои 2 цента.

0

Более универсальное решение, позволяющее разделить сложный код, - это шаблон View Services. Долгое и короткое из них, похожее на Views, View Services зависят от платформы, но в отличие от Views, являются известными ViewModel. Практическая реализация заключается в том, что ViewModels Constructor ожидает интерфейс требуемого ViewService, в то время как сторона «View» кода обрабатывает реализацию упомянутого интерфейса с помощью View specific code like Dialogs, Notifications и т. Д. Это очень хорошо работает с Inversion of Control слишком. Не то, что помогает, если вообще не предполагается никакого кода на View.