Winrt, кнопки і mvvm

На перший погляд здається, що ідея застосування шаблону MVVM для усунення більшої частини вмісту файлу відокремленого коду придатна тільки для елементів, що генерують значення. Якщо ж мова заходить про кнопках, концепція починає хитатися. Об'єкт Button ініціює подія Click. Ця подія має бути оброблено в файлі відокремленого коду. Якщо модель уявлення фактично реалізує логіку кнопки (що цілком ймовірно), обробник події повинен викликати метод моделі уявлення. Таке рішення допустимо з точки зору архітектури, але воно все одно виходить громіздким.

На щастя, у події Click існує альтернатива, яка ідеально підходить для MVVM. Іноді її неформально називають «командним інтерфейсом». Клас ButtonBase визначає властивості з ім'ям Command (типу ICommand) і CommandParameter (типу object), що дозволяють Button фактично звернутися з викликом до моделі представлення через прив'язку даних.

Властивості Command і CommandParameter підтримуються властивостями залежності; це означає, що вони можуть бути приймачами прив'язок. Властивість Command майже завжди є приймачем прив'язки даних, властивість CommandParameter не є обов'язковим. Наприклад, воно може використовуватися для розрізнення кнопок, прив'язаних до одного об'єкту Command, і зазвичай виконує приблизно ті ж функції, що і властивість Tag.

Припустимо, ви написали додаток-калькулятор, ядро ​​якого реалізовано в формі моделі уявлення, що задається властивістю DataContext. Примірник кнопки калькулятора для виконання команди додавання (+) може створюватися в розмітці XAML:

Це означає, що модель уявлення містить властивість CalculateCommand типу ICommand. яке, ймовірно, визначається наступним чином:

Модель представлення повинна ініціювати властивість CalculateComnand, задаючи йому екземпляр класу, що реалізовує інтерфейс ICommand, який визначається наступним чином:

При натисканні на цю кнопку викликається метод Execute об'єкта, на який посилається CalculateCommand, з аргументом "add". Виходить, що Button звертається з викликом безпосередньо до моделі представлення (а вірніше до класу, який містить метод Execute).

Два інших члена інтерфейсу ICommand відносяться до перевірки можливості виконання конкретної команди в конкретний момент. Якщо команда в даний час є недійсною (наприклад, калькулятор не може виконати додавання через відсутність введеного числа), відповідна кнопка повинна бути заблокована.

Ось як працює цей механізм: в процесі розбору і завантаження XAML під час виконання властивості Command об'єкта Button задається прив'язка (в даному прикладі) до об'єкта CalculateCommand. Button призначає оброблювач для події CanExecuteChanged і викликає метод CanExecute об'єкта з аргументом (в даному прикладі) "add". Якщо CanExecute поверне false, кнопка Button блокує себе. Надалі Button повторно викликає CanExecute при ініціюванні події CanExecuteChanged.

Щоб включити команду в вашу модель уявлення, необхідно надати клас, який реалізує інтерфейс ICommand. Однак такого класу з великою ймовірністю буде потрібно звертатися до властивостей класу моделі уявлення, і навпаки.

Виникає питання: чи не можна ці класи поєднати?

Теоретично можна, але тільки в тому випадку, якщо одні й ті ж методи Execute і CanExecute можуть використовуватися для всіх кнопок сторінки; для цього кожна кнопка повинна володіти унікальним значенням CommandParameter, за яким її можна буде ідентифікувати. Але спочатку я хотів би продемонструвати стандартний спосіб реалізації команд в моделі уявлення.

клас DelegateCommand

Перепишемо додаток SimpleKeypad. створене раніше так, щоб воно використовувало модель представлення для накопичення натискань клавіш і генерування відформатованої рядки. Крім реалізації інтерфейсу INotifyPropertyChanged, модель представлення також обробляє команди від всіх кнопок клавіатури. Обробники Click більше не потрібні.

А тепер проблема: щоб модель уявлення обробляла команди кнопок, вона повинна володіти одним або декількома властивостями типу ICommand; це означає, що нам знадобиться один або кілька класів, що реалізують інтерфейс ICommand. Для реалізації ICommand ці класи повинні містити методи Execute і CanExecute, а також подія CanExecuteChanged. При цьому тіла цих методів безумовно повинні взаємодіяти з іншими частинами моделі уявлення.

Проблема вирішується визначенням методів Execute і CanExecute в класі моделі представлення під іншими, унікальними іменами. Потім визначається спеціальний клас, який реалізує ICommand, а також безпосередньо викликає методи моделі уявлення.

Цей спеціальний клас часто називається DelegateCommand. Озирнувшись, ви знайдете кілька різних реалізацій цього класу, включаючи реалізацію з інфраструктури Microsoft Prism, яка допомагає розробникам реалізовувати шаблон MVVM в WPF (Windows Presentation Foundation) і Silverlight. Нижче наведена моя реалізація.

Мій клас DelegateCommand реалізує інтерфейс ICommand; це означає, що він містить методи Execute і CanExecute, а також подія CanExecuteChanged. Але як з'ясовується, DelegateCommand потрібен ще один метод для ініціювання події CanExecuteChanged. Ми назвемо його RaiseCanExecuteChanged. Перш за все слід визначити інтерфейс, який реалізує ICommand і містить цей додатковий метод:

Клас DelegateCommand реалізує інтерфейс IDelegateCommand і використовує прості (але корисні) узагальнені делегати, певні в просторі імен System. Ці зумовлені делегати називаються Action і Func і отримують від 1 до 16 аргументів. Делегати Func повертають об'єкт конкретного типу, а делегати Action цього не роблять. делегат Action представляє метод з одним аргументом object і повертається значенням void - сигнатура методу Execute. делегат Func представляє метод з аргументом object, який повертає bool; це сигнатура методу CanExecute. DelegateCommand визначає два поля зазначених типів для зберігання методів з такими сигнатурами:

Клас реалізує методи Execute і CanExecute, але ці реалізації просто викликають методи, збережені в полях. Поля заповнюються конструктором класу за переданими аргументів.

Наприклад, якщо в моделі уявлення є команда обчислення, то в ній може бути визначено властивість CalculateCommand:

Модель представлення також визначає два методи з іменами ExecuteCalculate () і CanExecuteCalculate ():

Конструктор класу моделі уявлення створює властивість CalculateCommand, створюючи екземпляр DelegateCommand з цими двома методами:

Тепер, коли ви зрозуміли загальну ідею, розглянемо модель представлення для клавіатури. Для вводиться і тексту, що відображається модель подання визначає два властивості: InputString і отформатированную версію DisplayText.

Модель представлення також визначає два властивості типу IDelegateCommand з іменами AddCharacterCommand (для цифрових і символьних клавіш) і DeleteCharacterCommand. Ці властивості створюються за допомогою створення екземпляра DelegateCommand з методами ExecuteAddCharacter (), ExecuteDeleteCharacter () і CanExecuteDeleteCharacter (). Методу CanExecuteAddCharacter () не існує, тому що введення після натискання клавіші завжди дійсний.

Метод ExecuteAddCharacter () передбачає, що в параметрі передається символ, який Ви самі ввели. Так одна команда спільно використовується для кількох кнопок.

Метод CanExecuteDeleteCharacter () повертає true тільки при наявності символів, які можна видалити; в іншому випадку кнопка видалення повинна бути заблокована. Але цей метод викликається тільки при початковому встановленні прив'язки, а потім тільки при ініціюванні події CanExecuteChanged. Логіка ініціювання цієї події перебуває в set-методі InputString (), який порівнює повернені значення CanExecuteDeleteCharacter до і після зміни вхідного рядка.

Файл XAML створює екземпляр моделі уявлення як ресурс, а потім визначає DataContext в Grid. Зверніть увагу на простоту прив'язок Command для тринадцяти елементів управління Button і використання CommandParameter для цифрових і символьних клавіш:

Сама нецікава частина цього проекту - файл відокремленого коду, який тепер не містить нічого, крім виклику InitializeComponent. Задача виконана.

Winrt, кнопки і mvvm

Ви можете скористатися наявними можливостями Windows Runtime для запуску мобільного застосування для інтернет-магазину. Раніше, при розгляді розробки магазину на ASP.NET MVC ми не описали важливі деталі про те як відкрити Інтернет-магазин офіційно оформивши документи, створивши служби підтримки, доставки, організувавши роботу кур'єрів і т.д.