Проектирование информационной системы для Windows Store. Обработка данных
Привязка к свойству элемента управления
Начнем с того, что для реализации привязки используется объект типа Binding. Независимо от того, связываются элементы или элемент и данные, всегда используется именно Binding. При этом Binding можно совершенно спокойно использовать как в коде, так и в разметке XAML.
Естественно, что использование Binding в XAML - самая распространенная ситуация. Для этих целей в XAML существует специальное расширение разметки. Рассмотрим небольшой пример:
<Page x:Class="Chapter5_Binding.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"> <Grid Background="{StaticResource ApplicationPageBackgroundBrush}" VerticalAlignment="Center"> <StackPanel x:Name="LayoutRoot"> <Image Source="Assets/hydrangeas.jpg" Width="400"> <Image.Projection> <PlaneProjection RotationY= "{Binding Value, ElementName=slider}"> </PlaneProjection> </Image.Projection> </Image> <Slider Minimum="0" Maximum="360" Name="slider" Width="400" Margin="10"></Slider> </StackPanel> </Grid> </Page>
Здесь создали элемент управления Image, который хотим "вращать" по оси Y. Для создания эффекта размещения элемента в трехмерном пространстве используется объект PlaneProjection, содержащий свойство RotationY, которое задает угол поворота элемента по оси Y. Чтобы сделать интерфейс более динамичным, вторым элементом добавили ползунок, который и будет задавать угол поворота. Используем два параметра:
- Path - позволяет задать свойство источника, с которым происходит связывание. Поскольку это свойство является свойством по умолчанию, то явно Path можно не писать;
- ElementName - задает имя элемента-источника.
В данном примере независимо от того, как модифицируется свойство Value бегунка, свойство RotationY будет обновляться автоматически. Привязку можно реализовать и по-другому:
<Page x:Class="Chapter7_Binding.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"> <Grid Background="{StaticResource ApplicationPageBackgroundBrush}" VerticalAlignment="Center"> <StackPanel x:Name="LayoutRoot" > <Image Source="Hydrangeas.jpg" Width="400"> <Image.Projection> <PlaneProjection x:Name="projection"></PlaneProjection> </Image.Projection> </Image> <Slider Minimum="0" Maximum="360" Name="slider" Width="400" Margin="10" Value= "{Binding RotationY, ElementName=projection, Mode=TwoWay}"> </Slider> </StackPanel> </Grid> </Page>
В этом примере в качестве источника выступает изображение. Отличие состоит в том, что, выполняя привязку ползунка к изображению, мы указали дополнительное свойство Mode. Это свойство может принимать одно из трех значений:
- OneTime - значение свойства устанавливается на основании значения свойства источника, но установка происходит лишь в момент создания объектов. Любые изменения в будущем игнорируются;
- OneWay - значение свойства устанавливается на основании значения свойства источника. При изменении свойства источника будет обновляться свойство основного объекта;
- TwoWay - значение свойства устанавливается на основании значения свойства источника. При изменении свойства источника или свойства основного объекта, будут происходить взаимные обновления.
В примере установили значение TwoWay свойству Mode. Таким образом, несмотря на то, что картинка является источником, ее поворот успешно задается ползунком.
Выбор источника зависит от конкретного приложения. Так, в примере выше выбор источника был не принципиален, но если мы решим добавить дополнительный элемент, модифицирующий значение угла поворота, то установить свойству два элемента Binding нам не удастся, а вот изменить направление привязки можно. Пример ниже расширяет наш интерфейс текстовым полем, которое также задает угол поворота.
<Page x:Class="Chapter5_Binding.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"> <Grid Background="{StaticResource ApplicationPageBackgroundBrush}" VerticalAlignment="Center"> <StackPanel x:Name="LayoutRoot"> <Image Source="Assets/hydrangeas.jpg" Width="400"> <Image.Projection> <PlaneProjection x:Name="projection"></PlaneProjection> </Image.Projection> </Image> <Slider Minimum="0" Maximum="360" Name="slider" Width="400" Margin="10" Value= "{Binding RotationY, ElementName=projection, Mode=TwoWay}"> </Slider> <TextBox Width="200" Text= "{Binding RotationY, ElementName=projection, Mode=TwoWay}"></TextBox> </StackPanel> </Grid> </Page>
Обратите внимание, что в этом примере действие значения, введенного в TextBox, вступит в силу лишь при потере фокуса элементом TextBox. Это связано с тем, что поведение свойств при реализации привязки может отличаться. Чтобы задать поведение свойства, используют метаданные (атрибуты). В свою очередь, метаданные для TextBox заданы таким образом, что им нужна потеря фокуса текстового поля, чтобы произвести обновление. Для элемента TextBox это вполне обосновано.
Наконец, если необходимо установить привязку к данным в коде, то это также можно сделать без проблем. Вот как будет выглядеть код для TextBox из нашего примера:
Binding binding = new Binding(); binding.ElementName = "projection"; binding.Path = new PropertyPath("RotationY"); binding.Mode = BindingMode.TwoWay; txtBox.SetBinding(TextBox.TextProperty, binding);
Перейдем теперь от привязки к элементам к привязке к объектам, которые не являются элементами управления.
Привязка к объекту
На практике значительно чаще приходится выполнять привязку элементов к данным, которые содержатся в объекте некоторого класса. Эти данные могут быть получены из базы данных или сгенерированы во время работы приложения. Создадим простой класс, описывающий информацию о сотруднике:
public class Employee { public string FirstName { get; set; } public string LastName { get; set; } public string EMail { get; set; } public int Age { get; set; } }
Этот класс содержит 4 свойства с модификатором public. Это основное требование при привязке к данным: свойства объектов, которые выступают в качестве источника, должны быть общедоступными.
Следующий код создает простую форму и связывает эту форму с объектом типа Employee, который мы определили в ресурсах (аналогично можно определить и в коде):
<Page x:Class=" Chapter5_Binding.MainPage " xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="using:Element_Binding_Sample"> <Page.Resources> <local:Employee x:Key="employee" Age="27" FirstName="Sergii" LastName="Lutai" EMail="sergii.lutai@dct.ua" /> </Page.Resources> <Grid x:Name="LayoutRoot" Width="400"> <Grid.RowDefi nitions> <RowDefi nition></RowDefi nition> <RowDefi nition></RowDefi nition> <RowDefi nition></RowDefi nition> <RowDefi nition></RowDefi nition> </Grid.RowDefi nitions> <Grid.ColumnDefi nitions> <ColumnDefi nition></ColumnDefi nition> <ColumnDefi nition></ColumnDefi nition> </Grid.ColumnDefi nitions> <TextBlock Text="First Name:" Grid.Row="0" Grid.Column="0"> </TextBlock> <TextBox Text="{Binding FirstName, Source={StaticResource employee}, Mode=TwoWay}" Grid.Row="0" Grid.Column="1"> </TextBox> <TextBlock Text="Last Name:" Grid.Row="1" Grid.Column="0"> </TextBlock> <TextBox Text="{Binding LastName, Source={StaticResource employee}, Mode=TwoWay}" Grid.Row="1" Grid.Column="1"> </TextBox> <TextBlock Text="EMail:" Grid.Row="2" Grid.Column="0"> </TextBlock> <TextBox Text="{Binding EMail, Source={StaticResource employee}, Mode=TwoWay}" Grid.Row="2" Grid.Column="1"> </TextBox> <TextBlock Text="Age:" Grid.Row="3" Grid.Column="0"> </TextBlock> <TextBox Text="{Binding Age, Source={StaticResource employee}, Mode=TwoWay}" Grid.Row="3" Grid.Column="1"> </TextBox> </Grid> </Page>
Как видите, механизм точно такой, как и при привязке к элементам. Только вместо ElementName используется свойство Source, которое задает ссылку на объект (в данном случае выбираемый из ресурсов).
Модифицируем код выше, используя еще одно полезное свойство - DataContext:
<Page x:Class="Chapter5_Binding.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="using:Element_Binding_Sample"> <Page.Resources> <local:Employee x:Key="employee" Age="27" FirstName="Sergii" LastName="Lutai" EMail="sergii.lutai@dct.ua" /> </Page.Resources> <Grid x:Name="LayoutRoot" Width="400" DataContext="{StaticResource employee}"> <Grid.RowDefi nitions> <RowDefi nition></RowDefi nition> <RowDefi nition></RowDefi nition> <RowDefi nition></RowDefi nition> <RowDefi nition></RowDefi nition> </Grid.RowDefi nitions> <Grid.ColumnDefi nitions> <ColumnDefi nition></ColumnDefi nition> <ColumnDefi nition></ColumnDefi nition> </Grid.ColumnDefi nitions> <TextBlock Text="First Name:" Grid.Row="0" Grid.Column="0"> </TextBlock> <TextBox Text="{Binding FirstName, Mode=TwoWay}" Grid.Row="0" Grid.Column="1"> </TextBox> <TextBlock Text="Last Name:" Grid.Row="1" Grid.Column="0"> </TextBlock> <TextBox Text="{Binding LastName, Mode=TwoWay}" Grid.Row="1" Grid.Column="1"> </TextBox> <TextBlock Text="EMail:" Grid.Row="2" Grid.Column="0"> </TextBlock> <TextBox Text="{Binding EMail, Mode=TwoWay}" Grid.Row="2" Grid.Column="1"> </TextBox> <TextBlock Text="Age:" Grid.Row="3" Grid.Column="0"> </TextBlock> <TextBox Text="{Binding Age, Mode=TwoWay}" Grid.Row="3" Grid.Column="1"> </TextBox> </Grid> </Page>
Как видите, нам немного удалось упростить код. Дело в том, что если не указать имя объекта с данными явно, то механизм привязки начинает перебор свойств DataContext всех родительских элементов, пока не найдет непустой. Найденный DataContext механизм связывания и используется в качестве источника.