- •Разделы дисциплины и виды занятий в часах
- •Общая информация о курсе
- •Введение
- •Об авторах
- •Лекция 1. Краткое знакомство с моделью программирования xaml.
- •Краткий экскурс в историю графических интерфейсов пользователя
- •Windows Presentation Foundation – три в одном
- •Декларативное программирование ui и xaml
- •Основы модели ui и xaml
- •Элементы xaml
- •Элементы управления
- •Графические примитивы
- •Службы документов
- •Преобразования и анимация
- •Ресурсы и стили
- •Разделение труда дизайнера и разработчика
- •Xaml – текущая поддержка
- •Модель программирования xaml
- •Ключевые термины
- •Краткие итоги
- •Xaml подчиняется всем правилам wellformed xml, в частности это означает, что xaml документ:
- •Вариант 2 Задачи 3.
- •Вариант 2 Задачи 8.
- •Вариант 3 Задачи 8.
- •Xaml может использоваться для описания ui:
- •Xaml может использоваться:
- •Лабораторная работа 1. Размещение элементов управления.
- •Задания для самостоятельного выполнения
- •Учебный элемент. Создание нового проекта для Silverlight в VisualStudio 2010.
- •Шаг 1. Создание проекта
- •Шаг 2. Добавляем элементы интерфейса для xaml-страниц
- •Учебный элемент. Silverlight: размещение элементов.
- •Шаг 1. Принципы разметки
- •Шаг 2. Создаем Twitter-приложение
- •Шаг 3. Навигация
- •Шаг 4. Размещаем элементы для страницы поиска
- •Шаг 5. Изменяем UriMapper для Search.Xaml
- •Краткие итоги
- •Литература
- •Лекция 2. Сведения о wpf и Silverlight: Введение и архитектура платформ.
- •Независимость от разрешения
- •Архитектура Silverlight
- •Состав ядра представления:
- •В подмножество .Net Framework входят:
- •Архитектура wpf
- •Иерархия классов
- •Элементы управления wpf
- •Компоновочные элементы управления в системе wpf
- •Ключевые термины
- •Краткие итоги
- •Лекция 3. Стили и шаблоны элементов управления wpf.
- •Создание стиля
- •Использование стиля
- •Размещение стилей
- •Основы шаблонов
- •Создание шаблона
- •Повторное использование шаблона Control’а
- •Связывание в Шаблонах
- •Установка шаблонов через стили
- •Повторное использование настроек цвета
- •Ключевые термины
- •Краткие итоги
- •Вариант 2 Задачи 5.
- •Задания для самостоятельного выполнения
- •Учебный элемент. Стили и ресурсы в wpf
- •Шаг 1. Применение стиля в xaml документе
- •Шаг 2. Динамическое применение стилей в коде приложения
- •Поддержка множества целевых платформ
- •Клиентский профиль .Net
- •Визуальный конструктор Visual Studio
- •Графика Шейдеры
- •Кеширование графики
- •Новые функции для анимации
- •Усовершенствования двухмерной графики.
- •Поддержка Generics
- •Ключевые термины
- •Краткие итоги
- •Набор для практики
- •Литература
- •Лекция 5. Silverlight и wpf в Visual Studio 2010 (продолжение).
- •Новые возможности Silverlight 4. Silverlight 5: основные нововведения ожидаемого релиза. Хронология Silverlight
- •Связывание с данными Отладка
- •Поддержка Full-Trust внутри браузера
- •Обработка множественного нажатия кнопки мыши
- •Поддержка выбора элементов в ItemsControl с клавиатуры
- •Повышение полномочий для приложений в браузере
- •Поддержка нескольких окон
- •Доступ к файловой системе для приложений с повышенными привилегиями
- •Установка имени файла по умолчанию в SaveFileDialog
- •Улучшение работы с аудио
- •Изменение скорости проигрывания медиа
- •Поддержка пульта дистанционного управления
- •Работа с текстом
- •Отладка при связывании с данными
- •Улучшенная поддержка шаблона mvvm
- •Использование связывания в стилях
- •Неявные шаблоны
- •Другие изменения
- •Краткие итоги
- •Вариант 2 Задачи 2.
- •Вариант 2 Задачи 4.
- •Вариант 2 Задачи 6.
- •Вариант 3 Задачи 6.
- •Литература
- •Лекция 6. Привязка данных в технологиях wpf и Silverlight
- •Принципы работы с данными
- •Модель данных в .Net
- •Всепроникающее связывание
- •Преобразование данных
- •Ресурсы
- •Основные принципы связывания
- •Привязка к объектам clr
- •Редактирование
- •Ключевые термины
- •Вариант 2 Задачи 5.
- •Шаг 1. Создание повторно используемой функциональности
- •Устанавливаем стили для элементов управления
- •Двусторонняя привязка данных
- •Шаг 2. Добавление обработки событий клавиатуры
- •Шаг 3. Инкапсуляция разметки и логики в элемент управления UserControl
- •Реализация элемента управления
- •Добавление кода
- •Использование пользовательского элемента управления
- •Многократное использование
- •Шаг 1. Рефакторинг кода адресной формы
- •Код внутри класса AdressCustomControl
- •Определение свойств зависимости
- •Шаг 2. Рефакторинг кода разметки указателя цвета
- •Шаг 3. Оптимизация шаблона элемента управления
- •Манипулирование частями шаблона
- •Документирование частей шаблона
- •Ключевые термины
- •Вариант 2 Задачи 3.
- •Вариант 3 Задачи 3.
- •Задача 4. Вариант 1 Задачи 4.
- •Вариант 2 Задачи 4.
- •Вариант 3 Задачи 4.
- •Задача 5. Вариант 1 Задачи 5.
- •Вариант 2 Задачи 5.
- •Вариант 3 Задачи 5.
- •Задача 6. Вариант 1 Задачи 6.
- •Вариант 2 Задачи 6.
- •Вариант 3 Задачи 6.
- •Задача 7. Вариант 1 Задачи 7.
- •Вариант 2 Задачи 7.
- •Вариант 3 Задачи 7.
- •Литература
- •Лабораторная работа 3. Пользовательские элементы управления.
- •Задания для самостоятельного выполнения
- •Учебный элемент. Создание пользовательского элемента управления средствами дизайнера VisualStudio Шаг 1. Создание Silverlight проекта
- •Шаг 2. Создание пользовательского элемента управления
- •Шаг 5. Использование Custom Control
- •Учебный элемент. Создание пользовательского элемента управления средствами дизайнера Expression Blend
- •Шаг 1. Инкапсуляция ui в User Control
- •Шаг 2. Привязка адресов к нашему AddressUserControl – контроллеру.
- •Краткие итоги
- •Литература
- •Лекция 8. Использование событий, команд и триггеров в технологиях wpf и Silverlight
- •Принципиальные основы действий
- •Композиция элементов
- •Слабая связь
- •Декларативные действия
- •События
- •Команды
- •Команды и привязка к данным
- •Триггеры
- •Добавление триггеров к данным
- •Добавление триггеров к элементам управления
- •Вариант 2 Задачи 2.
- •Вариант 2 Задачи 6.
- •Вариант 2 Задачи 8.
- •Вариант 3 Задачи 8.
- •Литература
- •Лабораторная работа 4. Использование команд в технологиях wpf и Silverlight
- •Задания для самостоятельного выполнения
- •Учебный элемент. Использование стандартных и нестандартных команд Шаг 1. Использование стандартных команд.
- •Шаг 2. Использование нестандартных команд.
- •Шаг 3. Перевод команд из неактивного состояния в активное и обратно.
- •Краткие итоги
- •Литература
- •Лекция 9. Применение паттерна mvvm как оптимального при проектировании wpf и Silverlight приложений
- •Паттерн Model-View-ViewModel (mvvm)
- •Обязанности и характеристики классов.
- •Класс представления (View).
- •Класс модели представления (View Model).
- •Представление или Модель Представления?
- •Класс модели (Model).
- •Взаимодействие классов.
- •Привязка данных.
- •Реализация iNotifyPropertyChanged.
- •Реализация iNotifyCollectionChanged.
- •Реализация iCollectionView.
- •Команды.
- •Реализация Command Objects.
- •Подтверждение правильности данных и сообщение об ошибках.
- •Реализация iDataErrorInfo.
- •Создание и соединение.
- •Создание модели представления через xaml.
- •Создание модели представления программно.
- •Создание представления, определенного как шаблон данных.
- •Вариант 2 Задачи 1.
- •Вариант 3 Задачи 1.
- •Задача 2. Вариант 1 Задачи 2.
- •Вариант 2 Задачи 2.
- •Вариант 3 Задачи 2.
- •Задача 3. Вариант 1 Задачи 3.
- •Вариант 2 Задачи 3.
- •Вариант 2 Задачи 6.
- •Вариант 3 Задачи 6.
- •Задача 7. Вариант 1 Задачи 7.
- •Вариант 2 Задачи 7.
- •Вариант 3 Задачи 7.
- •Литература
- •Лабораторная работа 5. Реализация wpf проекта с помощью mvvm toolkit’а
- •Задания для самостоятельного выполнения
- •Учебный элемент. Реализация паттерна mvvm средствами Model-View-ViewModel Toolkit 0.1
- •Шаг 1. Постановка задачи
- •Шаг 4. Редактирование
- •Шаг 5. Команды
- •Краткие итоги
- •Литература
- •Лекция 10. Проектирование приложения с учетом использования единого опыта разработки для настольных и Web-проектов.
- •Подходы к решению задачи
- •Разработка кроссплатформенного проекта с самого начала
- •Разработка для одной платформы с последующим портированием на другую
- •Инструментарий
- •Создание ссылок на файлы в Visual Studio
- •Создание ссылок на xaml файлы
- •Директивы препроцессора
- •Разделяемые классы
- •Синхронное использование xaml
- •Решение проблем недостающего функционала Отсутствие FrameworkPropertyMetadata
- •Отсутствие приведения значения
- •Отсутствие метода OverrideMetadata()
- •Отсутствие зависимых свойств только для чтения
- •Отсутствие класса Brushes
- •Конструкторы по умолчанию для геометрий
- •Отсутствие свойства No PathSegment.IsStroked
- •Краткие итоги
- •Принцип инверсии зависимостей
- •Формы инверсии зависимостей
- •IoC контейнер
- •Определение моделей представления
- •Определение представлений
- •Сопоставление модели представления и представления
- •Реализация iNotifyPropertyChanged средствами аспектно-ориентированного программирования
- •Краткие итоги
- •IoC контейнер в mef инкапсулируется классом …
- •Понятие iChildViewModelManager
- •Реализация ChildViewModelBase
- •Реализация ModalChildViewModelBase
- •Реализация MessageViewModel
- •Краткие итоги
- •Литература Лабораторная работа 6. Построение кроссплатформенного Silverlight/wpf приложения.
- •Создание проектов
- •Краткие итоги
- •Список литературы
Команды
Большинство событий в WPF связаны с деталями реализации конкретных элементов управления: изменился выбор, произошел щелчок, передвинулась мышь и т.д. События хороши, когда нужно выполнить некоторый код в ответ на получение извещения от элемента управления, но часто бывает нужен более абстрактный подход.
Предположим, что нужно реализовать возможность завершения программы. Конечно, необходимо включить соответствующий пункт в меню. Поэтому мы первым делом определим в разметке меню:
<MenuItem Header=’_File’> <MenuItem Header=’E_xit’ Click=’ExitClicked’ /> </MenuItem>
В файле с кодом реализуем обработчик события:
void ExitClicked(object sender, RoutedEventArgs e) { Application.Current.Shutdown(); }
Пока все хорошо, но давайте еще добавим текст, в который входит гиперссылка, позволяющая выйти из программы:
<TextBlock> Вас приветствует моя программа. Если вам надоело, можете <Hyperlink Click=’ExitClicked’>выйти</Hyperlink>. </TextBlock>
Вот теперь начинаются неприятности. Мы делаем слишком много предположений о реализации метода ExitClicked, например, что его сигнатура совместима с событием Hyperlink.Click и что он не делает ничего другого, кроме завершения приложения. К тому же, в разметку оказались зашиты произвольно выбранные имена методов из файла с кодом, а дизайнер, который конструирует пользовательский интерфейс, не будет знать, к каким обработчикам событий привязаться.
Для решения этой проблемы и придуманы команды. Они позволяют назначить имя желаемому действию. Чтобы воспользоваться командой, нужно сделать три вещи:
определить назначение команды;
написать реализацию команды;
создать для команды триггер.
Основой всех команд в WPF является довольно простой интерфейс ICommand:
public interface ICommand { event EventHandler CanExecuteChanged; bool CanExecute(object parameter); void Execute(object parameter); }
Метод CanExecute позволяет выяснить, находится ли команда в таком состоянии, когда ее можно выполнить. Обычно элементы управления пользуются этим методом, чтобы активировать или деактивировать себя. Иными словами, если ассоциированная с кнопкой команда возвращает false из метода CanExecute, то кнопка деактивируется. Такое обобществление понятия «активен» позволяет нескольким элементам, связанным с одной командой, поддерживать согласованное состояние. Метод Execute основной, его вызов означает выполнение команды. Реализация класса Button (как и любого другого элемента управления, поддерживающего команды) должна включать примерно такой код:
protected virtual void OnClick(RoutedEventArgs e) { if (Command != null && Command.CanExecute(CommandParameter)) { Command.Execute(CommandParameter); } // ... продолжение реализации }
Для определения новой команды мы должны реализовать интерфейс ICommand. Поскольку мы хотим, чтобы наша команда закрывала приложение, то можем вызвать метод Shutdown:
public class Exit: ICommand { public bool CanExecute(object parameter) { return true; } public event EventHandler CanExecuteChanged; public void Execute(object parameter) { Application.Current.Shutdown(); } }
Для привязки команды к пункту меню или к ссылке мы указываем в свойстве Command имя команды – Exit:
<MenuItem Header=’_File’> <MenuItem Header=’E_xit’> <MenuItem.Command> <l:Exit /> </MenuItem.Command> </MenuItem> </MenuItem> ... <Hyperlink> <Hyperlink.Command><l:Exit /></Hyperlink.Command> ... </Hyperlink>
Так как команда часто вызывается из нескольких мест, принято заводить статическое поле, содержащее экземпляр команды:
public partial class Window1: Window { public static readonly ICommand ExitCommand = new Exit(); ... }
Дополнительный плюс такой реализации заключается в том, что реализацию класса Exit можно скрыть, объявив, что поле имеет тип ICommand. Теперь Exit можно сделать закрытым классом, а в разметке привязаться к статическому полю:
<MenuItem Header=’_File’> <MenuItem Header=’E_xit’ Command=’{x:Static l:Window1.ExitCommand}’ /> </MenuItem>
Таким образом, наше окно может раскрывать свою функциональность в виде команд. Однако тут имеется интересная проблема. Сейчас команда Exit реализована на глобальном уровне; мы можем вызвать ее из любого места и тем самым завершить приложение. Предположим, однако, что «exit» должно означать закрытие текущего окна. В таком случае хотелось бы отделить реализацию выхода от определения, как это было с событиями.
Простейший способ добиться такого разделения состоит в том, чтобы прибегнуть к системе событий. Мы можем определить новое событие, связанное с командой, и воспользоваться маршрутизацией для уведомления компонентов:
class Exit : ICommand { public static readonly RoutedEvent ExecuteEvent = EventManager.RegisterRoutedEvent( «Execute», RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(Exit)); ... }
Поскольку для этого события задана стратегия Bubble, оно будет всплывать от источника. Чтобы возбудить событие, мы изменим реализацию метода Execute, так чтобы он искал текущий элемент (в данном примере мы воспользовались для этой цели методом Keyboard.FocusedElement, но могли бы остановиться на любом механизме обнаружения «текущего»), а затем возбуждал подходящее событие:
public void Execute(object parameter) { RoutedEventArgs e = new RoutedEventArgs(Exit.ExecuteEvent, Keyboard.FocusedElement); Keyboard.FocusedElement.RaiseEvent(e); }
Это подводит нас к идее привязки команд – возможности отделить реализацию команды от ее назначения. Вернемся к классу Window1 и добавим в него реализацию команды:
public partial class Window1: Window { ... public Window1() { InitializeComponent(); AddHandler(Exit.ExecuteEvent, ExitExecuted); } void ExitExecuted(object sender, RoutedEventArgs e) { this.Close(); } }
Тут возможно некоторое недопонимание. Напомним, что цель команды – предложить абстракцию того, что должно происходить. В данном случае элемент управления (скажем, MenuItem) вызывает команду в ответ на событие (к примеру, Click). При нашей реализации команда Exit возбудит событие Execute, которое распространится по дереву элементов, а объект Window сможет подписаться на него и выполнить те или иные действия:
Пользователь щелкает по пункту меню.
MenuItem вызывает метод Execute команды.
Реализация команды Exit возбуждает событие Exit.Execute от имени элемента, имеющего фокус (в данном случае MenuItem).
Событие всплывает вверх по дереву.
Window получает событие Exit.Execute.
Window выполняет обработчик события (закрывает окно).
Можно было бы пойти дальше и включить в интерфейс ICommand средства поддержки привязок к вводу (для обработки ввода с клавиатуры, от мыши и пера), параметров и прочего. Однако в каркасе есть встроенный класс RoutedCommand, который большую часть всего этого уже умеет делать. Маршрутизируемые команды позволяют полностью отделить реализацию команды от ее назначения. Паттерн определения новой команды похож на RoutedEvent и DependencyProperty. Команда определяется как статическое свойство; это просто уникальный маркер, обозначающий ее идентичность:
public partial class Window1: Window { public static readonly ICommand ExitCommand = new RoutedCommand(«Exit», typeof(Window1)); ... }
Чтобы при выполнении команды что-то происходило, нужно связать с командой обрабатывающий код (аналогично тому, как мы поступили с событием в предыдущем примере). Маршрутизируемые команды всплывают (как и события), поэтому можно добавить привязку к команде в корневое окно и таким образом увидеть все команды. Для привязки к команде нужно указать вид интересующей команды и код, который будет выполняться при ее поступлении:
public Window1() { InitializeComponent(); CommandBindings.Add(new CommandBinding(ExitCommand, ExitExecuted)); } void ExitExecuted(object sender, ExecutedRoutedEventArgs e) { this.Close(); }
Привязка к команде позволяет решить, следует ли активировать команду, а также с помощью свойства InputBindings отобразить на команды действия по вводу данных:
<Window x:Class=’EssentialWPF.Window1’ xmlns=’http://schemas.microsoft.com/winfx/2006/xaml/presentation’ xmlns:x=’http://schemas.microsoft.com/winfx/2006/xaml’ xmlns:l=’clr_namespace:EssentialWPF’ Title=’EssentialWPF’> <Window.InputBindings> <KeyBinding Key=’A’ Modifiers=’Control’ Command=’{x:Static l:Window1.ExitCommand}’ /> </Window.InputBindings> ... </Window>
Еще одна существенная особенность – это понятие о «безопасных командах». С некоторыми командами, например вырезания, копирования и вставки, сопряжены определенные угрозы. С целью гарантировать, что система выполняет такие операции только по явному запросу пользователя (или если разрешает система управления цифровыми правами), класс RoutedCommand может отслеживать, была ли команда инициирована пользователем.
Использование команд четко отделяет отображение от поведения. Заведя единственное имя, обозначающее семантическое действие, мы обходим многие проблемы тесной связи, которые могут возникнуть при попытке ассоциировать несколько элементов управления с одним обработчиком события. В общем случае логику приложения следует реализовывать в терминах команд, а не обработчиков событий. Во многих типичных ситуациях, когда принято устанавливать прямую ассоциацию с обработчиком события, лучше воспользоваться триггерами. О них мы расскажем чуть ниже.
