В предыдущих статьях, посвященных MVVM Light toolkit, я уже приводил примеры использования RelayCommand и ViewModelBase классов. Сейчас я хочу подробнее остановиться на еще трех классах, которые предлагает этот toolkit, а именно:
- EventToCommand behavior позволяет привязывать команды к любым событиям любых UI контролов.
- Класс Messenger позволяет организовывать обмен сообщениями внутри приложения.
- Класс DispatcherHelper облегчает жизнь при работе с UI потоком.
EventToCommand
Несмотря на все свои достоинства, в WPF команды поддерживаются очень ограниченным числом UI элементов (только производными от типа ButtonBase). Более того команду можно привязать только к событию Click. Получается, что для того чтоб запустить комаду, определенную во ViewModel, по событию сгенерированному UI элементом, не поддерживающим команды, нам понадобится в codebehind создавать обработчик этого события и из него руками вызывать команду.
Однако всех этих сложностей можно избежать, если использовать тип EventToCommand предлагаемый MVVM Light toolkit. Давайте рассмотрим небольшой пример. Допустим у нас есть TextBox и мы хотим к его событию TextChanged привязать команду ChangeMode. Сделать это очень просто:
<TextBox Text="{Binding TagName, UpdateSourceTrigger=PropertyChanged}" Grid.Column="1" Margin="0 0 0 5" MinWidth="200"> <i:Interaction.Triggers> <i:EventTrigger EventName="TextChanged"> <cmd:EventToCommand Command="{Binding ChangeMode, Mode=OneWay}" /> </i:EventTrigger> </i:Interaction.Triggers> </TextBox>
Messenger
Messenger позволяет организовать обмен сообщениями внутри приложения. В отличие от событий, отправитель сообщений не хранит ссылки на получателей. Получатели в общем случае могут даже и не знать отправителя.
Вместо того чтобы подписываться на сообщения от какого-то конкретного объекта получатели просто сообщают Messenger’у какие именно сообщения они хотят получать. Например, вот так можно зарегистрироваться для получения сообщений типа TagInfo:
Messenger.Default.Register<TagInfo>(this, x => { Tags.Add(x); });
Теперь получателю будут приходить все сообщения, содержащие TagInfo, от любого отправителя. Лямбда, приведенная в примере, описывает реакцию получателя на сообщение.
С другой стороны отправители не занимаются рассылкой самостоятельно. Вместо этого они создают экземпляр сообщения нужного типа и просят Messenger’а его оправить:
Messenger.Default.Send<TagInfo, MainViewModel>( new TagInfo { Name = TagName, Score = TagScore });
После отправки это сообщение будет доставлено всем заинтересованным получателям.
Хочу отметить следующие три момента связанные с Messenger’ом:
- Связи между отправителями и получателями можно настраивать. Совсем не обязательно рассылать сообщения всем подряд. Например, используя токены при регистрации и отправке, можно организовать общение только между конкретными экземплярами отправителя и получателя.
- Messenger никак не работает с потоками. Т.е. если необходимо пробрасывать сообщения из одного потока в другой, то организовывать такую переброску нужно самостоятельно.
- Messenger не хранит жестких ссылок на получателей. Т.е. после регистрации ничто не мешает сборщику мусора удалить экземпляр получателя.
DispatcherHelper
DispatcherHelper – это класс, который упрощает запуск операций в потоке UI из других потоков. При инициализации DispatcherHelper сохраняет в своем свойстве UIDispatcher ссылку на диспетчера потока UI. После этого любая операция, запущенная с помощью DispatcherHelper.CheckBeginInvokeOnUI() гарантированно будет выполнена в потоке UI. При этом если запуск происходит из потока UI, то операция будет выполнена стазу же синхронно. Если же из другого потока, то операция будет поставлена в очередь выполнения диспетчера потока UI и выполнена асинхронно.
Вот так, например, можно отправить сообщение из фонового потока в поток UI:
DispatcherHelper.CheckBeginInvokeOnUI( () => { Messenger.Default.Send<TagInfo, MainViewModel>( new TagInfo { Name = DateTime.Now.ToLongTimeString(), Score = 1 }); } );
На этом у меня все. Исходный код проекта, демонстрирующего описанный функционал, можно скачать здесь.
Здравствуйте.
ReplyDeleteНачал новый проект с использованием MVVM Light Toolkit. Сильно досаждает полное отсутствие документации... Расскажите подробнее про ViewModelLocator. В частности интересует где нужно создавать View и соответствующие ViewModels в рамках тулкита.
Например на главной форме есть кнопка, при клике на которую нужно показать новое окно. При этом ViewModel этого окна нужны начальные данные для инициализации.
Сейчас сделано так: на клик повешена команда, реализация которой находится в MainViewModel. В этом обработчике создаю View и ViewModel (со всеми нужными параметрами в конструкторе), а потом показываю окно. Что-то подсказывает что это неправильный подход...
Danila, на этих выходных напишу про ViewModelLocator и постараюсь ответить на Ваш вопрос
ReplyDeleteОчень познавательно, но хотелось бы больше информации про варианты открытия новых окон из ViewModel в том числе диалоговых. А также контроль закрытия View внутри ViewModel и наоборот - закрытие View через ViewModel. Очень кратко написано про Messanger хочется разобраться более детально. Спасибо.
ReplyDelete