Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Wpf-АК11.doc
Скачиваний:
3
Добавлен:
01.03.2025
Размер:
3.47 Mб
Скачать

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.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]