
- •«Комп’ютерні технології та програмування»
- •Оглавление
- •Лабораторная работа 1 Знакомство с wpf
- •Теоретические сведения
- •1. Знакомство с редактором xaml
- •2.Создание приложения wpf
- •3) Создание обработчиков событий
- •Задание к лабораторной работе
- •Варианты индивидуальных заданий
- •Контрольные вопросы
- •Лабораторная работа 2 Подробное введение в разработку приложений wpf
- •Теоретические сведения
- •1. Ознакомление с управлением проектами wpf в msVisualStudio
- •2. Создание тестового приложения wpf
- •Задание к лабораторной работе
- •Варианты индивидуальных заданий
- •Контрольные вопросы
- •Лабораторная работа 3 Макет программы и основные элементы управления
- •Теоретически сведения
- •1. Макет в wpf
- •2. Новые возможности wpf
- •3. Элемент управления Canvas
- •4. Элементуправления StackPanel
- •5. Элементуправления WrapPanel
- •6. Элемент управления DockPanel
- •7. Элемент Grid
- •Задание к лабораторной работе
- •Варианты индивидуальных заданий
- •Контрольные вопросы
- •Лабораторная работа 4
- •Xaml и код, расширения разметки и ресурсы
- •Теоретически сведения
- •2. Ссылки на ресурсы в wpf
- •Задание к лабораторной работе
- •Варианты индивидуальных заданий
- •Контрольные вопросы
- •Лабораторная работа 6 Свойства зависимости
- •Теоретически сведения
- •1. Особенности свойств clr
- •2. Наследование значений свойств зависимости
- •3. Присоединенные свойства
- •4. Метаданныесвойствзависимости
- •5. Проверка допустимости значения
- •Задание к лабораторной работе
- •Варианты индивидуальных заданий
- •Контрольные вопросы
- •Лабораторная работа 7 Привязка данных
- •Теоретически сведения
- •1. Концепция привязки данных
- •2. Свойство DataContext
- •3. Основные понятия привязки данных
- •4. Синтаксис привязки данных
- •5. Привязка к элементам интерфейса пользователя
- •6. Привязка к xml
- •7. Привязка к коллекции
- •8. Преобразователи значений привязки данных
- •9. Проверкапривязкиданных
- •Задание к лабораторной работе
- •Варианты индивидуальных заданий
- •Контрольные вопросы
9. Проверкапривязкиданных
При использовании привязок одна из основных проблем связана с проверкой введенных данных, особенно если свойству Binding.Mode присвоено значение TwoWay или OneWayToSource. Необходимо сделать так, чтобы в исходный объект передавались только допустимые данные. Это также необходимо для того, чтобы не отправлять бессмысленные данные в постоянное хранилище (базу данных, файл и т. д.).
Корпорация Microsoft знала об этой проблеме и предоставила нам все необходимое для создания правильных привязок данных с возможностями проверки. Далее мы рассмотрим три основных способа выполнения проверки.
В состав демонстрационного решения входит проект Validation, который при выполнении выглядит так (рис. 7.7):
Рис. 7.7 - Демонстрационная программа
Изменение внешнего вида при проверке. В проект Validation было включено несколько стилей. На данный момент вам всего лишь нужно знать, что недопустимый ввод в элементе TextBox приведет к изменению внешнего вида соответствующего элемента за счет стиля или шаблона. И еще один момент. Всплывающая подсказка TextBox.ToolTip отображает сообщение проверки правильности. Это также реализуется с помощью более «умной» привязки.
<Setter Property="ToolTip"
Value="{Binding RelativeSource={RelativeSource Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
Проверка на основе исключений. Возможно, самый простой способ проверки привязки — применить правило проверки на основе исключений. Любое исключение, возникшее при попытке обновления привязанного свойства, можно использовать для информирования пользователя. Как правило, в этом случае отображается сообщение подсказки и изменяется внешний вид элемента TextBox. Воспользуемся простым тестовым классом для привязки.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Validation
{
public class TestClass
{
public int Age { get; set; }
public DateTime StartDate { get; set; }
public TestClass()
{
StartDate = DateTime.Now;
}
}
}
Зададим привязку в коде программной части следующим образом (с помощью свойства DataContext).
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Validation
{
/// <summary>
/// Логикавзаимодействиядля Window1.xaml
/// </summary>
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
this.DataContext = new TestClass();
txtIDataErrorInfoAge.DataContext = new Person();
txtIDataErrorInfoName.DataContext = new Person();
}
}
}
Затем можно использовать проверку на основе исключений в XAML.
<Window x:Class="Validation.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Validation"
Title="Window1" Height="800" Width="800"
WindowStartupLocation="CenterScreen">
<Window.Resources>
<!--Стиль TextBox проверки на основе исключений и правила проверки -->
<Style x:Key="textStyleTextBox" TargetType="TextBox">
<Setter Property="Foreground" Value="#333333" />
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={RelativeSource Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
<!--Шаблон элемента управления для проверки на основе правила проверки -->
<ControlTemplate x:Key="validationTemplate">
<DockPanel>
<TextBlock Foreground="Red" FontSize="20">!</TextBlock>
<AdornedElementPlaceholder/>
</DockPanel>
</ControlTemplate>
</Window.Resources>
<ScrollViewer HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto">
<StackPanel Orientation="Vertical">
<!-- Проверка на основе исключений -->
<Label Content="Exception Based Validitaion" Margin="5,0,0,0"
FontSize="14" FontWeight="Bold" />
<StackPanel Orientation="Horizontal" Margin="10,10,10,10"
Background="Gainsboro">
<TextBlock TextWrapping="Wrap" Text="Exception Based Validitaion,
type an non integer value" Width="400"/>
<TextBox Name="txtException" Style="{StaticResource textStyleTextBox}"
Width="120" Height="25" Margin="5,0,0,0"
Text="{Binding Path=Age,
UpdateSourceTrigger=PropertyChanged,
ValidatesOnExceptions=True}" />
</StackPanel>
</StackPanel>
</ScrollViewer>
</Window>
Следуетотметить, что Binding.ValidatesOnExceptions=True, поэтомудлясозданиясообщенияпроверки, отображаемогововсплывающейподсказкеTextBox.Tooltip, будетиспользоватьсяправилопроверки ExceptionValidationRule, встроенноев WPF.
Проверка на основе пользовательских правил проверки. Хотя пользовательские правила проверки похожи на встроенное правило ExceptionValidationRule, в их случае мы используем собственное правило для создания сообщений проверки. Ниже приведен пример пользовательского правила проверки, которое проверяет, что введенное значение имеет тип DateTime и относится к будущему времени.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Globalization;
namespace Validation
{
class FutureDateValidationRule : ValidationRule
{
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
DateTime date;
try
{
date = DateTime.Parse(value.ToString());
}
catch (FormatException)
{
return new ValidationResult(false, "Value is not a valid date.");
}
if (DateTime.Now.Date > date)
{
return new ValidationResult(false, "Please enter a date in the future.");
}
else
{
return ValidationResult.ValidResult;
}
}
}
}
Зададимпривязкувкодепрограммнойчастиследующимобразом (спомощьюсвойства DataContext).
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Validation
{
/// <summary>
/// Логикавзаимодействиядля Window1.xaml
/// </summary>
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
this.DataContext = new TestClass();
txtIDataErrorInfoAge.DataContext = new Person();
txtIDataErrorInfoName.DataContext = new Person();
}
}
}
Затем можно использовать это пользовательское правило проверки FutureDateValidationRule в коде XAML.
<Window x:Class="Validation.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Validation"
Title="Window1" Height="800" Width="800"
WindowStartupLocation="CenterScreen">
<Window.Resources>
<!--Стиль TextBox проверки на основе исключений и правила проверки -->
<Style x:Key="textStyleTextBox" TargetType="TextBox">
<Setter Property="Foreground" Value="#333333" />
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={RelativeSource Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
<!--Шаблон элемента управления для проверки на основе правила проверки -->
<ControlTemplate x:Key="validationTemplate">
<DockPanel>
<TextBlock Foreground="Red" FontSize="20">!</TextBlock>
<AdornedElementPlaceholder/>
</DockPanel>
</ControlTemplate>
</Window.Resources>
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<StackPanel Orientation="Vertical">
<!-- Проверка на основе правила проверки -->
<Label Content="ValidationRule Based Validitaion" Margin="5,0,0,0"
FontSize="14" FontWeight="Bold" />
<StackPanel Orientation="Horizontal" Margin="10,10,10,10" Background="Gainsboro">
<TextBlock TextWrapping="Wrap" Text="ValidationRule Based Validitaion,
type a future date" Width="400"/>
<TextBox Name="txtStartDate"
Validation.ErrorTemplate="{StaticResource validationTemplate}"
Style="{StaticResource textStyleTextBox}" Width="150" Height="25"
Margin="5,0,0,0">
<TextBox.Text>
<!-- Поскольку требуется предоставить дочерний объект ValidationRule,
необходимо использовать синтаксис элемента свойства -->
<Binding Path="StartDate" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<local:FutureDateValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</StackPanel>
</StackPanel>
</ScrollViewer>
</Window>
Нужноотметить, что, посколькутребуетсядобавитьновоеправило FutureDateValidationRule всвойствоBinding.ValidationRules, необходимоиспользоватьсинтаксисэлементасвойства.
Использование интерфейса IDataErrorInfo в .NET 3.5. В выпуске платформы .NET 3.5 добавлены язык LINQ и несколько усовершенствований WPF. Одним из таких усовершенствований стал новый интерфейс IDataErrorInfo, который изменяет место, где выполняется проверка. Она переносится из отдельных классов проверки в сами бизнес-объекты.
Пример простого класса, который реализует интерфейс IDataErrorInfo.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
namespace Validation
{
/// <summary>
/// Это новый метод .NET 3.5, где у каждого бизнес-объекта свои средства проверки
/// с помощью интерфейса IDataErrorInfo
/// </summary>
public class Person : IDataErrorInfo
{
public int Age { get; set; }
public string Name { get; set; }
public Person()
{
this.Age = 0;
this.Name = "sacha";
}
#region IDataErrorInfo Members
public string Error
{
get
{
return null;
}
}
/// <summary>
/// Проверяет, было ли свойство изменено, и предоставляет
/// сообщение об ошибке на основе некоторых правил
/// </summary>
/// <param name="name">The property that changed</param>
/// <returns>a error message string</returns>
public string this[string name]
{
get
{
string result = null;
// По сути, для каждого проверяемого свойства необходим один из этих блоков
switch (name)
{
case "Age":
if (this.Age < 0 || this.Age > 150)
{
result = "Age must not be less than 0 or greater than 150.";
}
break;
case "Name":
if (this.Name == string.Empty)
{
result = "Name can't be empty";
}
if (this.Name.Length > 5)
{
result = "Name can't be more than 5 characters";
}
break;
}
return result;
}
}
#endregion
}
}
Посуществу, этотинтерфейспозволяетпроверятьизмененноесвойствоспомощьюсинтаксиса public string this[string name]. В XAML все немного по-другому. Для привязки больше не нужно использовать отдельный класс проверки, можно просто использовать сокращенный синтаксис XAML с фигурными скобками { } либо более подробный синтаксис.
<Window x:Class="Validation.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Validation"
Title="Window1" Height="800" Width="800"
WindowStartupLocation="CenterScreen">
<Window.Resources>
<!--Стиль TextBox проверки на основе исключений и правила проверки -->
<Style x:Key="textStyleTextBox" TargetType="TextBox">
<Setter Property="Foreground" Value="#333333" />
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={RelativeSource Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<StackPanel Orientation="Vertical">
<!-- Проверкасиспользованиеминтерфейса IDataErrorInfo -->
<Label Content="IDataErrorInfo Based Validitaion" Margin="5,0,0,0"
FontSize="14" FontWeight="Bold" />
<StackPanel Orientation="Horizontal" Margin="10,10,10,10" Background="Gainsboro">
<TextBlock TextWrapping="Wrap" Text="IDataErrorInfo Based Validitaion,
type a number below 0 or above 150 " Width="400"/>
<!-- Область ввода возраста -->
<Label Content="Age"/>
<TextBox Name="txtIDataErrorInfoAge" Style="{StaticResource textStyleTextBox}"
Width="60" Height="25" Margin="5,0,0,0"
Text="{Binding Path=Age,
UpdateSourceTrigger=PropertyChanged,
ValidatesOnExceptions=True,
ValidatesOnDataErrors=True}" />
<!-- Областьвводаимени -->
<Label Content="Name"/>
<TextBox Name="txtIDataErrorInfoName" Style="{StaticResource textStyleTextBox}"
Width="60" Height="25" Margin="5,0,0,0"
Text="{Binding Path=Name,
UpdateSourceTrigger=PropertyChanged,
ValidatesOnDataErrors=True}" />
</StackPanel>
</StackPanel>
</ScrollViewer>
</Window>
Обратитевнимание, чтовэтотразиспользуетсявыражение Binding.ValidatesOnDataErrors=True. Это означает, что будет включено правило DataErrorValidationRule, использующее реализацию интерфейса IDataErrorInfo.