Привязка данных, не являющихся элементами WPF
Создадим проект для интерфейсного элемента ввода данных ( рис. 3.1).
Рис. 3.1.Окно ввода вещественных чисел
XAML-описание окна Window1 приведен ниже:
<Window x:Class="Wpf_Primer1.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Ввод вещественного числа" Height="140" Width="300"> <Grid Name="gridPrimir" Height="85"> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition /> </Grid.ColumnDefinitions> <Label Margin="3,3,3,15" Name="label1">Параметр</Label> <TextBox Grid.Column="1" Margin="5,5,28,15" Name="textBox1" /> <Button Grid.Column="1" Grid.Row="1" Margin="5,5,28,12" Name="button1" Click="button1_Click">Проверить</Button> </Grid></Window>Код класса Window1:
using System;using System.Windows;using System.Windows.Controls;namespace Wpf_Primer1{ public partial class Window1 : Window { public static float Factor { get; set; } public Window1() { InitializeComponent(); } private void button1_Click(object sender, RoutedEventArgs e) { MessageBox.Show(Factor.ToString(), "Проверка значения параметра"); } }}Свойство Factor класса Window1 необходимо для приема вводимого вещественного числа и его целесообразно сделать статическим для доступа из других классов приложения. Элемент TextBox используется для ввода вещественных чисел с плавающей точкой. Вводимое число должно передаваться свойству Factor. Кнопка button1 введена в форму для инициации проверки – вывода сообщения со значением свойства Factor. У нас встает задача привязки свойства Factor к свойству зависимостей Text элемента TextBox.
В общем случае для привязки к объекту, не являющемуся элементом, можно использовать следующие свойства:
· Source, которое указывает на объект-источник, поставляющий данные;
· RelativeSource, которое указывает на объект-источник, используя объект RelativeSource, позволяющий базировать ссылку на текущем элементе;
· DataContent, которому необходимо присвоить значение объекта-источника для текущего или более высокого элемента в дереве визуальных элементов приложения.
В нашем случае целесообразно использовать свойство RelativeSource. Для реализации привязки введем изменение в XAML-код элемента TextBox, добавив описание свойства Text:
<TextBox Grid.Column="1" Margin="5,5,28,15" Name="textBox1" Text="{Binding Path = Factor, RelativeSource = {RelativeSource FindAncestor, AncestorType = {x:Type Window}}}"/>В выражении привязки данных применяется расширение разметки XAML, которое заключается в фигурные скобки. Выражение начинается со слова Binding означающее, что создается экземпляр класса System.Windows.Data.Binding. Свойству Path присваивается значение объекта-источника, в нашем случае это свойство Factor. Объект RelativeSource задается с помощью расширенной разметки. Режим FindAncestor является значением перечисления RelativeSourceMode и определяет выражение привязки к родительскому элементу в визуальном дереве интерфейсного элемента. Кроме значения FindAncestor перечисление RelativeSourceMode может задавать следующие режимы:
· Self - для привязки к другому свойству того же элемента;
· PreviousData – для привязки к предыдущему элементу данных в привязываемых к данным списке;
· TemplateParent – для привязывания к элементу, к которому применен шаблон.
Свойство AncestorType определяет элемент в визуальном дереве, где содержится объект-источник. В нашем случае свойство Factor является членом класса Window1 элемента Window.
Протестируем созданное приложение. При вводе строки 0.1, что соответствует формату вещественного числа, получаем результат проверки вещественное число 0,1 ( рис. 3.2).
Рис. 3.2.Проверка ввода строки "0.1"
При выводе вещественное число преобразуется к строковому представлению методом ToString():
MessageBox.Show(Factor.ToString(), "Проверка значения параметра");В результате этого десятичная точка заменяется на запятую и в окне "Проверка значения параметра" выводится строка "0,1", а не введенная "0.1":
При вводе строки "0,1", что не соответствует формату вещественного числа с плавающей точкой, получаем в результате преобразования по умолчания целое число "1" ( рис. 3.3).
Рис. 3.3.Проверка ввода строки "0,1"
При вводе недопустимого символа, получаем в результате проверки целое число 1, которое было присвоено свойству Factor в предыдущем сеансе тестирования программы ( рис. 3.4).
Рис. 3.4.Проверка ввода недопустимого символа
Результаты тестирования разработанной программы показывают, что при вводе корректного представления вещественного числа программа работает правильно, а при вводе недопустимых символов или нарушении формата вещественного числа (ввод запятой вместо точки) программа выдает неправильный результат. Это определяет необходимость проверки достоверности ввода для предотвращения некорректных значений.
При корректном вводе данных мы убедились, что взаимодействие между свойством Text элемента TextBox и свойством Factor класса Window1 правильно поддерживается созданной привязкой, то есть информация от целевого объекта передается к объекту-источнику.
В приложениях часто возникают ситуации, когда данные изменяются в коде программы и это изменение желательно увидеть в интерфейсном элементе. Проверим корректность связи от объекта-источника к целевому объекту. Для этого добавим в форму ещё одну кнопку, при нажатии на которую значение свойства Factor будет увеличиваться в 2 раза. При тестировании изменения значения свойства Factor при нажатии кнопки button2 значение свойства Factor (объекта-источника) изменяется, а свойство Text элемента TextBox (целевого объекта) остается прежним ( рис. 3.5).
Рис. 3.5.Проверка корректности программного изменения объекта-источника
Проверка корректности программного изменения объекта-источника
Это является результатом того, что свойство Text элемента TextBox не уведомляется об изменении свойства Factor, которое не является свойством зависимостей. Для обеспечения уведомления между объектом-источником и целевым объектом можно поступить следующим образом:
· сделать свойство Factor свойством зависимостей;
· инициировать событие изменения свойства Factor ;
· реализовать в классе, содержащем свойство Factor интерфейс INotifyPropertyChanged.
Продемонстрируем первый и третий способ решения задачи обеспечения синхронизации представления данных между объектом-источником и целевым объектом.
Определим в классе Window1 свойство зависимостей Factor. Измененный код принимает следующий вид:
public partial class Window1 : Window{ public static readonly DependencyProperty FactorProperty; public float Factor { set{SetValue(FactorProperty, value);} get{return (float)GetValue(FactorProperty);} } static Window1() { FactorProperty = DependencyProperty.Register("Factor", typeof(float), typeof(Window1), new FrameworkPropertyMetadata (0.0F, FrameworkPropertyMetadataOptions.AffectsRender)); } public Window1() { InitializeComponent(); } private void button1_Click(object sender, RoutedEventArgs e) { MessageBox.Show(Factor.ToString(), "Проверка значения параметра"); } private void button2_Click(object sender, RoutedEventArgs e) { Factor *= 2; }}Вначале определяется свойство зависимостей:
public static readonly DependencyProperty FactorProperty;Затем формируют методы доступа к свойству зависимостей:
public float Factor{ set{SetValue(FactorProperty, value);} get{return (float)GetValue(FactorProperty);}}На последнем шаге свойство зависимостей регистрируется в статическом конструкторе:
static Window1(){ FactorProperty = DependencyProperty.Register("Factor", typeof(float), typeof(Window1), new FrameworkPropertyMetadata (0.0F, FrameworkPropertyMetadataOptions.AffectsRender));}Теперь при нажатии кнопки button2 изменяется значение свойства Factor и целевого объекта свойства Text элемента TextBox.
Для реализации интерфейса INotifyPropertyChanged создадим класс FactorClass:
public class FactorClass: INotifyPropertyChanged{ public event PropertyChangedEventHandler PropertyChanged; public void OnPropertyChanged(PropertyChangedEventArgs e) { if (PropertyChanged != null) PropertyChanged(this, e); } private float factor; public float Factor { get { return factor; } set { factor = value; OnPropertyChanged((new PropertyChangedEventArgs("Factor")); } }}В классе объявляется событие PropertyChanged:
event PropertyChangedEventHandler PropertyChanged;Затем кодируется обработчик события OnPropertyChanged:
public void OnPropertyChanged(PropertyChangedEventArgs e) { if (PropertyChanged != null) PropertyChanged(this, e);}Для свойства Factor при его изменении производится генерация события:
public float Factor { get { return factor; } set { factor = value; OnPropertyChanged OnPropertyChanged(new PropertyChangedEventArgs("Factor")); } }С учетом создания класса FactorClass код окна Window1 требует корректировки:
public partial class Window1 : Window{ private static FactorClass factor = new FactorClass(); public static FactorClass Factor { get { return Window1.factor; } } public Window1() { InitializeComponent(); } private void button1_Click(object sender, RoutedEventArgs e) { MessageBox.Show(Factor.Factor.ToString(), "Проверка значения параметра"); } private void button2_Click(object sender, RoutedEventArgs e) { Factor.Factor *= 2; }}В коде окна Window1 добавляется поле и свойства Factor типа FactorClass:
private static FactorClass factor = new FactorClass();public static FactorClass Factor{ get { return Window1.factor; }}В обработчиках кнопок обращение к свойству Factor производят через экземпляр класса FactorClass.
Тестирование внутреннего изменения значение свойства Factor и целевого объекта свойства Text элемента TextBox показывают синхронизацию изменений свойств.
При использовании класса FactorClass выражение привязки можно упростить. В этом случае создается привязка без указания источника, то есть необходимо указать только свойство Factor класса FactorClass:
<TextBox Grid.Column="1" Margin="5,5,28,15" Name="textBox1" Text="{Binding Path = Factor}"/>Элемент Grid спроектированного окна является контейнером для всех визуальных элементов пользовательского интерфейса. Если свойству DataContext элемента Grid задать значение экземпляра класса FactorClass, то выражение привязки данных будет использовать общедоступные свойства класса FactorClass для заполнения себя данными. Установка свойства DataContext элемента Grid может быть сделана в конструкторе окна:
public Window1(){ InitializeComponent(); gridPrimir.DataContext = Factor;}Проведем тестирование созданного приложения. Вначале введем значение 0.3 и проверим результат ( рис. 3.6).
Рис. 3.6.Проверка корректности привязки данных
Далее нажмем кнопку "Увеличить в 2 раза" и посмотрим реакцию приложения ( рис. 3.7). Приложение работает корректно.
Рис. 3.7.Проверка корректности привязки данных при программном изменении объекта-источника
В рассматриваемом примере поддерживается двусторонняя связь между объектом-источником и целевым объектом. В общем случае направление привязки может быть однонаправленным и двунаправленным. Это определяется свойством Binding.Mode, которое может принимать следующие значения:
· OneWay определяет, что целевое свойство обновляется при изменении свойства-источника;
· TwoWay определяет, что и источник и целевое свойство обновляются при изменении какого-либо свойства;
· OneTime определяет, что целевое свойство устанавливается изначально на основе значения свойства-источника;
· OneWaySource определяет, что исходное свойство-источника обновляется при изменении целевого свойства, которое никогда не обновляется;
· Default определяет, что тип привязки зависит от целевого свойства. Так для свойства TextBox.Text по умолчанию будет TwoWay.
Для явного задания направления привязки необходимо сформировать свойство Mode целевого объекта:
<TextBox Grid.Column="1" Margin="5,5,28,15" Name="textBox1" Text="{Binding Path = Factor, Mode = TwoWay}" />При применении привязки нужно принимать во внимание режим обновления данных между источником и целевым свойством. Режим обновления задается свойством Binding.UpdateSourceTrigger, которое может принимать следующие значения:
· PropertyChanged задает обновление источника немедленно после обновления целевого свойства;
· LostFocus задает обновление источника, когда целевой элемент теряет фокус;
· Expicit задает обновление источника только при вызове метода BindingExpression.UpdateSource() ;
· Default определяет, что обновления определяются метаданными целевого свойства.
Если нам необходимо немедленное обновление целевого свойства и источника, то необходимо добавить изменения с XAML разметку элемента TextBox:
<TextBox Grid.Column="1" Margin="5,5,28,15" Name="textBox1" Text="{Binding Path = Factor, Mode = TwoWay, UpdateSourceTrigger = PropertyChanged}" />Дата добавления: 2015-04-15; просмотров: 1499;