Опубликован: 08.07.2011 | Уровень: для всех | Доступ: платный
Самостоятельная работа 1:

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

< Лекция 5 || Самостоятельная работа 1: 12345 || Лекция 6 >

Задание 6. Реализовать валидацию данных – 1 час.

Разработку пользовательского правила валидации данных рассмотрим на примере проверки шаблона при вводе свойства Email, то есть адреса электронной почты. В строке ввода должны присутствовать символы @ и точка.

Спроектируйте правило проверки для привязки, которое будет использоваться со столбцом "Электронная почта". Для этого необходимо создать класс правила проверки EmailRule, который должен наследоваться от базового класса ValidationRule. Класс EmailRule поместите во вновь созданную папку ValidationRules проекта.

public class EmailRule: ValidationRule
{
    public override ValidationResult Validate(object value,
                    System.Globalization.CultureInfo cultureInfo)
    {
        string email = string.Empty;
        if (value != null)
        {
            email = value.ToString();
        }
        else
            return new ValidationResult(false, " Адрес электронной почты не задан! ");
        if (email.Contains("@") && email.Contains("."))
        {
            return new ValidationResult(true, null);
        }
        else
        {
            return new ValidationResult(false, 
"Адрес электронной почты должен содержать 
символы @ и (.) точки \n Шаблон адреса:   adres@mymail.com");
        }
       
    }
}

В классе правил проверки необходимо переопределить метод Validate, который возвращает результат проверки – экземпляр класса ValidationResult. Если проверка проходит успешно, тогда возвращаемым значением является объект класса ValidationResult, который создается с параметрами true и null.

return new ValidationResult(true, null);

Если проверка приводит к выявлению несоответствия, то класс ValidationResult, создается с параметрами false и строка сообщения об ошибке.

return new ValidationResult(false, "Адрес электронной почты должен содержать 
символы @ и (.) точки \n Шаблон адреса:   adres@mymail.com");

Для использования объекта EmailRule в XAML-документе страницы приложения добавьте в её описание пространство имен WpfApplProject.ValidationRules.

xmlns:rule="clr-namespace:WpfApplProject.ValidationRules"

Внесите изменения в XAML-описание колонки Электронная почта.

<DataGridTextColumn  Header="Электронная почта" EditingElementStyle="{StaticResource errorStyle}">
    <DataGridTextColumn.Binding >
        <Binding Path="Email" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged"
ValidatesOnExceptions ="True" >
            <Binding.ValidationRules>
                 <rule:EmailRule />
          </Binding.ValidationRules>
        </Binding>
    </DataGridTextColumn.Binding>
</DataGridTextColumn>

Для привязки Binding свойству ValidatesOnExceptions задайте значение true. Задание данного свойства обеспечивает использование элемента ExceptionValidationRule. ExceptionValidationRule предоставляет встроенное правило проверки, проверяющее возникновение исключений при обновлении свойства источника. В случае возникновения ошибки механизм привязки создает ValidationError с исключением и добавляет его в коллекцию Validation.Errors привязанного элемента. Отсутствие ошибок сбрасывает это состояние обратной связи проверки, если другое правило не вызовет событие проверки.

Правило проверки задается для свойства ValidationRules класса Binding.

<Binding.ValidationRules>
	<rule:EmailRule />
</Binding.ValidationRules>

Модель привязки данных WPF позволяет связывать ValidationRules с объектом Binding, используя пользовательские правила. Подсистема привязки проверяет каждое из ValidationRule, связанных с привязкой, каждый раз, когда вводимое значение (значение свойства цель привязки) переносится в свойство источник привязки.

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

<Style x:Key="errorStyle" TargetType="{x:Type TextBox}">
    <Setter Property="Padding" Value="-2"/>
    <Style.Triggers>
        <Trigger Property="Validation.HasError" Value="True">
            <Setter Property="Background" Value="Red"/>
            <Setter Property="BorderThickness" Value="1" />
            <Setter Property="ToolTip"
                    Value="{Binding RelativeSource={RelativeSource Self},
                Path=(Validation.Errors)[0].ErrorContent}"/>
        </Trigger>
    </Style.Triggers>
</Style>

В стиле errorStyle триггер срабатывает, когда свойство Validation.HasError принимает значение true. При этом цвет заливки ячейки становится красным и при наведении на ячейку указателя мыши выводится сообщение-подсказка (свойство ToolTip ), текст которого определяется в правиле проверки EmailRule при обнаружении ошибки ( Validation.Errors )[0] .ErrorContent ).

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

<DataGrid.RowValidationErrorTemplate>
    <ControlTemplate>
        <Grid Margin="0,-2,0,-2"
              ToolTip="{Binding RelativeSource={RelativeSource FindAncestor,
                                           AncestorType={x:Type DataGridRow}},
            Path=(Validation.Errors)[0].ErrorContent}">
            <Ellipse StrokeThickness="0" Fill="Red" Width="{TemplateBinding FontSize}"
                                                 Height="{TemplateBinding FontSize}" />
            <TextBlock Text="!" FontSize="{TemplateBinding FontSize}" FontWeight="Bold"
                                    Foreground="White" HorizontalAlignment="Center"  />
        </Grid>
    </ControlTemplate>
</DataGrid.RowValidationErrorTemplate>

Задание 7. Разработать методы поиска данных – 1 час.

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

Общий подход к поиску реализации поиска данных, получаемых из базы данных, рассмотрим на примере поиска сотрудника по фамилии и должности. Для реализации функций поиска добавьте на страницу приложения элементы контроля в соответствии с рис. 6.18.

Дизайн страницы с элементами контроля для поиска

увеличить изображение
Рис. 6.18. Дизайн страницы с элементами контроля для поиска

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

<TextBlock Name="TextBlockSurname" Text="Фамилия" />
<TextBlock Name="TextBlockTitle" Text="Должность" />
<TextBox Name="TextBoxSurname" TextChanged="TextBoxSurname_TextChanged"/>
<ComboBox Name="ComboBoxTitle
ItemsSource="{Binding Source={StaticResource listTitle}}"
DisplayMemberPath="Title1"
SelectionChanged="ComboBoxTitle_SelectionChanged"/>
<Button Name="ButtonFindSurname" ToolTip="Поиск по фамилии"
IsEnabled="False" Click="ButtonFindSurname_Click">
<Image Source="Images/Search.jpg"/>
</Button>
<Button Name="ButtonFindTitle" ToolTip="Поиск по должности"
IsEnabled="False" Click="ButtonFindTitle_Click">
<Image Source="Images/Search.jpg" />
</Button>

Элементы контроля размещаем в рамке BorderFind. В рамке располагаем сетку gridFind с тремя колонками и строками. Первая строка в объединенных колонках содержит текстовый блок find с текстом "Поиск". Во второй и третьей строках первой колонки размещены текстовые блоки TextBlockSurname и TextBlockTitle соответственно. Во второй колонке размещаются элементы контроля для ввода информации: текстовый блок TextBoxSurname (вторая строка) и выпадающий список ComboBoxTitle (третья строка). В третьей колонке размещаются кнопки: для поиска по фамилии ButtonFindSurname (вторая строка) и для поиска по должности ButtonFindTitle (третья строка).

При загрузке страницы PageEmployee рамка BorderFind невидима, так как свойству Visibility задано значение "Hidden". При активизации команды Find из меню или панели инструментов запускается обработчик FindCommandBinding_Executed, который делает рамку ButtonFindTitle видимой.

private void FindCommandBinding_Executed(object sender, 
ExecutedRoutedEventArgs e)
        {
            BorderFind.Visibility = System.Windows.Visibility.Visible;
        }

Если в текстовом блоке TextBoxSurname не введена информация или в выпадающем списке ComboBoxTitle не сделан выбор, то кнопки ButtonFindSurname и ButtonFindTitle будут недоступны.

При вводе информации в текстовый блок TextBoxSurname запускается обработчик TextBoxSurname_TextChanged, который делает доступной кнопку ButtonFindSurname.

private void TextBoxSurname_TextChanged(object sender, TextChangedEventArgs e)
{
    ButtonFindSurname.IsEnabled = true;
    ButtonFindTitle.IsEnabled = false;
    ComboBoxTitle.SelectedIndex = -1;
}

При нажатии кнопки ButtonFindSurname запускается обработчик ButtonFindSurname_Click.

private void ButtonFindSurname_Click(object sender, RoutedEventArgs e)
{
    string surname = TextBoxSurname.Text;
    DataEntitiesEmployee = new TitlePresonalEntities();
    ListEmployee.Clear();
    ObjectQuery<Employee> employees = DataEntitiesEmployee.Employees;
    var queryEmployee = from employee in employees
                            where employee.Surname == surname
                            select employee;
    foreach (Employee emp in queryEmployee)
    {
        ListEmployee.Add(emp);
    }
    if (ListEmployee.Count > 0)
    {
        DataGridEmployee.ItemsSource = ListEmployee;
        ButtonFindSurname.IsEnabled = true;
        ButtonFindTitle.IsEnabled = false;
    }
    else
        MessageBox.Show("Сотрудник с фамилией \n"+surname+"\n не найдан",
             "Предупреждение", MessageBoxButton.OK, MessageBoxImage.Warning);
}

В данном обработчике события Click выполняется LINQ запрос на получение данных из базы TitlePersonal в соответствии с заданной фамилией сотрудника.

var queryEmployee = from employee in employees
                        where employee.Surname == surname
                        select employee;

При выполнении запроса по фамилии страница PageEmployee имеет вид, приведенный на рис. 6.19

Поиск по фамилии

увеличить изображение
Рис. 6.19. Поиск по фамилии

Найденные данные по сотруднику можно редактировать и удалять.

Если поиск в базе данных не привел к нахождению информации, то выдается информационное сообщение ( рис. 6.20).

Неудачный поиск по фамилии

увеличить изображение
Рис. 6.20. Неудачный поиск по фамилии

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

private void ComboBoxTitle_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    ButtonFindTitle.IsEnabled = true;
    ButtonFindSurname.IsEnabled = false;
    TextBoxSurname.Text = "";
}

При нажатии на кнопку ButtonFindTitle срабатывает обработчик ButtonFindTitle_Click.

private void ButtonFindTitle_Click(object sender, RoutedEventArgs e)
{
    DataEntitiesEmployee = new TitlePresonalEntities();
    ListEmployee.Clear();
    
    Title title = ComboBoxTitle.SelectedItem as Title;
    ObjectQuery<Employee> employees = DataEntitiesEmployee.Employees;
    var queryEmployee = from employee in employees
                        where employee.TitleID == title.ID
                        orderby employee.Surname
                        select employee;
    foreach (Employee emp in queryEmployee)
    {
        ListEmployee.Add(emp);
    }
    DataGridEmployee.ItemsSource = ListEmployee;
}

При запуске данного обработчика выполняется LINQ-запрос к базе данных TitlePersonal для выборки данных по сотрудникам, имеющим заданную должность.

Для обновления выводимого списка сотрудников в приложение добавлена функция обновления, которая реализуется командой Refresh. Данная команда добавлена в коллекцию команд страницы PageEmployee.

<Page.CommandBindings>
....
        <CommandBinding Command="Refresh" Executed="RefreshCommandBinding_Executed" />
... 
    </Page.CommandBindings>

Вызов команды добавлен в меню

<MenuItem Header="Обновить" Command="Refresh"/>

и панель инструментов

<Button Name="Refresh" Margin="5,2,5,2"  Command="Refresh"
        ToolTip="обновить данные по сотрудникам">
    <Image Source="Images/Refresh.jpg" ></Image>
</Button>

Основное назначение обработчика команды RefreshCommandBinding_Executed – обновление списка ListEmployee.

private void RefreshCommandBinding_Executed(object sender, 
ExecutedRoutedEventArgs e)
{
    RewriteEmployee();
    DataGridEmployee.IsReadOnly = false;
    isDirty = false;
    SetUnvisibilityFind();
}

Команда "Обновить" обычно используется после поиска данных или ввода данных по новому сотруднику.

< Лекция 5 || Самостоятельная работа 1: 12345 || Лекция 6 >
Александр Петров
Александр Петров

При загрузке данных из БД возникает исключение InvalidOperationException с сообщением: Элемент коллекции должен быть пустым перед использованием ItemsSource. Знаю, что для заполнения DataGrid можно использовать коллекции Items или ItemsSource, но одновременно их использовать нельзя: если задано значение для свойства ItemsSource и в коде C# добавляется элемент в Items, возникает исключение. 
Вопрос, как отследить и отключить добавление элемента в Items?

Максим Спиридонов
Максим Спиридонов

В пятой лекции на второй странице в компиляторе выскакивает ошибка в строчке :

ObjectQuery<Employee> employees = DataEntitiesEmployee.Employees;

Ошибка CS0029

Не удается неявно преобразовать тип "System.Data.Entity.DbSet<WpfApplProject.Employee>" в "System.Data.Entity.Core.Objects.ObjectQuery<WpfApplProject.Employee>".

в using прописал все как положено, здесь похоже именно с преобразованием типов проблемы

Igor Chelyadinski
Igor Chelyadinski
Беларусь, Минск, №54, 2013
Валентина Алешина
Валентина Алешина
Россия