Общие сведения о связывании данных

Привязка данных Windows Presentation Foundation (WPF) обеспечивает простой и согласованный способ представления данных и взаимодействия с ними в приложениях. Можно связывать элементы с данными из разнообразных источников данных в форме объектов common language runtime (CLR) и XML. Элементы управления ContentControl, например Button, элементы управления ItemsControl, например ListBox и ListView обладают встроенной возможностью включения гибких стилей для отдельных элементов данных и коллекций элементов данных. Представления сортировки, фильтрации и группировки могут быть организованы поверх данных.

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

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

В этом разделе содержатся следующие подразделы.

  • Понятие о привязке к данным
  • Основные понятия связывания данных
  • Создание привязки
  • Преобразование данных
  • Привязка к коллекциям
  • Шаблоны данных
  • Проверка данных
  • Механизм отладки
  • Связанные разделы

Понятие о привязке к данным

Привязка к данным является процессом, который устанавливает связь между приложением UI и бизнес-логикой. Если привязка имеет правильные параметры и данные предоставляют правильные уведомления, то при изменении значений данных в элементах, которые привязаны к данным, автоматически отражаются изменения. Привязка к данным может также означать, что если внешнее представление данных в элементе изменяется, то базовые данные могут автоматически обновляться для отражения изменений. Например если пользователь изменяет значение в элементе TextBox, базовое значение данных автоматически обновляется, чтобы отразить это изменение.

Привязка к данным обычно используется для того, чтобы поместить сервер или локальную конфигурацию данных в формы или другие элементы управления UI. В WPF эта концепция расширяется и уже включает привязку широкого диапазона свойств к различным источникам данных. В WPF, свойства зависимостей элементов могут быть привязаны к объектам CLR (включая объекты ADO.NET или объекты, связанные с веб-службами и веб-свойствами), и к данным XML.

Привязку данных можно рассмотреть на примере следующего приложения UIсо страницы Примера привязки данных (содержимое страницы может отображаться на английском языке):

Снимок экрана примера привязки данных

Выше показан интерфейс UI приложения, который отображает список лотов аукциона. Приложение демонстрирует следующие возможности связывания данных:

  • Содержимое ListBox привязано к коллекции объектов AuctionItem. Объект AuctionItem имеет такие свойства как Description StartPrice StartDate, Category, SpecialFeatures и т.д.

  • Данные (объекты AuctionItem), отображаемые в ListBox, используются в шаблоне таким образом, что для каждого элемента показаны его описание и текущая цена. Это делается с помощью DataTemplate. Кроме того, внешний вид каждого элемента зависит от значенияSpecialFeatures отображаемого объекта AuctionItem. Если значением SpecialFeatures объекта AuctionItem является Color, элемент имеет синюю границу. Если значением является Highlight, элемент имеет оранжевые границы и помечается звездочкой. Раздел Создание шаблонов данных содержит сведения о создании шаблонов данных.

  • Пользователь может группировать, фильтровать или сортировать данные с помощью предоставленных элементов CheckBox. На рисунке, приведенном выше, выбраны элементы CheckBox "Группировать по категориям" и "Сортировать по категориям и дате". Обратите внимание, что данные группируются по категориям продуктов, а имена категорий приводятся в алфавитном порядке. Из рисунка трудно заметить, что элементы в каждой категории также сортируются по начальной дате. Это делается с использованием представления коллекции. В разделе Привязка к коллекциям обсуждаются представления коллекций.

  • Когда пользователь выбирает элемент, ContentControl отображает подробное описание выбранного элемента. Это называется Скрипт "основной/подробности". В разделе Скрипт "основной/подробности" содержатся сведения об этом типе скрипта привязки.

  • Типом свойства StartDate является DateTime, который возвращает дату, включая время с точностью до миллисекунды. В этом приложении пользовательский преобразователь был использован таким образом, чтобы дата отображалась в укороченном формате. В разделе Преобразование данных представлены сведения о преобразователях.

При нажатии кнопки Добавить продукт, появляется следующая форма:

Добавить страницу списка продуктов

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

На этом рисунке не показана логика проверки, предоставленная в TextBoxStart Date. Если пользователь вводит недопустимую дату (недопустимый формат или прошедшую дату), он будет уведомлен ToolTip и красным восклицательным знаком после TextBox. В разделе Проверка данных обсуждается создание логики проверки.

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

Основные понятия связывания данных

Этот подраздел состоит из следующих пунктов.

  • Направление потока данных
  • Что инициирует обновления источника

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

Основная схема привязки данных

Как показано в приведенном выше рисунке, связывание данных является по существу мостом между целью привязки и источником привязки. На рисунке представлены следующие основные концепции связывания данных WPF:

  • Обычно каждая привязка имеет четыре компонента: объект цели привязки, свойство цели, источник привязки и путь к значению используемого источника привязки. Например, если требуется связать содержимое TextBox со свойством Имя объекта Сотрудник, объектом цели является TextBox, свойством цели является свойство Text, используемым значением является Имя, а объектом источника является объект Сотрудник.

  • Свойством цели должно быть свойство зависимостей. Большинство свойств UIElement является свойствами зависимостей, а большинство свойств зависимостей, за исключением свойств, доступных только для чтения, по умолчанию поддерживает связывание данных. (Свойства зависимости можно определить только через типы DependencyObject, а все элементы UIElement производятся от DependencyObject).

  • Хотя на рисунке это не показано, однако следует отметить, что объект источника привязки не обязательно должен быть пользовательским объектом CLR. Привязка данных WPF поддерживает данные в форме объектов CLR и XML. Например, источником привязки может быть UIElement, любой объект списка, объект CLR, связанный с данными ADO.NET или веб-службами, или узел XmlNode, содержащий данные XML. Дополнительные сведения см. в разделе Общие сведения об источниках привязки.

В процессе чтения других разделов software development kit (SDK) важно помнить, что при связывании цель привязки привязывается к источнику привязки. Например, при отображении некоторых базовых данных XML в ListBox с использованием привязки данных, ListBox привязывается к данным XML.

Чтобы установить привязку, используйте объект Binding. В остальной части этого раздела обсуждаются многие понятия, связанные с некоторыми свойствами и использованием объекта Binding.

Направление потока данных

Как упоминалось ранее и как показывает стрелка на приведенном выше рисунке, поток данных связывания может идти от цели привязки к источнику привязки (например, исходное значение изменяется, когда пользователь изменяет значение TextBox) и/или от источника привязки к цели привязки (например, содержимое TextBox обновляется при изменениях в источнике привязки), если источник привязки предоставляет соответствующие уведомления.

Возможно, требуется, чтобы в приложении пользователи могли изменить данные и передать их обратно объекту источника. Или может потребоваться не предоставлять пользователям возможности обновления источника данных. Это можно регулировать с помощью свойства Mode объекта Binding. На следующем рисунке показаны различные типы потоков данных:

Поток данных привязки данных

  • Связывание OneWay приводит к изменениям свойства источника для автоматического обновления свойства цели, но изменения свойства цели не передаются обратно к свойству источника. Этот тип привязки подходит, если привязка элемента управления неявно доступна только для чтения. Например, можно привязаться к источнику, такому как биржевые сводки, или, возможно, свойство цели не имеет интерфейса для внесения изменений, например цвета фона привязанной к данным таблицы. Если отсутствует необходимость отслеживать изменения свойства цели, можно использовать режим привязки OneWay, при котором удастся избежать издержек режима привязки TwoWay.

  • Тип связывания TwoWay вызывает изменения либо свойства цели, либо свойства источника для автоматического обновления другого. Этот тип привязки подходит для изменяемых форм или других полностью интерактивных скриптов UI. Большинство свойств являются свойствами по умолчанию для связывания OneWay, но некоторые свойства зависимостей (обычно свойства изменяемых пользователями элементов управления, такие как свойство Text элемента TextBox и свойство IsChecked элемента CheckBox) являются свойствами по умолчанию для связывания TwoWay. Определить программный способом, связано ли по умолчанию свойство зависимостей односторонним или двусторонним способом, можно, получив метаданные свойства с помощью GetMetadata, а затем проверив логическое значение свойства BindsTwoWayByDefault.

  • Связывание OneWayToSource является обратным к связыванию OneWay; оно обновляет свойство источника при изменении свойства цели. Одним примером скрипта является пересчет значения цели из UI.

  • На рисунке не показана привязка OneTime, которая вызывает инициализацию свойства цели свойством источника, но последующие изменения при этом не распространяются. Это означает, что, если в контексте данных производятся изменения или меняется объект, это изменение не отражается в целевом свойстве. Этот тип привязки можно применять, если для использования данных подходит снимок или данные являются статическими. Этот тип привязки также может использоваться, если необходимо инициализировать целевое свойство с некоторым значением из исходного свойства, и контекст данных не известен заранее. По существу, это является простой формой связывания OneWay, которая обеспечивает лучшую производительность в случаях, когда значение цели не изменяется.

Обратите внимание, что для обнаружения изменений в источнике (применимо для связывания OneWay и TwoWay), источник должен реализовывать подходящий механизм уведомления об изменении свойства, такой как INotifyPropertyChanged. Пример реализации класса INotifyPropertyChanged см. в разделе Практическое руководство. Реализация уведомления об изменении свойства.

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

Что инициирует обновления источника

Привязки типовTwoWay и OneWayToSource отслеживают изменения свойства цели и передают их обратно к источнику. Это называется обновлением источника. Например, можно изменять текст элемента TextBox для изменения базового значение источника. Как описано в последнем разделе, направление потока данных определяется значением свойства привязки Mode.

Однако обновляется ли значение источника при изменении текста или после завершения изменения текста и отвода указателя мыши от элемента TextBox? Свойство UpdateSourceTrigger привязки определяет, что инициирует обновление источника. Точки стрелок вправо на следующем рисунке показывают роль свойства UpdateSourceTrigger:

Схема UpdateSourceTrigger

Если значение элемента UpdateSourceTrigger равно PropertyChanged, значение, на которое указывает правая стрелка привязок TwoWay или OneWayToSource, обновляется сразу же после изменения свойства цели. Однако если значение UpdateSourceTrigger равно LostFocus, значение обновляется только тогда, когда свойство цели теряет фокус.

Аналогично свойству Mode, различные свойства зависимостей имеют различные значения по умолчанию для UpdateSourceTrigger. Значением по умолчанию для большинства свойств зависимостей является PropertyChanged, в то время как свойствоText по умолчанию имеет значение LostFocus. Это означает, что обновления цели обычно происходят при изменении свойства цели, что подходит для CheckBox и других простых элементов управления. Однако для текстовых полей обновления после каждого нажатия клавиши уменьшают производительность и не дают пользователю обычной возможности удаления предыдущего символа и исправления ошибок ввода до того, как новое значение будет зафиксировано. Именно поэтому свойство Text по умолчанию имеет значение LostFocus вместо PropertyChanged.

Сведения о том, как найти значение по умолчанию UpdateSourceTrigger свойства зависимостей, см. на странице свойства UpdateSourceTrigger.

В следующей таблице представлены примеры скриптов для каждого значения UpdateSourceTrigger, использующие TextBox как пример:

Значение UpdateSourceTrigger

Когда обновляется значение источника

Пример скрипта для TextBox

LostFocus (значение по умолчанию для TextBox.Text)

Возникает при потере фокуса элементом управления TextBox.

TextBox, который связан с логикой проверки (см. раздел "Проверка данных")

PropertyChanged

При вводе в TextBox

Элемент управления TextBox в окне чата

Explicit

Когда приложение вызывает UpdateSource

Элемент управления TextBox в редактируемой форме (обновляет значения источника только при нажатии пользователем кнопки отправки)

Пример см. в разделе Практическое руководство. Управление обновлением источника из поля TextBox.

Создание привязки

Этот подраздел состоит из следующих пунктов.

  • Указание источника привязки
  • Указание пути к значению
  • Связывание и выражение привязки

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

Рассмотрим следующий пример, в котором объектом источника привязки является класс с именем MyData, определенный в пространстве имен SDKSample. В качестве демонстрационного примера класс MyData имеет строковое свойство ColorName, установленное в значение "Красный". Таким образом, этот пример создает кнопку с красным фоном.

<DockPanel
  xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:c="clr-namespace:SDKSample">
  <DockPanel.Resources>
    <c:MyData x:Key="myDataSource"/>
  </DockPanel.Resources>
  <DockPanel.DataContext>
    <Binding Source="{StaticResource myDataSource}"/>
  </DockPanel.DataContext>
  <Button Background="{Binding Path=ColorName}"
          Width="150" Height="30">I am bound to be RED!</Button>
</DockPanel>

Дополнительные сведения о синтаксисе объявления привязки и примеры настройки привязки в коде см. в разделе Общие сведения об объявлении привязок.

Если применить этот пример к основной диаграмме, полученное изображение будет выглядеть следующим образом. Это привязка OneWay, так как свойство Background по умолчанию поддерживает привязку OneWay.

Схема привязки данных

Может вызвать удивление, почему это работает, несмотря на то, что свойство ColorName — строковое, в то время как свойство Background является свойством типа Brush. Это происходит в результате преобразования типов по умолчанию, которое обсуждается в разделе Преобразование данных.

Указание источника привязки

Обратите внимание, что в предыдущем примере источник привязки определялся установкой свойства DataContext на элементе DockPanel. Затем Button наследует значение DataContext от DockPanel, являющегося его родительским элементом. Повторим, что объект источника привязки является одним из четырех необходимых компонентов привязки. Таким образом, без указания объекта источника привязки эта привязка не имела бы смысла.

Есть несколько способов для указания объекта источника привязки. С помощью свойства DataContext родительского элемента удобно привязывать несколько свойств к одному источнику. Однако иногда удобнее указывать источник привязки в отдельных объявлениях привязки. В предыдущем примере вместо использования свойства DataContext можно указать источник привязки, установив свойство Source непосредственно в объявлении привязки кнопки, как показано в следующем примере:

<DockPanel.Resources>
  <c:MyData x:Key="myDataSource"/>
</DockPanel.Resources>
<Button Width="150" Height="30"
        Background="{Binding Source={StaticResource myDataSource},
                             Path=ColorName}">I am bound to be RED!</Button>

Кроме установки свойства DataContext непосредственно в элементе, наследования значения DataContext от предка (как, например, кнопки в первом примере) и явного указания источника привязки установкой свойства Source на Binding (например, кнопки в последнем примере), можно также использовать свойство ElementName или свойство RelativeSource для указания источника привязки. Свойство ElementName полезно при связывании с другими элементами приложения, например при использовании ползунка для настройки ширины кнопки. Свойство RelativeSource используется при установке связывания в ControlTemplate или Style. Дополнительные сведения см. в разделе Практическое руководство. Указание источника привязки.

Указание пути к значению

Если источник привязки является объектом, используйте свойство Path для указания значения, используемого для привязки. При связывании данных XML используйте свойство XPath для указания значения. В некоторых случаях удобно применить свойство Path, даже если это данные XML. Например, если требуется получить доступ к свойству "Имя" возвращаемого XmlNode (в результате выполнения запроса XPath), следует использовать свойство Path в дополнении к свойству XPath.

Сведения о синтаксисе и примеры см. в описании свойств Path и XPath.

Обратите внимание, что хотя мы и подчеркнули, что путь Path к используемому значению является одним из четырех необходимых компонентов связывания, в скриптах, которые требуется привязать к всему объекту, используемое значение должно быть таким же, как и у объекта источника привязки. В этих случаях оно применяется без указания Path. Рассмотрим следующий пример:

<ListBox ItemsSource="{Binding}"
         IsSynchronizedWithCurrentItem="true"/>

В приведенном выше примере используется синтаксис пустой привязки: {Привязка}. В этом случае ListBox наследует DataContext от родительского элемента DockPanel (не показан в этом примере). Если путь не указан, по умолчанию производится привязка к всему объекту. Другими словами, в этом примере путь не был указан, так как мы выполнили привязку свойства ItemsSource ко всему объекту. (Подробное обсуждение см. в разделе Привязка к коллекции.)

Кроме привязки к коллекции, этот скрипт полезен также для возможности привязки ко всему объекту, а не только к одному свойству объекта. Например, если объект источника является объектом строкового типа, и всего лишь нужна привязка к самой строке. Другим распространенным скриптом является необходимость привязки элемента к объекту с несколькими свойствами.

Обратите внимание, что вам, возможно, потребуется применить пользовательскую логику, чтобы данные были применимы к вашему привязанному свойству цели. Пользовательская логика может быть представлена в виде пользовательского преобразователя (если не существует преобразования типа по умолчанию). Сведения о преобразователях см. в разделе Преобразование данных.

Связывание и выражение привязки

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

Например, рассмотрим следующую ситуацию, когда myDataObject является экземпляром классаMyData, myBinding является источником объекта Binding, а классMyData представляет собой определенный класс, содержащий строковое свойство MyDataProperty. В этом примере привязывается текстовое содержимое MyText, экземпляр класса TextBlock, к MyDataProperty.

Dim data1 As New MyData(DateTime.Now)
Dim binding1 As New Binding("MyDataProperty")
binding1.Source = data1
Me.myText.SetBinding(TextBlock.TextProperty, binding1)
//make a new source
  MyData myDataObject = new MyData(DateTime.Now);      
  Binding myBinding = new Binding("MyDataProperty");
  myBinding.Source = myDataObject;
  myText.SetBinding(TextBlock.TextProperty, myBinding);

Можно использовать один и тот же объект myBinding для создания других привязок. Например, можно использовать объект myBinding для привязки текстового содержимого элемента флажка к MyDataProperty. В этом сценарии будут два экземпляра BindingExpression, совместно использующих объект myBinding.

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

Преобразование данных

В предыдущем примере кнопка красная, так как ее свойство Background привязано к строковому свойству со значением "Красный". Это работает, поскольку для типа Brush существует преобразователь, который преобразует строковое значение в значение типа Brush.

Если добавить эти сведения в рисунок из раздела Создание привязки, то диаграмма будет выглядеть следующим образом:

Схема привязки данных

Однако, что делать, если вместо свойства строкового типа объект источника привязки имеет свойство Цвет типа Color? В этом случае для создания привязки в первую очередь необходимо преобразовать значение свойства Цвет во что-то, что примет свойство Background. Необходимо создать пользовательский преобразователь, реализующий интерфейс IValueConverter, как в следующем примере:

    <ValueConversion(GetType(Color), GetType(SolidColorBrush))>
    Public Class ColorBrushConverter
        Implements IValueConverter
        Public Function Convert(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.Convert
            Dim color As Color = CType(value, Color)
            Return New SolidColorBrush(color)
        End Function

        Public Function ConvertBack(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.ConvertBack
            Return Nothing
        End Function
    End Class
[ValueConversion(typeof(Color), typeof(SolidColorBrush))]
public class ColorBrushConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        Color color = (Color)value;
        return new SolidColorBrush(color);
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return null;
    }
}

Страница ссылки IValueConverter предоставляет дополнительные сведения.

Теперь пользовательский преобразователь используется вместо преобразования по умолчанию, и наша диаграмма выглядит следующим образом:

Схема привязки данных

Таким образом, преобразования по умолчанию могут быть доступны благодаря преобразователям типов, присутствующим в типе, к которому производится привязка. Такое поведение будет зависеть от того, какие преобразователи типов доступны в цели. Если существуют какие-то сомнением, создайте свой собственный преобразователь.

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

  • Данные должны отображаться по-разному, в зависимости от региональных стандартов. Например, можно реализовать преобразователь валюты или преобразователь даты/времени в календаре на основе значений или стандартов, используемых в определенных региональных стандартах.

  • Используемые данные не обязательно предназначены для изменения текстового значения свойства, а предназначены для изменения некоторых других значений, например, источника изображения, цвета или стиля отображаемого текста. Преобразователи могут использоваться в данном экземпляре для преобразования привязки неподходящего свойства, например, привязки текстового поля к свойству Background ячейки таблицы.

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

  • Пока еще мы не рассматривали MultiBinding, где свойство цели имеет коллекцию привязок. В случае MultiBinding следует использовать пользовательский IMultiValueConverter для получения окончательного значения из значений привязок. Например, цвет может быть вычислен из соотношения красного, синего и зеленого значений, которые могут быть значениями одних и тех же или разных объектов источника привязки. Примеры и другие сведения содержатся на странице класса MultiBinding.

Привязка к коллекциям

Этот подраздел состоит из следующих пунктов.

  • Способы реализации коллекций
  • Представления коллекций

Объект источника привязки может рассматриваться как отдельный объект, свойства которого содержат данные, или как коллекцию данных полиморфных объектов, часто группируемых вместе (например, в результате запроса к базе данных). Пока еще мы обсуждали привязку только к одному объекту, однако привязка к коллекции данных является распространенным скриптом. Распространенным сценарием является использование элементов управления ItemsControl, таких как ListBox, ListView, или TreeView, для отображения коллекции данных, как, например, в приложении, показанном в разделе Понятие о привязке данных.

К счастью, наша основная диаграмма по-прежнему применима. Если привязать ItemsControl к коллекции, диаграмма будет выглядеть следующим образом:

Схема ItemsControl привязки данных

Как показано на этой диаграмме, выполнить привязку ItemsControl к объекту коллекции можно с помощью свойства ItemsSource. Свойство ItemsSource можно представить как содержимое ItemsControl. Обратите внимание, что привязка является привязкой типа OneWay, поскольку свойство ItemsSource по умолчанию поддерживает привязку OneWay.

Способы реализации коллекций

Пользователь может выполнить перечисление элементов любой коллекции, реализующей интерфейс IEnumerable. Однако чтобы настроить динамические привязки таким образом, чтобы вставки и удаления элементов в коллекции автоматически обновляли UI, в коллекции должен быть реализован интерфейс INotifyCollectionChanged. Этот интерфейс предоставляет событие, которое должно быть инициировано при изменении базовой коллекции.

WPF предоставляет класс ObservableCollection<T>, который является встроенной реализацией коллекции данных, предоставляющей интерфейс INotifyCollectionChanged. Обратите внимание, что для полной поддержки передачи значений данных от объектов источника в цели каждый объект в коллекции, который поддерживает свойства связывания, должен также реализовывать интерфейс INotifyPropertyChanged. Дополнительные сведения см. в разделе Общие сведения об источниках привязки.

До реализации собственной коллекции рассмотрите возможность использования класса ObservableCollection<T> или одного из существующих классов коллекций, таких как List<T>, Collection<T> и BindingList<T> (среди многих прочих). Если имеется расширенный скрипт и требуется реализовать свою собственную коллекцию, следует рассмотреть возможность использования IList, которая предоставляет коллекцию объектов, к которым можно по отдельности обращаться по индексу и следовательно, максимально быстро.

Представления коллекций

Так как ItemsControl связан с коллекцией данных, существует возможность сортировать, фильтровать или группировать данные. Для этого используется представления коллекций, которые являются классами, реализующими интерфейс ICollectionView.

Этот подраздел состоит из следующих пунктов.

  • Понятие о представлениях коллекций
  • Создание представления
  • Сортировка
  • Фильтрация
  • Группировка
  • Указатели на текущий элемент
  • Скрипт привязки "основной/подробности"

Понятие о представлениях коллекций

Представление коллекции — это слой, расположенный в верхней части связанной исходной коллекции, с помощью которого можно перемещаться по исходной коллекции и просматривать ее содержимое на основе запросов сортировки, фильтрации и группировки, не изменяя саму коллекцию. В представлении коллекции также поддерживается указатель на текущий элемент коллекции. Если в исходной коллекции реализован интерфейс INotifyCollectionChanged, изменения, инициированные событием CollectionChanged, передаются представлениям.

Поскольку представления не изменяют базовые исходные коллекции, каждая исходная коллекция может иметь несколько связанных с ней представлений. Например, можно иметь коллекцию объектов Задача. С помощью представлений можно отображать одни и те же данные различными способами. Например, в левой части страницы можно отображать задачи, отсортированные по приоритету, а справа — сгруппированные по областям.

Создание представления

Одним из способов создания и использования представления является непосредственное создание объекта представления, и затем использование его в качестве источника привязки. В качестве примера рассмотрим приложение, представленное в документе Data Binding Demo, которое показано в разделе Понятие о привязке к данным. Приложение реализовано таким образом, что ListBox привязывается к представлению коллекции данных, а не к коллекции данных напрямую. Следующий пример извлечен из приложения Пример привязки данных. Класс CollectionViewSource является прокси Extensible Application Markup Language (XAML) для класса, производного от CollectionView. В этом отдельном примере источник Source представления привязан к коллекции AuctionItems (типа ObservableCollection<T>) объекта текущего приложения.

<Window.Resources>


...


<CollectionViewSource 
      Source="{Binding Source={x:Static Application.Current}, Path=AuctionItems}"   
      x:Key="listingDataView" />


...


</Window.Resources>

Ресурс listingDataView затем служит источником привязки для элементов в приложении, таких как ListBox:

<ListBox Name="Master" Grid.Row="2" Grid.ColumnSpan="3" Margin="8"
    ItemsSource="{Binding Source={StaticResource listingDataView}}">


...


</ListBox>

Чтобы создать другое представление для той же коллекции, можно создать другой экземпляр CollectionViewSource и присвоить ему другое имя x:Key.

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

Тип исходной коллекции

Тип представления коллекции

Примечания

IEnumerable

Внутренний тип, основанный на CollectionView

Не удается сгруппировать элементы.

IList

ListCollectionView

Самый быстрый.

IBindingList

BindingListCollectionView

Использование представления по умолчанию

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

Чтобы получить представление по умолчанию, используйте метод GetDefaultView. Пример см. в разделе Практическое руководство. Получение представления по умолчанию для коллекции данных.

Использование представлений коллекций с таблицами данных ADO.NET

Для повышения производительности представления коллекций для объектов ADO.NET DataTable и DataView делегируют функции сортировки и фильтрации объекту DataView. При этом функции сортировки и фильтрации совместно используются всеми представлениями коллекции для источника данных. Чтобы включить возможность независимой сортировки и фильтрации для каждого представления коллекции, инициализируйте каждое представление коллекции с использованием собственного объекта DataView.

Сортировка

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

В следующем примере показана логика сортировки для флажка CheckBox "Сортировать по категории и дате" в UI приложения, описанного в разделе Понятие о привязке к данным.

private void AddSorting(object sender, RoutedEventArgs args)
{
    // This sorts the items first by Category and within each Category,
    // by StartDate. Notice that because Category is an enumeration,
    // the order of the items is the same as in the enumeration declaration
    listingDataView.SortDescriptions.Add(
        new SortDescription("Category", ListSortDirection.Ascending));
    listingDataView.SortDescriptions.Add(
        new SortDescription("StartDate", ListSortDirection.Ascending));
}

Фильтрация

Представления могут применять к коллекции фильтр. Это означает, что несмотря на то, что элемент может существовать в коллекции, его конкретное представление предназначено для отображения только некоторого подмножества полной коллекции. Возможна фильтрация по условию в данных. Например, как показано в приложении из раздела Понятие о привязке к данным, флажок CheckBox "Показывать только товары по сниженным ценам" содержит логику фильтрации товаров с ценой 25 долл. США и выше. Следующий код выполняется для установки фильтра ShowOnlyBargainsFilter в качестве обработчика событий Filter при выборе этого CheckBox:

listingDataView.Filter += new FilterEventHandler(ShowOnlyBargainsFilter);

Обработчик событий ShowOnlyBargainsFilter реализуется следующим образом:

private void ShowOnlyBargainsFilter(object sender, FilterEventArgs e)
{
    AuctionItem product = e.Item as AuctionItem;
    if (product != null)
    {
        // Filter out products with price 25 or above
        if (product.CurrentPrice < 25)
        {
            e.Accepted = true;
        }
        else
        {
            e.Accepted = false;
        }
    }
}

Если используется один из классов CollectionView непосредственно вместо CollectionViewSource, следует использовать свойство Filter для указания обратного вызова. Пример см. в разделе Практическое руководство. Фильтрация данных в представлении.

Группировка

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

В следующем примере показана логика флажка CheckBox "Группировка по категориям"

// This groups the items in the view by the property "Category"
PropertyGroupDescription groupDescription = new PropertyGroupDescription();
groupDescription.PropertyName = "Category";
listingDataView.GroupDescriptions.Add(groupDescription);

Другой пример группировки см. в разделе Практическое руководство. Группировка элементов в объекте ListView, реализующем GridView.

Указатели на текущий элемент

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

Поскольку WPF выполняет привязку к коллекции только через представление коллекции (либо указанное пользователем, либо представление коллекции по умолчанию), для всех привязок к коллекциям имеется указатель на текущий элемент. При привязке к представлению знак косой черты ("/") в значении Path указывает на текущий элемент представления. В приведенном ниже примере контекст данных является представлением коллекции. В первой строке выполняется привязка к коллекции. Во второй строке выполняется привязка к текущему элементу коллекции. В третьей строке выполняется привязка к свойству Description текущего элемента коллекции.

<Button Content="{Binding }" />
<Button Content="{Binding Path=/}" />
<Button Content="{Binding Path=/Description}" /> 

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

<Button Content="{Binding /Offices/}" />

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

Скрипт привязки "основной/подробности"

Понятие текущего элемента применимо не только для перемещения элементов в коллекции, но также для скрипта привязки "основной/подробности". Еще раз рассмотрим UI приложения из разделаПонятие о привязке к данным. В этом приложении выделение в ListBox определяет содержимое, показанное в ContentControl. При выборе элемента ListBox ContentControl отображает подробные сведения о выбранном элементе, что позволяет поместить выделение другим способом.

Для выполнения скрипта необходимо наличие двух или более элементов управления, привязанных к одному и тому же представлению. В следующем примере из документа Data Binding Demo показывается разметка элементов управления ListBox и ContentControl, которые можно видеть в UI приложения из раздела Понятие о привязке к данным.

<ListBox Name="Master" Grid.Row="2" Grid.ColumnSpan="3" Margin="8"
    ItemsSource="{Binding Source={StaticResource listingDataView}}">


...


</ListBox>


...


<ContentControl Name="Detail" Grid.Row="3" Grid.ColumnSpan="3" 
        Content="{Binding Source={StaticResource listingDataView}}" 
        ContentTemplate="{StaticResource detailsProductListingTemplate}" 
        Margin="9,0,0,0"/>

Обратите внимание, что оба элемента управления привязаны к одному источнику, статическому ресурсу listingDataView (просмотреть определение этого ресурса можно в разделе Создание представления). Это работает, так как если объект одноэлементного множества (в данном случае ContentControl) связан с представлением коллекции, он автоматически привязывается к CurrentItem представления. Обратите внимание, что объекты CollectionViewSource автоматически синхронизуют значения денежного формата и выделение. Если элемент управления списка не привязан к объекту CollectionViewSource, как в этом примере, для корректной работы необходимо задать его свойству IsSynchronizedWithCurrentItem значение true.

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

Можно заметить, что в приведенном выше примере используется шаблон. Фактически данные не будут отображаться выбранным способом без использования шаблонов (один явно используется элементом ContentControl, а другой — неявно элементом ListBox). К шаблонам данных мы перейдем в следующем разделе.

Шаблоны данных

Без использования шаблонов данных UI приложения из раздела Понятие о привязке к данным будет выглядеть следующим образом:

Демонстрация привязки данных без шаблонов данных

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

Чтобы устранить эту проблему, приложение задает шаблоны DataTemplate. Как показано в примере из предыдущего раздела, элемент управления ContentControl явно использует шаблон detailsProductListingTemplate DataTemplate. Элемент управления ListBox неявно использует следующий DataTemplate для отображении объектов AuctionItem в коллекции:

<DataTemplate DataType="{x:Type src:AuctionItem}">
    <Border BorderThickness="1" BorderBrush="Gray"
            Padding="7" Name="border" Margin="3" Width="500">
        <Grid>
          <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
          </Grid.RowDefinitions>
          <Grid.ColumnDefinitions>
            <ColumnDefinition Width="20"/>
            <ColumnDefinition Width="86"/>
            <ColumnDefinition Width="*"/>
          </Grid.ColumnDefinitions>

            <Polygon Grid.Row="0" Grid.Column="0" Grid.RowSpan="4"
                     Fill="Yellow" Stroke="Black" StrokeThickness="1"
                     StrokeLineJoin="Round" Width="20" Height="20"
                     Stretch="Fill"
                     Points="9,2 11,7 17,7 12,10 14,15 9,12 4,15 6,10 1,7 7,7"
                     Visibility="Hidden" Name="star"/>

            <TextBlock Grid.Row="0" Grid.Column="1" Margin="0,0,8,0"
                       Name="descriptionTitle"
                       Style="{StaticResource smallTitleStyle}">Description:</TextBlock>
            <TextBlock Name="DescriptionDTDataType" Grid.Row="0" Grid.Column="2" 
                Text="{Binding Path=Description}" 
                Style="{StaticResource textStyleTextBlock}"/>

            <TextBlock Grid.Row="1" Grid.Column="1" Margin="0,0,8,0"
                       Name="currentPriceTitle"
                       Style="{StaticResource smallTitleStyle}">Current Price:</TextBlock>
            <StackPanel Grid.Row="1" Grid.Column="2" Orientation="Horizontal">
                <TextBlock Text="$" Style="{StaticResource textStyleTextBlock}"/>
                <TextBlock Name="CurrentPriceDTDataType" 
                    Text="{Binding Path=CurrentPrice}" 
                    Style="{StaticResource textStyleTextBlock}"/>
            </StackPanel>
        </Grid>
    </Border>
    <DataTemplate.Triggers>
        <DataTrigger Binding="{Binding Path=SpecialFeatures}">
            <DataTrigger.Value>
                <src:SpecialFeatures>Color</src:SpecialFeatures>
            </DataTrigger.Value>
          <DataTrigger.Setters>
            <Setter Property="BorderBrush" Value="DodgerBlue" TargetName="border" />
            <Setter Property="Foreground" Value="Navy" TargetName="descriptionTitle" />
            <Setter Property="Foreground" Value="Navy" TargetName="currentPriceTitle" />
            <Setter Property="BorderThickness" Value="3" TargetName="border" />
            <Setter Property="Padding" Value="5" TargetName="border" />
          </DataTrigger.Setters>
        </DataTrigger>
        <DataTrigger Binding="{Binding Path=SpecialFeatures}">
            <DataTrigger.Value>
                <src:SpecialFeatures>Highlight</src:SpecialFeatures>
            </DataTrigger.Value>
            <Setter Property="BorderBrush" Value="Orange" TargetName="border" />
            <Setter Property="Foreground" Value="Navy" TargetName="descriptionTitle" />
            <Setter Property="Foreground" Value="Navy" TargetName="currentPriceTitle" />
            <Setter Property="Visibility" Value="Visible" TargetName="star" />
            <Setter Property="BorderThickness" Value="3" TargetName="border" />
            <Setter Property="Padding" Value="5" TargetName="border" />
        </DataTrigger>
    </DataTemplate.Triggers>
</DataTemplate>

При использовании двух шаблонов DataTemplate результирующий пользовательский интерфейс будет аналогичен пользовательскому интерфейсу, показанному в разделе Понятие о привязке к данным. Как можно увидеть из этого снимка, в дополнение к тому, что шаблоны DataTemplate дают возможность располагать данные в элементах управления, они позволяют определять подходящие визуальные элементы для данных. Например, элементы DataTrigger используются в указанном выше DataTemplate, так что элементыAuctionItem со значением SpecialFeatures для HighLight будут отображаться с оранжевой границей и звездочкой.

Дополнительные сведения о шаблонах данных см. в разделе Общие сведения о шаблонах данных.

Проверка данных

Этот подраздел состоит из следующих пунктов.

  • Связь проверочных правил и привязки
  • Предоставление визуального отклика
  • Процесс проверки

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

Связь проверочных правил и привязки

Модель привязки данных WPF позволяет связать свойство ValidationRules с объектом Binding. Например, в следующем примере выполняется привязка элемента управления TextBox к свойству с именем StartPrice и добавление объекта ExceptionValidationRule к свойству Binding.ValidationRules.

<TextBox Name="StartPriceEntryForm" Grid.Row="2" Grid.Column="1"
    Style="{StaticResource textStyleTextBox}" Margin="8,5,0,5">
  <TextBox.Text>
    <Binding Path="StartPrice" UpdateSourceTrigger="PropertyChanged">
      <Binding.ValidationRules>
        <ExceptionValidationRule />
      </Binding.ValidationRules>
    </Binding>
  </TextBox.Text>
</TextBox>

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

  • Объект ExceptionValidationRule проверяет исключения, возникшие во время обновления привязанного исходного свойства. В предыдущем примере StartPrice имеет тип integer. Когда пользователь вводит значение, которое невозможно преобразовать в целое число, создается исключение, приводящее к тому, что привязка будет помечена как недопустимая. Чтобы явно установить ExceptionValidationRule, можно также задать для свойства ValidatesOnExceptions значение true в объекте Binding или MultiBinding.

  • Объект DataErrorValidationRule выполняет проверку ошибок объектов, реализующих интерфейс IDataErrorInfo. Пример использования этого правила проверки см. в разделе DataErrorValidationRule. Чтобы явно установить DataErrorValidationRule, можно также задать для свойства ValidatesOnDataErrors значение true в объекте Binding или MultiBinding.

Можно также создать свои собственные правила проверки, определив класс, производный от класса ValidationRule, и реализовав метод Validate. В следующем примере показано правило, используемое элементом управления TextBox "Дата начала" из окна добавления продуктов, показанного в разделе Понятие о привязке к данным.

class FutureDateRule : 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;
        }
    }
}

StartDateEntryForm TextBox использует это правило FutureDateRule, как показано в следующем примере:

<TextBox Name="StartDateEntryForm" Grid.Row="3" Grid.Column="1" 
    Validation.ErrorTemplate="{StaticResource validationTemplate}" 
    Style="{StaticResource textStyleTextBox}" Margin="8,5,0,5">
    <TextBox.Text>
        <Binding Path="StartDate" UpdateSourceTrigger="PropertyChanged" 
            Converter="{StaticResource dateConverter}" >
            <Binding.ValidationRules>
                <src:FutureDateRule />
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>

Обратите внимание, что поскольку значение UpdateSourceTrigger является PropertyChanged, механизм привязки обновляет значение источника при каждом нажатии клавиши, то есть он также проверяет каждое правило в коллекции ValidationRules при каждом нажатии клавиши. Это будет обсуждаться далее в разделе "Процесс проверки".

Предоставление визуального отклика

Если пользователь вводит недопустимое значение, следует обеспечить некоторый отклик приложения UI на ошибку. Одним из способов обеспечения такого отклика является установка вложенного свойства Validation.ErrorTemplate в пользовательский шаблон ControlTemplate. Как показано в предыдущем подразделе, StartDateEntryForm TextBox использует ErrorTemplate, называемый шаблоном проверки. В следующем примере рассмотрено определение элемента Шаблон проверки:

<ControlTemplate x:Key="validationTemplate">
  <DockPanel>
    <TextBlock Foreground="Red" FontSize="20">!</TextBlock>
    <AdornedElementPlaceholder/>
  </DockPanel>
</ControlTemplate>

Элемент AdornedElementPlaceholder указывает, куда будет помещен выбранный элемент управления.

Кроме того, для вывода сообщения об ошибке можно использовать ToolTip. И StartDateEntryForm, и StartPriceEntryForm TextBox используют стиль textStyleTextBox, который создает ToolTip, отображающий сообщение об ошибке. В следующем примере рассмотрено определение элемента textStyleTextBox: Вложенное свойство Validation.HasError принимает значение true, если одна или несколько привязок к свойствам связанного элемента вызвали ошибку.

<Style x:Key="textStyleTextBox" TargetType="TextBox">
  <Setter Property="Foreground" Value="#333333" />
  <Setter Property="MaxLength" Value="40" />
  <Setter Property="Width" Value="392" />
  <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>

При использовании пользовательских шаблонов ErrorTemplate и ToolTip TextBox форма StartDateEntryForm при ошибке проверки выглядит следующим образом:

Ошибка проверки привязки данных

Если привязка Binding имеет правила проверки, но не указан ErrorTemplate на присоединенном элементе управления, для уведомления пользователей об ошибке проверки по умолчанию будет использоваться ErrorTemplate. ErrorTemplate по умолчанию — это шаблон элемента управления, определяющий красную границу графического уровня. С используемым по умолчанию ErrorTemplate и с ToolTip, элемент UI формы StartPriceEntryForm TextBox при ошибке проверки выглядит следующим образом:

Ошибка проверки привязки данных

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

Процесс проверки

Проверка обычно выполняется, когда целевое значение передается свойству источника привязки. Это выполняется для типов привязки TwoWay и OneWayToSource. Таким образом, причина обновления источника зависит от значения свойства UpdateSourceTrigger, как описано в разделе Что инициирует обновления источника.

Ниже описан процесс проверки. Обратите внимание, что при возникновении ошибки проверки или ошибки другого типа на любом этапе данного процесса процесс будет прерван.

  1. Обработчик привязки проверяет наличие настраиваемых объектов ValidationRule, свойству ValidationStep которых присвоено значение RawProposedValue для данного типа привязки Binding; в этом случае для каждого правила ValidationRule вызывается метод Validate, пока одно из них не вернет ошибку или пока все они не будут выполнены.

  2. Обработчик привязки вызывает преобразователь, если таковой существует.

  3. При успешном завершении работы преобразователя обработчик привязки проверяет наличие настраиваемых объектов ValidationRule, свойству ValidationStep которых присвоено значение ConvertedProposedValue для данного типа привязки Binding; в этом случае для каждого правила ValidationRule, свойству ValidationStep которого присвоено значение ConvertedProposedValue, вызывается метод Validate, пока одно из них не вернет ошибку или пока все они не будут выполнены.

  4. Обработчик привязки присваивает значение исходному свойству.

  5. Обработчик привязки проверяет наличие настраиваемых объектов ValidationRule, свойству ValidationStep которых присвоено значение UpdatedValue для данного типа привязки Binding; в этом случае для каждого правила ValidationRule, свойству ValidationStep которого присвоено значение UpdatedValue, вызывается метод Validate, пока одно из них не вернет ошибку или пока все они не будут выполнены. Если правило DataErrorValidationRule связано с привязкой и его свойству ValidationStep задано значение по умолчанию, UpdatedValue, в этой точке производится проверка правила DataErrorValidationRule. В этой же точке проверяются привязки, в которых для свойства ValidatesOnDataErrors установлено значение true.

  6. Обработчик привязки проверяет наличие настраиваемых объектов ValidationRule, свойству ValidationStep которых присвоено значение CommittedValue для данного типа привязки Binding; в этом случае для каждого правила ValidationRule, свойству ValidationStep которого присвоено значение CommittedValue, вызывается метод Validate, пока одно из них не вернет ошибку или пока все они не будут выполнены.

Если правило ValidationRule не выполняется на любом этапе данного процесса, обработчик привязки создает объект ValidationError и добавляет его в коллекцию Validation.Errors связанного элемента. Перед запуском обработчиком привязки объектов ValidationRule на любом этапе обработчик привязки удаляет все объекты ValidationError, добавленные во Validation.Errors вложенное свойство связанного элемента на соответствующем этапе. Например, если правило ValidationRule, свойству ValidationStep которого присвоено значение UpdatedValue, не выполняется, то при следующей проверке обработчик привязки удалит объект ValidationError непосредственно перед вызовом любого правила ValidationRule, свойству ValidationStep которого присвоено значение UpdatedValue.

Если свойство Validation.Errors не пусто, Validation.HasError вложенному свойству элемента присваивается значение true. Кроме того, если свойству NotifyOnValidationError объекта Binding присвоено значение true, обработчик привязки вызывает Validation.Error вложенное событие элемента.

Также обратите внимание, что при передаче допустимого значения в любом направлении (от целевого объекта к источнику и от источника к целевому объекту) очищается Validation.Errors вложенное свойство.

Если для привязки задано связанное с ней правило ExceptionValidationRule или для ее свойства ValidatesOnExceptions задано значение true и при задании обработчиком привязки значения источника возникает исключение, обработчик привязки проверяет наличие фильтра UpdateSourceExceptionFilter. Имеется возможность использования отклика UpdateSourceExceptionFilter для возможности создания пользовательского обработчика исключений. Если фильтр исключений UpdateSourceExceptionFilter для привязки Binding не указан, обработчик привязки создает объект ValidationError с исключением и добавляет его в коллекцию Validation.Errors связанного элемента.

Механизм отладки

Можно установить вложенное свойство PresentationTraceSources.TraceLevel связанного с привязкой объекта для получения сведений о состоянии конкретной привязки.

См. также

Задачи

Практическое руководство. Привязка к результатам запроса LINQ

Практическое руководство. Привязка к источнику данных ADO.NET

Ссылки

DataErrorValidationRule

Основные понятия

Новые возможности WPF версии 4

Оптимизация производительности: привязка данных

Другие ресурсы

Data Binding Demo

Практические руководства по привязке данных