1

Тема: Data Binding (связывание данных)

Связывание Данных

Оригинал: Data Binding

Связывание данных предлагает простой путь для отображения и взаимодействия с данными в приложениях Silverlight. Способ отображения информации отделяется от управления информацией. Связывание или связь объекта UI и объекта данных позволяет производить обмен информацией между ними. Когда связь между этими элементами установлена и происходит изменение данных, связанный с этими данными элемент UI способен отражать все изменения автоматически. Точно также, изменения содержимого UI элемента пользователем могут быть автоматически отражены в объект данных. Например, если пользователь редактирует содержимое элемента TextBox, данные привязанного к нему объекта автоматически изменяются, приходя в соответствие с данными UI элемента.

Некоторые наиболее распространенные сценарии включают связывание элемента ListBox со списком каких-либо наименований, формы ввода вроде TextBox с объектом данных клиента, или Image с текущий фотографией пользователя.

Данная тема содержит следующие разделы:

•    Связывание элементов пользовательского интерфейса (UI) с объектами данных
•    Направление потока данных
•    Оповещение об изменении данных
•    Обновление источника данных
•    Связывание с коллекциями
•    Проверка данных
•    Преобразование данных

Thumbs up +1 Thumbs down

2

Re: Data Binding (связывание данных)

Связывание элементов UI с данными
Connecting User Interface Elements with Data

Каждое связывание должно точно определять источник (source) и приемник (target) данных. Следующая иллюстрация демонстрирует основные понятия о связывании данных:

Механизм связывания данных

Механизм связывания получает информацию из объекта Binding о следующих элементах:

•    Свойство-приемник UI элемента, который отображает и, возможно, позволяет пользователю редактировать данные. Приемником может быть любое свойство типа DependencyProperty объекта типа FrameworkElelemnt. Приемником также может быть DependencyProperty некоторого DependencyObject в следующих случаях:
  o    Объект DependencyObject представлен как значение свойства элемента FrameworkElement.
  o    Объект DependencyObject находится в коллекции, которая представляет собой значение свойства элемента FrameworkElement (например, свойство Resources).
  o    Объект DependencyObject находится в DependencyObjectCollection(T).

•    Объект-источник содержащий данные, которые участвуют в обмене между источником и приемником. Источником может быть любой CRL-объект, включая сам приемник или другие элемента UI. Если приемник находится в шаблоне данных (Data Template), источником может служить элемент UI, к которому применяется данных шаблон.

•    Направление потока данных. Направление задается через свойство Mode объекта Binding.

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

Например, свойство Foreground элемента TextBox может быть привязано к объекту SolidColorBrush, так что цвет текста будет изменяться, если изменится значение объекта SolidColorBrush. В данном сценарии свойство Foreground – это приемник, а объект SolidColorBrush – источник связывания.

В следующем примере показано, как связать цвет свойства Foreground элемента TextBox с объектом SolidColorBrush через код и разметку XAML. Источником связывания здесь выступает свойство класса MyColors, описание которого вы встретите несколько позднее.

<TextBox x:Name="MyTextBox" Text="Text" Foreground="{Binding Brush1, Mode=OneWay}"/>
// Создаем экземпляр класса MyColors, 
// который реализует INotifyPropertyChanged.
MyColors textcolor = new MyColors();

// Свойству Brush1 присваиваем SolidColorBrush с значение Red.
textcolor.Brush1 = new SolidColorBrush(Colors.Red);

// Устанавливаем свойство DataContext элемента MyTextBox.
MyTextBox.DataContext = textcolor;

Связывание создается в XAML с использованием синтаксиса {Binding …}. Источник задается в коде через свойство DataContext элемента TextBox.

DataContext наследуется. Если вы зададите DataContext родительскому элементу, тогда все дочерние элементы получат тот же самый контекст. Дочерний элемент может заместить данное поведение через установку свойства Source связываемого объекта или через его DataContext, который будет применен ко всем его дочерним элементам.

Использование DataContext удобно, когда вы хотите получить сразу несколько связываний использующих один и тот же источник. Чтобы задать источник только для одного связывания, задайте свойство Source объекта Binding. Подробности смотрите в теме How to: Create a Binding.

Также вы можете использовать свойство ElementName или свойство RelativeSource чтобы установить связывание источника. Свойство ElementName полезно, когда вы привязываетесь к другим элементам вашего приложения, например, когда вы используете ползунок, чтобы настроить ширину кнопки. Свойство RelativeSource полезно когда связывание устанавливается в ControlTemplate или в Style. Подробности смотрите в темах Binding Markup Extension и RelativeSource Markup Extension.

Thumbs up Thumbs down

3

Re: Data Binding (связывание данных)

Направление потока данных
Direction of the Data Flow

Каждое связывание содержит свойство Mode, которое определяет, как и когда происходит передача данных. Silverlight разрешает три типа связывания:

•    Связывание OneTime обновляет значение приемника данными источника в тот момент, когда создается связывание.

•    Связывание OneWay обновляет значение приемника данными источника в момент, когда создается взязывание, а также всегда, когда данные источника изменяются.

•    Связывание TwoWay обновляет как источник, так и приемник, когда данные одного из них были изменены извне. Также можно отключить автоматическое обновление источника и обновлять его только в то время, когда вы того захотите.

Чтобы автоматическое обновление приемника было возможным, объект источника должен реализовывать интерфейс INotifyPropertyChanged, как будет описано в следующем разделе.

Thumbs up Thumbs down

4

Re: Data Binding (связывание данных)

Оповещение об изменении данных
Change Notification

Чтобы изменения источника могли отразиться на приемнике источник должен реализовать интерфейс INotifyPropertyChanged. Итерфейс INotifyPropertyChanged предоставляет событие PropertyChanged, которое сообщает механизму связывания, что данные источника изменились, благодаря чем механизм связывания узнает, что пора обновить значение приемника.

В следующем примере класс MyColors реализует интерфейс INotifyPropertyChanged для связывания в OneWay режиме.

// Создаем класс реализующий INotifyPropertyChanged.
public class MyColors : INotifyPropertyChanged
{
    private SolidColorBrush _Brush1;

    // Объявляем событие PropertyChanged.
    public event PropertyChangedEventHandler PropertyChanged;

    // Создаем свойство, которое будет служить источником связывания.
    public SolidColorBrush Brush1
    {
        get { return _Brush1; }
        set
        {
            _Brush1 = value;
            // Вызываем NotifyPropertyChanged когда свойство источника 
            // изменяется.
            NotifyPropertyChanged("Brush1");
        }
    }


    // Метод NotifyPropertyChanged возбуждает событие PropertyChanged, 
    // передавая имя свойства-источника, которое изменилось.
    public void NotifyPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, 
                new PropertyChangedEventArgs(propertyName));
        }
    }
}

Чтобы получать уведомление об изменениях при связывании коллекций со свойством ItemsControl, реализуйте интерфейс INotifayCollectionChanged в дополенение к INotifyPropertyChanged. Если вы реализуете INotifyCollectionChanged, тогда изменения коллекции, такие как добавление или удаление элемента, будут переданы приемнику. Чтобы получать уведомления об изменении самих объектов коллекции, объекты должны реализовывать интерфейс INotifyPropertyChanged.

Прежде чем реализовывать свою коллекцию, рассмотрите вариант использования класса ObservableCollection(T), в который уже встроена реализация INotifyCollectionChanged и INotifyPropertyChanged.

Thumbs up Thumbs down

5

Re: Data Binding (связывание данных)

Обновление источника данных
Updating the Data Source

В режиме связывания TwoWay изменения приемника автоматически отражаются на источнике, за исключением случая, когда связывание применено к свойству Text элемента TextBox. В этом случае обновление произойдет, когда TextBox потеряет фокус.

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

Чтобы отключить автоматическое обновление источника установите свойство UpdateSourceTrigger в Explicit. Этот параметр повлияет на все связывания, использующие один объект Binding (например, при наследовании DataContext). Тогда вы должны будете обновлять каждое связывание отдельно. Чтобы выполнить обновление связывания, прежде всего, сделайте вызов метода  FrameworkElement.GetBindingExpression элемента-приемника, передав в качестве параметра DependencyProperty. После чего вы можете использовать значение, которое вернул метод, для вызова метода BindingExpression.UpdateSource. Следующий пример кода демонстрирует этот процесс.

<TextBox x:Name="textBox1" Text="{Binding Test, Mode=TwoWay, UpdateSourceTrigger=Explicit}" />
<Button Content="Update" Click="Button_Click" />
public class TestData
{
    public String Test { get; set; }
}

TestData data;

public MainPage()
{
    InitializeComponent();
    data = new TestData { Test = "one" };
    textBox1.DataContext = data;
}

private void Button_Click(object sender, RoutedEventArgs e)
{
    BindingExpression expression =      textBox1.GetBindingExpression(TextBox.TextProperty);
    MessageBox.Show("Before UpdateSource, Test = " + data.Test);
    expression.UpdateSource();
    MessageBox.Show("After UpdateSource, Test = " + data.Test);
}

Thumbs up Thumbs down

6

Re: Data Binding (связывание данных)

Связывание с коллекциями
Binding to Collections

Источник связывания может рассматриваться либо как одиночный объект с некоторыми данными доступными через свойства, либо как коллекция объектов. Вы можете пожелать отобразить список некоторых элементов, например, счет кредитной карты за каждый месяц. Для этого воспользуйтесь контролом ItemsControl и задайте DataTemplate для всех элементов коллекции. Подробности о DataTemplates смотрите в разделе How to: Customize Data Display with Data Templates.

<Grid.Resources>
    <DataTemplate x:Name="dataTemplate">
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <TextBlock Grid.Column="0" Text="{Binding Month, Converter={StaticResource Converter1}}"/>
            <TextBlock Grid.Column="1" Text="{Binding Total}"/>
        </Grid>
    </DataTemplate>
</Grid.Resources>

<ItemsControl x:Name="IC1" ItemsSource="{Binding}" ItemTemplate="{StaticResource dataTemplate}"/>

</controls:ChildWindow>

Вы можете использовать любую коллекцию, которая реализует интерфейс IEnumerable. Если вы хотите, чтобы приемник при изменении коллекции обновлял ItemsSource, реализуйте в коллекции интерфейс INotifyCollectionChanged. Дополнительная информация по теме оповещения об изменениях содержится в разделе "Оповещение об изменениях данных".

Дополнительные сведения о создании связывания с коллекциями данных смотрите тему Walkthrough: Binding to a Collection and Creating a Master/Details View.

Информацию об инкапсуляции шаблонного списка в объект, так чтобы можно было его связать с контролом, смотрите первый пример кода List<T> и Dictionary<TKey, TValue>.

Thumbs up Thumbs down

7

Re: Data Binding (связывание данных)

Валидация данных
Data Validation

Silverlight поддерживает элементарную валидацию данных в режиме связывания TwoWay в направлении потока данных от-приемника-к-источнику. Также Silverlight поддерживает проверку данных с IDataErrorInfo и INotifyDataErrorInfo в любом режиме связывания.

Silverlight уведомляет об ошибке проверки данных всегда, когда присоединяемое свойство (attached property) Validation.Errors связывания содержит ошибки. Ошибки попадают в данную коллекцию в следующих случаях:

•    Выброшено исключение из конвертера типов механизма связывания.
•    Выброшено исключение из аксессора set объекта связыания.
•    Выброшено исключение из атрибута валидации, который был применен к объекту данных или члену.
•    Объект связывания реализует интерфейс IDataErrorInfo и его свойство Item возвращает значение отличное от null или Empty.
•    Объект связывания реализует интерфейс INotifyDataErrorInfo и его метод GetErrors возвращает значение отличное от null. Возвращаемое методом GetErrors значение может изменяться, т.к. является результатом завершения асинхронных операций валидации.


Silverlight выбрасывает визуальное оповещение об ошибках валидации в следущих случаях:

•    Свойство ValidatesOnException выставлено в true.
•    Свойство ValidatesOnDataErrors выставлено в true. В любом случае свойство ValidatedOnDataErrors игнорируется для свойства источника, если значение свойства ValidatesOnExceptions есть true и сеттер свойства источника выбрасывает исключение.
•    Свойство ValidatesOnNotifyDataErrors выставлено в true. Это свойство может быть задействовано в комбинации с ValidatesOnExceptions либо с ValidatesOnDataErrors.

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

Оповещение об ошибке валидации

Вы можете настроить визуальное оповещение контрола путем модификации или замены стандартного ControlTemplate. Подробности смотрите в разделе Control Customization.

Чтобы получать оповещения об ошибках валидации, вы также должны задать свойству объекту связывания NotifyOnValidationError значение true. Задавая ValidatesOnExceptions значение true вы сообщаете механизму связывания, что нужно создать ошибку валидации, когда случается исключение. Задав в NotifyOnValidationError значение true вы сообщаете механизму связывания, что нужно возбуждать событие BindingValidationError, когда случается ошибка валидации. Например, вы можете обрабатывать данное событие ошибки для записи в лог или отображать дополнительное визуальное оповещение.

Чтобы обрабатывать событие BindingValidationError создайте обработчик события в объекте-приемнике или в одном из его родительских объектов. BindingValidationError – это маршрутизируемое событие, поэтому если вы не перехватите его в элементе, который его породил, оно продолжит движение вверх по иерархии объектов, пока не будет перехвачено. Дополнительную информацию о маршрутизируемых событиях смотрите в разделе Events Overview for Silverligh.

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

Связывание создается в XAML.

<StackPanel BindingValidationError="StackPanel_BindingValidationError" >
    <StackPanel.Resources>
        <my:Bills x:Name="MyBills"/>
    </StackPanel.Resources>
    <TextBox x:Name="MyTextBox" Width="50" Margin="10">
        <TextBox.Text>
            <Binding Mode="TwoWay" Source="{StaticResource MyBills}" 
                     Path="Amount" NotifyOnValidationError="true" 
                     ValidatesOnExceptions="true"/>
        </TextBox.Text>
    </TextBox>
    <Button Height="50" Width="150" Content="Click To Update Source"/>
</StackPanel>

Объект-источник выбрасывает исключение, если присваиваемое значение меньше нуля.

public class Bills
{
    private double _Amount;
    public double Amount
    {
        get { return _Amount; }
        set
        {
            if (value < 0)
                throw new Exception("Amount must be greater than zero.");
            _Amount = value;
        }
    }

}

Контрол StackPanel реализует обработчик события BindingValidationError.

private void StackPanel_BindingValidationError(object sender, 
    ValidationErrorEventArgs e)
{
    if (e.Action == ValidationErrorEventAction.Added)
    {
        MyTextBox.Background = new SolidColorBrush(Colors.Red);

    }
    else if (e.Action == ValidationErrorEventAction.Removed)
    {
        MyTextBox.Background = new SolidColorBrush(Colors.White);
    }
}

Просмотреть данный пример

Запустите пример и введите символы вместо числа, чтобы спровоцировать ошибку конвертирования типов. Затем введите отрицательное число, чтобы спровоцировать ошибку в set-акссессоре в объекте-источнике. Теперь введите положительное число, чтобы ушла ошибка валидации. Обновление данных в направлении от-приемника-к-источнику произойдет только когда элемент TextBox потеряет фокус. Нажатие кнопки как раз снимет фокус с TextBox. Если хотите, можете обновлять источник вручную по событию кнопки Click, как уже описывалось ранее в разделе «Обновление данных источника».

Более подробную информацию об использовании атрибутов валидации, определяющих допустимые значения свойств или объектов вы найдете в разделе Using Data Annotations to Customize Data Classes. Дополнительную информацию о предоставлении более детальных отчетов валидации смотрите в обзоре класса ValidationSummary.

Thumbs up Thumbs down

8

Re: Data Binding (связывание данных)

Конвертирование данных
Data Conversions

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

•    Хранение цвета формате RGBA, но отображение его в строковом формате.
•    Хранение числа как значения с плавающей точкой, но отображение в формате некоторой валюты.
•    Хранение даты как DataTime, но отображение в виде календаря.
•    Хранение значений null, но отображение его в более дружественном пользователю виде значения по умолчанию.

Вы можете задавать формат отображения значения любой строки, установив свойство StringFormat. Информацию о кодах форматирования смотрите в разделе Formatting Types.

Вы можете отображать значение null в более понятном и дружественном виде, установив свойство TargetNullValue.

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

// Пользовательский класс реализует интерфейс IValueConverter.
public class DateToStringConverter : IValueConverter
{

    #region IValueConverter Members

    // Определите метод Convert, чтобы преобразовывать объект DataTime 
    // в строку месяца.
    public object Convert(object value, Type targetType, 
        object parameter, 
System.Globalization.CultureInfo culture)
    {
        // значение представляет собой данные из объекта-источника
        DateTime thisdate = (DateTime)value;
        int monthnum = thisdate.Month;
        string month;
        switch (monthnum)
        {
            case 1:
                month = "Январь";
                break;
            case 2:
                month = "Февраль";
                break;
            default:
                month = "Нет такого месяца";
                break;
        }
        // Возвращаем значение для передачи в объект-приемник.
        return month;

    }

    // Метод ConvertBack не реализован для связывания в режиме OneWay.
    public object ConvertBack(object value, Type targetType, 
        object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    #endregion
}

Механизм связывания может вызывать методы Convert и ConvertBack если параметр Converter определен в связывании. Когда данные передаются от источника, механизм связывания вызывает метод Convert и затем передает возвращаемое значение в приемник. Когда данные поступают от приемника, механизм связывания вызывает метод ConvertBack и передает возвращаемые данные к источнику. Следующий пример демонстрирует, как установить параметр Converter.

<UserControl.Resources>
  <local:DateToStringConverter x:Key="Converter1"/>
</UserControl.Resources>


...


<TextBlock Grid.Column="0" 
 
  Text="{Binding Month, Converter={StaticResource Converter1}}"/>

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

Если во время конвертирования случилась ошибка, не следует выбрасывать исключение. Вместо этого верните значение DependencyProperty.UnsetValue, которое остановит передачу данных.

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

Thumbs up Thumbs down