За это время пример, описанный ранее (с биндингом FlowDocument) практически без доработок напильником был встроен в рабочий проект и работает.
Но сегодня про другое. В WPF есть классная штука, под названием RoutedUICommand. Если сильно упростить - то с ее помощью можно указывать какую команду будет выполнять кнопка или пункт меню. При этом сама реализации команды будет жить где-то отдельно. Причем для разных элементов управления может быть разная реализация (например команда "Сохранить" может сохранять как документ, так и какой-нибудь элемент справочника). Более того, в нужный момент WPF проверяет а можно ли вообще выполнит команду и если нельзя, то дизаблит кнопку или что у нас там к команде привязано. В общем не буду долго расписывать что это такое, кому интересно может в журнале почитать.
Однако как всегда не все так хорошо как хотелось. В простом примере все шоколадно. Но как только мы начинаем все усложнять то нам сразу хочется тестов. А для такого случая умные дядьки придумали MVVM (опять так есть гугл и журнал). В кратце смысл в том, что на форме у нас вообще никакого кода нет, а вся логика живет в View-модели, на которую настроены биндинги и которая разными шаманскими методами (самый простой - тупо назначить DataContext) подсовывается форме. А так как вся логика получсется живет в обычном классе (который о форме вообще ничего не знает), то его и тестировать можно очень легко.
И вот как раз на перекрестии двух очень интересных идей как обычно и живет засада. Заключается она в том, что команды привязываются через CommandBinding. А он как обычно не является DependencyObject и соответственно никакие его свойства мы не можем использовать в биндинге. Хотя как-бы и логично, потому что привязка в оригинале задумывалась как привязка к данным, а тут медоды... И соотв. нельзя написать что-то типа
(Идея не моя, где-то в блогах подсмотрел).* This source code was highlighted with Source Code Highlighter.
- <UserControl.CommandBindings>
- <CommandBinding Command="ApplicationCommands.Save" Execute="{Binding SaveExecute}"/>
- UserControl.CommandBindings>
Получается что единственный более-менее стандартный метод - это описывать обработчики команд в коде формы (или UserControl-а, в общем в xaml.cs) и их этих обработчиков звать методы презентера. Что конечно можно, но не красиво.
Первым желанием что-либо сделать было создать универсальный обработчик команд, который через Reflection каждый раз лез бы в DataContext, искал там метод по атрибуту или еще как-нибудь и вызывал его. Однако посмотрев на частоту, с которой вызывается CanExecute возникли подозрения что тормозить будет безбожно... Немного почесав репу пришло в голову примерно следующее решение: так как презентер на самом деле меняется редко (чаще всего вообще назначается один раз при создании формы) то можно каждый раз отслеживать поменялся ли он, и лезть Reflection-ом только когда поменялся. А если ничего не менялось - то вызывать заранее запомненные (или созданные) делегаты. Такая вот эмуляция DataBinding-а.
Осталось определиться с тем, как связывать команды. Так как есть стандартный CommandBinding, то его и будем расширять. Вопрос только, как искать методы, исполняющие команду. Пока в голову пришло 2 варианта
- В презентере создавать экземпляр ICommand, и просто делегировать методы ему. При этом так как в презентере может быть много команд, то каким-то образом надо выбрать какая из них соответствует какому CommandBindig-у. Пока в голову пришло все команды в презентере делать определенным классом (DataContextCommand) у которого есть свойство, определяющее какую команду обслуживаем. Как это выглядит при использовании можно посмотреть тут.
- Пометить методы для CanExecute и Execute атрибутами. При этом в CommandBinding-е указать имя команды (к сожалению никак, кроме как строками не получается) и в атрибуте тоже указать имя команды. Как это выглядит при использовании можно посмотреть тут.
- StdCommand - почти целиком копия из примера для статьи в msdn magazine. Просто чтобы продемонстрировать как оно работает в простом случае.
- BindToICommand - реализация первого способа.
- NamedBind - реализация второго способа.
Комментариев нет:
Отправить комментарий