Разработка приложений на базе WPF и Silverlight

Разработка приложений на базе WPF

Показывать лекцию целиком

Цель

Освоить основные приемы разработки WPF-приложений на основе страничной организации приложения, создания меню, панели команд, табличных элементов управления и системы команд для выполнения задач приложения.

Создание приложения

Как правило, корпоративное приложение представляет собой программу, реализующую определенную бизнес-задачу (бизнес-функцию). Приложение должно взаимодействовать с данными, которые располагаются в базе данных информационной системы. Архитектура приложения обычно включает слой представления, бизнес-логики и данных. Функциональность каждого слоя приложения во многом определяется предметной областью информационной системы, но имеются и общие, основополагающие функции, которые присущи практически любому корпоративному приложению. Так в приложении необходимо разработать слой представления, который обеспечит интерфейс пользователя с системой. Интерфейс может быть создан с использованием Windows окон и страниц WPF, которые наполняются различными визуальными элементами контроля. Элементы контроля должны поддерживать визуальное представление функциональности системы для пользователя, проводить верификацию вводимых данных и взаимодействовать с бизнес-классами. Слой бизнес-логики приложения должен обеспечивать основную функциональность приложения: формировать бизнес-классы, реализовывать алгоритмы обработки данных, обеспечивать соединение с данными и их кэширование. Реализация данного слоя приложения может быть построена на базе классов, реализующих бизнес-логику, методами классов интерфейсных элементов или методами классов модели данных. Слой данных должен обеспечить взаимодействие приложения с данными системы управления базами данных. В корпоративных приложениях для этого наиболее целесообразно использовать платформу ADO.NET Entity Framework и модель EDM ( Entity Data Model ). Модель EDM описывает структуру данных независимо от формы хранения.

Для изучения вопросов проектирования корпоративных приложений рассмотрим основные подходы при разработке отдельной функции информационной системы, которая обеспечивает обработку данных по сотрудникам компании. Для учебного примера используется база данных TitlePresonal с небольшим набором таблиц и полей, а функциональность приложения предполагает обеспечение ввода, корректировки и удаление данных о сотрудниках компании. Разрабатываемое приложение должно обеспечивать хранение и обработку следующих данных по сотрудникам компании: фамилия; имя; отчество; должность; дата рождения; телефон; адрес электронной почты.

Функции приложения:

  • просмотр данных по сотрудникам;
  • ввод данных по новому сотруднику;
  • редактирование данных по сотруднику;
  • удаление данных по сотруднику;
  • поиск данных по сотруднику.
  • Для разработки приложения необходимо создать WPF-проект (рис 4.1). В окне " ), выбрать шаблоны Windows (2 – на рис 4.1), а среди имеющихся шаблонов задать Приложение WPF и в поле " Имя " ввести имя проекта WpfApplProject.

    После нажатия кнопки " ОК " будет сформирован шаблон проекта. При этом инструментальная система сгенерирует следующий XAML-документ:

    <Window x:Class="WpfApplProject.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="350" Width="525">
        <Grid>
            
        </Grid>
    </Window>
    (рис 4.1) Окно создания проекта

    Дизайнер проекта приведен на рис 4.2.

    (рис 4.2) Окно дизайнера проекта

    В приведенном XAML-документе имеется один элемент верхнего уровня <Window>. Дескриптор </Window> завершает весь документ. В XAML-документе приведено имя класса MainWindow

    x:Class="WpfApplProject.MainWindow

    два пространства имен

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml

    и три свойства:

    Title="MainWindow" Height="350" Width="525"

    Каждый атрибут соответствует определенному свойству класса .

    (рис 4.3) Окно MainWindow проекта

    Когда выполняется компиляция приложения, XAML-файл, который определяет пользовательский интерфейс ( MainWindow.xaml ), транслируется в объявление типа CLR , которое объединяется с логикой приложения из файла класса отдельного кода ( MainWindow.xaml.cs ).

    Метод InitializeComponent() генерируется во время компиляции приложения и в исходном коде не присутствует.

    Для программного управления элементами управления, описанными в XAML-документе, необходимо задать XAML атрибут Name. Так для задания имени элементу Grid необходимо записать следующую разметку:

    <Grid Name="grid">
            
    </Grid>

    Формирование начальной страницы приложения

    В последнее время разработчики корпоративных приложений начали осознавать преимущества технологий веб-дизайна, которые базируются на качественном дизайне, четком и понятном интерфейсе. Технология WPF позволяет создавать страничную модель (приложения), с готовыми средствами навигации. Как правило, для каждой страницы приложения создается файл XAML и файл отдельного кода, например на языке C#. При компиляции такого приложения компилятор создает производный класс страницы, который объединяет написанный код с генерируемыми автоматически связующими элементами.

    Страницы можно размещать внутри окон и внутри других страниц. В WPF при создании страничных приложений контейнером наивысшего уровня могут быть следующие объекты:

  • NavigationWindow, который представляет собой несколько видоизмененную версию класса Window ;
  • Frame, находящийся внутри другого окна или другой страницы;
  • Frame, обслуживаемый непосредственно в Internet Explorer.
  • Для вставки страницы внутрь окна будем использовать класс ). Автоматически будет сгенерирован экземпляр класса ).

    (рис 4.5) Выбор класса Frame в панели инструментов(рис 4.4) Фрейм с фиксированными границами

    В XAML-документ проекта будет добавлена следующая строка:

    <Frame Height="100" HorizontalAlignment="Left" 
    Name="frame1" VerticalAlignment="Top" Width="200" />

    С учетом того, что создается страничное приложение размеры фрейма не нужно фиксировать, поэтому изменим описание свойств фрейма:

    <Frame Name="frame1" Margin="3" />

    В результате фрейм заполнит всё окно (рис 4.6):

    (рис 4.6) Фрейм, заполняющий всё окно

    Созданный фрейм необходим для размещения в нем страницы WPF-приложения. Класс Page допускает наличие только одного вложенного элемента. Он не является элементом управления содержимым и наследуется от класса FrameworkElement. Класс Page имеет небольшой набор дополнительных свойств, которые позволяют настраивать его внешний вид, взаимодействовать с контейнером и использовать навигацию. Для перехода на другую страницу необходимо использовать навигацию.

    Класс Page имеет следующие свойства:

  • Background – принимает кисть, которая устанавливает заливку для фона;
  • Content – принимает один элемент, который отображается на странице. Обычно в роли такого элемента выступает контейнер макета (элемент Grid или StackPanel );
  • Foreground, FontFamile, FontSize – определяет используемый по умолчанию внешний вид для текста внутри страницы. Значение этих свойств наследуется элементами внутри страницы.
  • WindowWidth, WindowHeight – определяет внешний вид окна, в которое упаковывается страница (не действуют, если страница обслуживается во фрейме);
  • NavigationService – возвращает ссылку на объект NavigationService, которую можно использовать для отправки пользователя на другую страницу программным путем;
  • KeepAlive – определяет, должен ли объект страницы оставаться действующим после перехода пользователя на другую страницу;
  • ShowsNavigationUI – определяет, должны ли в обслуживающем данную страницу хосте отображаться навигационные элементы управления;
  • Title – устанавливает имя, которое должно применяться для страницы в хронологии навигации;
  • Window Title – устанавливает заголовок окна, который отображается в строке заголовка.
  • Добавим в проект начальную страницу. Для этого в ), а в раскрывающемся меню пункт ).

    В окне ).

    (рис 4.8) Меню создания страницы(рис 4.7) Окно Добавления нового элемента

    В дизайнере проекта сгенерируется страница ).

    (рис 4.9) Дизайнер страницы PageMain.xaml

    В сгенерированной странице в качестве контейнера верхнего уровня используется Grid. Заменим Grid на контейнер StackPanel.

    Объект StackPanel имеет свойство Background, которое предоставляет кисть для рисования фоновой области элемента. Данное свойство является сложным, т.е. должно задаваться в следующем виде:

    <StackPanel.Background>
    ... 
    </StackPanel.Background>

    Классы кистей наследуются от класса System.Windows.Media.Brush и обеспечивают различные эффекты при заполнении фона, переднего плана или границ элементов. Некоторые классы кистей приведены ниже:

  • LineaGradientBrush – рисует область, используя линейное градиентное заполнение, представляющее собой плавный переход от одного цвета к другому;
  • RadialGradientBrush – рисует область, используя радиальное градиентное заполнение, представляющее собой плавный переход от одного цвета к другому в радиальном направлении от центральной точки;
  • ImageBrush – рисует область, используя графическое изображение, которое может растягиваться, масштабироваться или многократно повторяться;
  • DrawingBrush – рисует область, используя объекты Drawing, которые могут быть пользовательскими фигурами или битовыми картами;
  • VisualBrush – рисует область, используя объекты Visual.
  • Создадим градиентную заливку созданной страницы. Для определения градиентной заливки необходимо создать объект LinearGradientBrush. Далее необходимо заполнить свойство LinearGradientBrush.GradientStops коллекцией объектов GradientStop. Каждый объект GradientStop имеет свойства Offset и Color, значение которых задаются в соответствии с синтаксисом " свойство - атрибут ". Свойство Offset может принимать значение от 0 до 1, определяя области заполнения градиентом от одного цвета к другому.

    Далее приведен фрагмент XAML-документа для определения градиентной заливки страницы.

    <StackPanel.Background>
        <LinearGradientBrush>
            <LinearGradientBrush.GradientStops>
                <GradientStop Offset="0.00" Color="#FF87A0DE"/>
                <GradientStop Offset="0.50" Color="Azure"/>
                <GradientStop Offset="1.00" Color="#FF009CFF"/>
            </LinearGradientBrush.GradientStops>
        </LinearGradientBrush>
    </StackPanel.Background>

    Добавим в страницу текстовую строку "Система внутреннего учета инвестиционной компании".

    <TextBlock Margin="25,30,25, 5" FontSize="20">
              Система внутреннего учета инвестиционной компании
    </TextBlock>

    Подключим созданную страницу к фрейму. Для этого в разметке фрейма необходимо указать источник его заполнения Source, а также определим свойства Foreground, BorderBrush и Background.

    <Frame Name="frame1" Margin="3"
       Source="PageMain.xaml" 
       Foreground="#FFE4D8D8" 
       BorderBrush="#FFF7F3F3" 
       Background="#FFB3D9E5"/>

    Главная страница приложения в дизайнере представлена на рис 4.10.

    (рис 4.10) Главная страница WPF-приложения в дизайнере

    Изменим свойство .

    (рис 4.11) Главная страница WPF-приложения

    Контейнером верхнего уровня главной страницы приложения является StackPanel. Для данной панели свойству Background, которое определяет кисть для рисования фоновой области, установлено сложное значение, определяющее градиентную заливку. С учетом того, что в приложении предполагается использовать аналогичную градиентную кисть и для других страниц, то целесообразным является сформировать значение для данной кисти в виде ресурса, разместив его на уровне приложения.

    <Application.Resources>
        <LinearGradientBrush x:Key="BackgroundWindowResource">
        	<GradientStop Offset="0.00" Color="#FF87A0DE"/>
        	<GradientStop Offset="0.50" Color="Azure"/>
        	<GradientStop Offset="1.00" Color="#FF009CFF"/>
        </LinearGradientBrush>
    </Application.Resources>

    Сформированный ресурс для градиентной кисти имеет ключ BackgroundWindowResource. Введение в XAML описание приложения ресурса требует провести изменение свойства Background панели StackPanel, использую расширения разметки и ссылку на статический ресурс BackgroundWindowResource.

    <StackPanel Background="{StaticResource BackgroundWindowResource}">
    . . . 
    </StackPanel>

    На странице WPF-приложения можно размещать элементы пользовательского интерфейса (элементы управления) для обеспечения взаимодействия пользователя с бизнес-логикой системы.

    Навигация страничного приложения

    Основная страница должна обеспечивать переход на другие страницы, обеспечивающие инферфейс для отдельных функций и выход из системы. Для перехода на другие страницы будем использовать гиперссылки. Гиперссылки позволяют пользователю перемещаться с одной страницы на другую. Элемент гиперссылки, соответствующий объекту класса Hyperlink, определяется следующей строкой:

    <Hyperlink >Текст Гиперссылки</Hyperlink>

    Класс Hyperlink имеет свойство NavigateUri. Данное свойство определяет на какую страницу будет переходить приложение при щелчке на соответствующей гиперссылке. Например, NavigateUri="Page2.xaml".

    В WPF гиперссылки являются не отдельными, а внутристроковыми потоковыми элементами, которые должны размещаться внутри другого поддерживающего их элемента. Это можно сделать, например в элементе TextBlock, который для гиперссылки является контейнером.

    На первой странице создадим следующие гиперссылки: Сотрудники, Клиенты, Договора, Ценные бумаги, Сделки и Справка. XAML-описание гиперссылки для страницы Сотрудники приведено ниже.

    <TextBlock Margin="25,30,5, 5">
      <Hyperlink NavigateUri="PageEmployee.xaml">Сотрудники</Hyperlink>
    </TextBlock>

    Для реализации функциональности первого окна WPF-приложения осталось добавить кнопку выхода.

    <Button Margin="25,15" Width="60" HorizontalAlignment="Left" 
    Command="Close" >Выход</Button>

    Результат компиляции и выполнения WPF-приложения приведен на рис 4.12.

    (рис 4.12) Начальная страница WPF-приложения с гиперссылками

    По условию учебного примера будем постепенно добавлять функциональность приложения в части обработки данных по сотрудникам компании.

    Создадим страницу с именем PageEmployee. В качестве основного контейнера будем использовать панель StackPanel. Определим для неё градиентную заливку, аналогичную начальной странице приложения.

    <Page x:Class="WpfApplProject.PageEmployee"
          xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
          xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
          xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
          xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
          mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="600"
    	Title="PageEmployee">
        <StackPanel Margin="3" Background="{StaticResource BackgroundWindowResource}">
        </StackPanel>

    Страница PageEmployee должна обеспечивать пользователю просмотр данных по сотрудникам, ввод данных по новому сотруднику, редактирование, поиск и удаление данных. Доступ пользователя к функциональности системы будем предоставлять через различные меню.

    Проектирование интерфейса

    В WPF можно создать основное и контекстное меню. Кроме того, можно создать панель инструментов с кнопками, которые будут реализовывать функциональность, аналогичную пунктам меню.

    Основное меню создается с помощью класса Menu, который представляет Windows элементы управления меню, позволяющие иерархически организовать элементы, связанные с командами и обработчиками событий. Меню формируют из объектов MenuItem (имя пункта меню) и Separator (разделитель). Класс MenuItem имеет свойство Header, которое определяет текст элемента меню. Данный класс может хранить коллекцию объектов MenuItem, которая соответствует подпунктам меню. Класс Separator отображает горизонтальную линию, разделяющую пункты меню. Добавим в StackPanel страницы PageEmployee XAML- описание меню, которое на верхнем уровне будет содержать два пункта Действие и Отчет. Пункт Действие включает подпункты Отменить, Создать, Редактировать, Сохранить, Найти и Удалить. Между пунктами Отменить, Создать и пунктами Найти, Удалить добавлены разделительные линии.

    <Menu>
        <MenuItem Header="Действие" >
            <MenuItem Header="Отменить" ></MenuItem>
            <Separator></Separator>
            <MenuItem Header="Создать" ></MenuItem>
            <MenuItem Header="Редактировать" ></MenuItem>
            <MenuItem Header="Сохранить" ></MenuItem>
            <MenuItem Header="Найти" />
            <Separator></Separator>
            <MenuItem Header="Удалить" ></MenuItem>
        </MenuItem>
        <MenuItem Header="Отчет"></MenuItem>
    </Menu>

    Запустим приложение и выберем ссылку ).

    (рис 4.13) Главное меню страницы PageEmployee

    Панель инструментов представляет специализированный контейнер для хранения коллекции элементов, обычно кнопок. Расположим в панели инструментов кнопки, функциональное назначение которых будет соответствовать подпунктам меню Действие, то есть Отменить, Создать, Редактировать, Сохранить, Найти и Удалить.

    На лицевой стороне кнопок поместим графическое изображение соответствующего действия. Для этого добавим в файл проекта папку Images и в неё включим графические объекты, которые можно найти в библиотеке графических объектов Visual Studio (папка VS2010ImageLibrary).

    После добавления графических файлов в проект они будут отображены в обозревателе решений (рис 4.14).

    (рис 4.14) Включение в проект папки Images с файлами рисунков

    Теперь можно разработать XAML-описание панели инструментов для страницы PageEmployee.

    Для каждой кнопки зададим свойство Name – имя объекта в программе и свойство ToolTip с текстом всплывающей подсказки при наведении указателя мыши на кнопку.

    Свойство Margin определяет внешние отступы для кнопки. Задание графического объекта для кнопки осуществляется определением для объекта Image источника Source, который должен соответствовать полному пути к графическому файлу.

    <ToolBar Name="ToolBar1" Margin="3">
    <Button Name="Undo" ToolTip="Отменить редактирование/создание" 
    Margin="5,2,5,2">
    <Image Source="Images/Undo.jpg" />
     </Button>
    	...
    </ToolBar>

    В дизайнере Visual Studio страница .

    (рис 4.15) Страниц PageEmployee с панелью инструментов в дизайнере

    После запуска приложения и выборе кнопки панели инструментов .

    Следующим шагом проектирования интерфейсных элементов для страницы PageEmployee является решение задачи формирования отображения данных по сотрудникам предприятия. Данная задача может быть решена различными способами: с помощью элементов контроля ListBox, ListView, TextBox, TextBlock, ComboBox, DataGrid и других.

    (рис 4.16) Страница PageEmployee с панелью инструментов

    В учебном примере будем использовать элемент контроля DataGrid для табличного отображения данных о сотрудниках. В качестве заголовка таблицы применим текстовый блок "Список сотрудников".

    Класс DataGrid представляет элемент управления, отображающий данные в настраиваемой сетке строк и столбцов. По умолчанию DataGrid автоматически создает столбцы, на основе источника данных. При этом генерируются следующие типы столбцов:

  • DataGridTextColumn – для отображения в ячейках столбцов текстового содержимого;
  • DataGridCheckBoxColomn – для отображения в ячейках столбцов логических данных;
  • DataGridComboBoxColomn – для отображения в ячейках столбцов данных, когда имеется набор элементов для выбора;
  • DataGridHyperlinkColomn – для отображения в ячейках столбцов элементов Uri.
  • Если разработчика не устраивает автоматическая генерация столбцов DataGrid можно её отключить. Для этого свойству AutoGenerateColumns необходимо присвоить значение false. Далее можно создать собственный набор столбцов ( Columns ), используя существующие типы столбцов или создать новый тип столбца с помощью шаблона DataGridTemplateColumn.

    DataGrid поддерживает множество способов настройки отображения данных. В табл. 4.1 приведен список стандартных сценариев.

    Сценарии настройки отображения данных
    Сценарий Подход
    Переменные цвета фона Задайте для свойства AlternationIndex значение 2 или больше, а затем назначьте объект Brush свойствам RowBackground и AlternatingRowBackground.
    Определение поведения при выборе ячейки и строки Установите свойства SelectionMode и SelectionUnit.
    Настройка внешнего вида заголовков, ячеек и строк Примените новый Style к свойствам ColumnHeaderStyle, RowHeaderStyle, CellStyle или RowStyle.
    Доступ к выбранным элементам Проверьте свойство SelectedCells, чтобы получить выделенные ячейки, и свойство SelectedItems, чтобы получить выбранные строки.
    Настройка взаимодействия с пользователем Установите свойства CanUserAddRows, CanUserDeleteRows, CanUserReorderColumns, CanUserResizeColumns, CanUserResizeRows и CanUserSortColumns.
    Отмена или изменение автоматически созданных столбцов Обработать событие AutoGeneratingColumn.
    Заморозка столбца Задайте для свойства FrozenColumnCount значение 1 и переместите столбец в крайнюю левую позицию, задав для свойства DisplayIndex значение 0.
    В качестве источника данных используются данные XML. Привяжите ItemsSource в DataGrid к запросу XPath, представляющему коллекцию элементов. Создайте каждый столбец в DataGrid. Свяжите каждый столбец, указав XPath для привязки к запросу, который получает свойство из источника элемента.

    XAML-документ описания интерфейсных элементов страницы PageEmployee имеет следующий вид.

    <TextBlock Margin="5" >Список сотрудников</TextBlock>
    <DataGrid Name="DataGridEmployee" >
        <DataGrid.Columns>
            <DataGridTextColumn Header="Фамилия"/>
    	.	.	.
            <DataGridComboBoxColumn Header="Должность" />
            <DataGridTemplateColumn Header="Дата рождения" />
    	. . .
        </DataGrid.Columns>
    </DataGrid>

    Для полей Фамилия, Имя, Отчество, Телефон и Электронная почта используется тип столбца DataGridTextColumn. Так как должность сотрудника задается в соответствии с данными из справочника базы данных, то есть предоставляется список, из которого можно произвести выбор, для данного столбца используется тип DataGridComboBoxColumn. Для столбца Дата рождения используется специфичный шаблон, то есть тип DataGridTemplateColumn, который будет описан позднее.

    С учетом добавленных интерфейсных элементов страница .

    (рис 4.17) Страница PageEmployee с элементом DataGrid в дизайнере

    После запуска приложения страница

    (рис 4.18) Страница PageEmployee с элементом DataGrid

    На данном этапе проектирования приложения на странице PageEmployee размещены все необходимые элементы контроля.

    Разработка бизнес-логики

    Для реализации функциональности приложения в части обработки информации о сотрудниках предприятия необходимо для страницы PageEmployee установить механизм запуска задач ( Отменить, Создать, Редактировать, Сохранить, Найти и Удалить ) при выборе соответствующих пунктов меню и нажатии на кнопки панели инструментов. Технология WPF предлагает модель команд для выполнения такой привязки.

    Модель команд обеспечивает делегирование событий определенным командам и управление доступностью элементов управления в зависимости от состояния соответствующей команды. В WPF команда представляет собой задачу приложения и механизм слежения за тем, когда она может быть выполнена. В то же время сама команда не содержит конкретного кода выполнения задачи. Одна и та же команда может быть привязана к одному или нескольким интерфейсным элементам приложения. Инициируют команду источники, которые могут быть различными элементами управления, например пункты меню MenuItem или кнопки – Button. Целевым объектом команды является элемент, для которого предназначена эта команда.

    Классы, реализующие команды должны поддерживать интерфейс ICommand. В этом интерфейсе определены два метода Execute, CanExecute и событие CanExecuteChanged.

    В WPF имеется библиотека базовых команд. Команды доступны через статические свойства следующих статических классов:

  • ApplicationCommands ;
  • NavigationCommands ;
  • EditingCommands ;
  • MediaCommands.
  • Для создания пользовательских команд целесообразно использовать классы RoutedCommand, который имеет реализацию интерфейса ICommand.

    В разрабатываемом приложении для функций Отменить, Создать, Сохранить и Найти будем использовать команды и библиотеки WPF – статический класс ApplicationCommands, а для функций Редактировать и Удалить спроектируем пользовательские команды.

    Для разработки пользовательских команд добавим в проект папку Commands и в ней создадим класс DataCommands.

    public class DataCommands
        {
            public static RoutedCommand Delete { get; set;}
            public static RoutedCommand Edit { get; set; }
            static DataCommands()
            {
                InputGestureCollection inputs = new InputGestureCollection();
                inputs.Add(new KeyGesture(Key.E, ModifierKeys.Control, "Ctrl+E"));
                Edit = new RoutedCommand("Edit", typeof(DataCommands), inputs);
                inputs = new InputGestureCollection();
                inputs.Add(new KeyGesture(Key.D, ModifierKeys.Control, "Ctrl+D"));
                Delete = new RoutedCommand("Delete", typeof(DataCommands), inputs);
            }
        }

    В классе DataCommands объявлены два свойства Delete и Edit типа RoutedCommand. Класс RoutedCommand определяет команду, реализующую ICommand. В конструкторе данного класса определяется объект inputs типа InputGestureCollection. Класс InputGestureCollection представляет упорядоченную коллекцию объектов InputGesture, которые позволяют с помощью класса KeyGesture задать комбинацию клавиш для вызова команды.

    Для использования страницей PageEmployee пользовательских команд в XAML-документе необходимо добавить пространство имен, где расположен класс DataCommands. Данному пространству имен присвоим ссылку command.

    xmlns:command="clr-namespace:WpfApplProject.Commands"

    Теперь для страницы PageEmployee сформируем коллекцию объектов CommandBinding, которая осуществляет привязку команд для данного элемента и объявляет связь между командой, ее событиями и обработчиками.

    <Page.CommandBindings>
    <CommandBinding Command="Undo" 
    Executed="UndoCommandBinding_Executed" 
                            CanExecute="UndoCommandBinding_CanExecute" />
    		. . . 
        </Page.CommandBindings>

    Для класса CommandBinding свойство Command определяет ссылку на соответствующую команду, а свойства Executed и CanExecute задают обработчики событий при выполнении команды.

    На странице приложения используются следующие команды: Отменить, Создать, Редактировать, Поиск, Сохранить и Удалить. Команды могут быть доступны или недоступны пользователю при работе приложения. Это проверяет метод CanExecute при генерации события CanExecuteChanged, которое вызывается при изменении состояния команды. Доступность команд определяется состоянием, в котором находится приложение. В тоже время выполнение какой-либо команды переводит, как правило, приложение в какое-либо другое состояние. Для проектируемого приложения можно определить следующие состояния:

  • первоначальная загрузка страницы (1);
  • просмотр данных по всем сотрудникам (2);
  • редактирование данных по отдельному сотруднику (3);
  • создание новой записи по сотруднику в базе данных (4).
  • На рис 4.19 приведена диаграмма состояний приложения, на которой кружками обозначены состояния, а дуги соответствуют переходам при выполнении определенной команды.

    (рис 4.19) Диаграмма состояний приложения

    На основе диаграммы состояний построим таблицу доступности команд в различных состояниях (табл. 4.2).

    Доступность команд в различных состояниях приложения
    Состояние Доступность команд
    Сохранить Отменить Создать Поиск Редактировать Удалить
    1 false false true true true true
    2 false false true true true true
    3 true true false false false false
    4 true true false false false false

    Из табл. 4.2 видно, что в приложении режим доступности команд в состояниях 1 и 2 противоположно режиму доступности команд в состояниях 3 и 4. Фактически для приложения имеются два режима (один объединяет состояния 1 и 2, а второй – состояния 3 и 4), управлять которыми можно с помощью логической переменной.

    В код программы класса PageEmployee введем логическое поле isDirty для управления доступностью команд.

    private bool isDirty = true;

    В код класса добавим обработчики (реализация метода Executed ), определяющие бизнес-логику приложения. На данном этапе проектирования системы обработчики будут содержать только вывод сообщений о вызове команды и изменение поля isDirty. Код обработчика команды Удалить приведен ниже.

    private void UndoCommandBinding_Executed(object sender, 
    ExecutedRoutedEventArgs e)
            {
                MessageBox.Show("Отмена");
                isDirty = true;
            }

    В дальнейшем в обработчики добавим код для обеспечения требуемой функциональности.

    В коде класса остается добавить обработчики (реализация метода CanExecute ), которые управляют доступностью команд. Так как при анализе табл. 4.2 было выявлено, что для приложения различимо только два состояния доступности команд, то и обработчиков тоже достаточно иметь в программе два.

    private void EditCommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
    {
       e.CanExecute = isDirty;
    }
    private void SaveCommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
    {
       e.CanExecute = !isDirty;
    }

    Теперь необходимо модифицировать XAML-документ в части задания свойства Command при описании пунктов меню и панели инструментов для привязки команд.

    При описании меню (Menu) XAML-документ модифицирован следующим образом.

    <Menu>
        <MenuItem Header="Действие" BorderThickness="3" >
            <MenuItem Header="Отменить" Command="Undo"></MenuItem>
    	. . . 
    </Menu>

    Соответствующие изменения XAML-документа необходимо провести и для панели инструментов ToolBar.

    <ToolBar Name="ToolBar1" Margin="3">
         <Button Name="Undo" Margin="5,2,5,2" Command="Undo"
                 ToolTip="Отменить редактирование/создание" >
             <Image Source="Images/Undo.jpg">
            </Image>
         </Button>
    ...
    </ToolBar>

    При выполнении приложения различные состояния доступности пунктов меню приведены на рис 4.20 и рис 4.21.

    (рис 4.21) Состояние доступности пунктов меню после загрузки приложения(рис 4.20) Состояние доступности пунктов меню после редактирования данных

    При выборе какого-либо пункта меню, например пункта Создать, система выдает сообщение (рис 4.22).

    (рис 4.22) Выбор пункта меню Создать

    Следующим этапом разработки приложения является создание источника данных, который обеспечит взаимодействие приложения с базой данных.

    Ключевые термины

    Приложение WPF, пространство имен, атрибут, частичный класс, простое свойство, сложное свойство, фрейм, гиперссылка, меню, панель инструментов, команда, привязка команд.

    MainWindow, xmlns, partial, InitializeComponent, NavigationWindow, Frame, FrameworkElement, Page, Hyperlink, NavigateUri, Menu, MenuItem, DataGrid, DataGridTextColomn, DataGridCheckBoxColomn, DataGridComboBoxColomn, DataGridHyperlinkColomn, DataGridTemplateColumn, ICommand, Execute, CanExecute, CanExecuteChanged, CommandBinding.

    Краткие итоги

    В данной теме были рассмотрены вопросы разработки страничного WPF-приложения, организации перехода по страницам, создания системы меню, панели команд, табличного представления информации для просмотра и редактирования данных, системы универсальных команд многократного использования.

    Ресурсы для углубленного изучения

  • Application Development // http://msdn.microsoft.com/ru-ru/library/ms754032.aspx.
  • Система макета // http://msdn.microsoft.com/ru-ru/library/ms745058.aspx.
  • Библиотека классов // http://msdn.microsoft.com/ru-ru/library/ms753307.aspx.
  • , стр. 82 – 174, 288 – 362.
  • , стр. 1171 - 1212
  • , стр. 297 – 351,565 – 625.
  • , стр. 1048 – 1070.
  • Вопросы для самопроверки

  • Какой дескриптор верхнего уровня используется в WPF-проектах?
  • Какое назначение метода InitializeComponent() в коде класса?
  • Сколько можно вложить элементов в класс Page?
  • Поясните назначение свойства Content класса Page.
  • В каких контейнерах можно размещать страницы WPF?
  • Какие элементы контроля используются для перехода между страницами приложения?
  • Из каких объектов можно сформировать меню приложения?
  • Какие типы столбцов автоматически генерируются для элемента управления DataGrid?
  • Поясните назначение модели команд WPF.
  • Какие в WPF имеется библиотеки базовых команд?
  • Как можно управлять доступностью команд в приложении WPF?
  • Вернуться к учебному плану