Приложения Windows Phone обычно объявляют свой пользовательский интерфейс с помощью XAML (в Expression Blend). Одни параметры компонентов пользовательского интерфейса будут жестко заданы в XAML, другие — предоставлены источниками данных (объекты, базы данных, XML-каналы). Привязка данных — это метод, который устанавливает взаимосвязь между компонентами XAML и их значениями, определенными в коде.
Режимов привязки несколько, они определены в перечисляемом типе BindingMode:
- TwoWay — изменения, внесенные в пользовательский интерфейс, автоматически дублируются в модели, и наоборот.
- OneWay — изменения, внесенные в модели, дублируются в пользовательском интерфейсе. Этот вариант подходит для элементов управления, работающих в режиме «только для чтения» и заполняемых данными.
- OneTime — пользовательский интерфейс обновляется после запуска приложения или когда изменяется контекст данных. Выберите этот вариант, если данные в приложении не изменяются.
- OneWayToSource — изменения, внесенные в пользовательском интерфейсе, дублируются в модели.
- Default — для привязки к целевому объекту (элементу управления в пользовательском интерфейсе) используется режим по умолчанию. Для различных элементов управления результаты также отличаются.
В этом примере кода показано, как привязать элементы управления TextBox в XAML к объекту-источнику в файле C#. В нем используются режимы OneTime, OneWay и TwoWay. Для режимов, в которых источник (модель) обновляет интерфейс (целевой объект), также показано, как использовать класс интерфейса INotifyPropertyChanged, чтобы пользовательский интерфейс обновлялся каждый раз при изменении базового кода.
В соответствующем примере создаются три текстовых поля в XAML, а при помощи привязки обновляются их значения с использованием информации из объектов C#.
Рисунок 1. Привязка данных в Windows Phone.
Создание пользовательского интерфейса XAML
Сначала мы создаем простую страницу Windows Phone с панелью заголовка, за которой следует панель содержимого (Grid) с тремя элементами TextBox для вывода информации о производителе, программном обеспечении и модели. Обратите внимание: элемент управления Grid для контента называется ContentPanel, далее мы будем указывать это имя, когда будем обращаться к XAML из кода C# (имена (свойство Name) остальных элементов TextBox нам также понадобятся).
<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--TitlePanel contains the name of the application and page title-->
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock x:Name="ApplicationTitle" Text="MY APPLICATION"
Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock x:Name="PageTitle" Text="DataBindingExample"
Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<TextBox Height="70" HorizontalAlignment="Left" Margin="0,130,0,0"
Name="manufacturerBox" Text="manufacturer" VerticalAlignment="Top" Width="450" />
<TextBox Height="72" HorizontalAlignment="Left" Margin="0,206,0,0"
Name="modelBox" Text="model" VerticalAlignment="Top" Width="450" />
<TextBox Height="72" HorizontalAlignment="Left" Margin="6,284,0,0"
Name="softwareBox" Text="software" VerticalAlignment="Top" Width="450" />
</Grid>
</Grid>
В представленном выше коде XAML жестко задаются значения свойства Text для производителя, модели и программного обеспечения. Чтобы привязать элементы управления к данным, сначала следует добавить в требуемые свойства ключевое слово {Binding}. Ключевое слово позволяет указать имя свойства, для которого мы хотим создать привязку, а также режим привязки, который определяет, будут ли изменения, внесенные в пользовательский интерфейс, распространяться на базовый объект, и наоборот. Оба свойства не являются обязательными: если вы просто укажете ключевое слово {Binding}, то будет происходить вызов метода ToString() привязываемого объекта для получения значения свойства, а для целевого объекта будет установлен режим привязки «по умолчанию».
В этом примере мы указываем конкретные значения для свойств привязываемого объекта и передаем их в Text (то есть текст из свойства manufacturer станет значением свойства manufacturerBox). Кроме того, мы выбираем режим OneTime, поэтому пользовательский интерфейс будет обновляться согласно данной модели только при загрузке страницы.
Совет. Рекомендуется всегда явным образом выбирать конкретный режим, поскольку чаще всего это обеспечивает прирост производительности.
Если мы не указали режим в коде, приложение будет по-прежнему «работать», поскольку по умолчанию для элемента TextBox выбирается режим TwoWay. Но без INotifyPropertyChanged контент элемента TextBox будет обновлен из кода только один раз. Введенный в поле TextBox текст по-прежнему будет храниться в привязанном свойстве.
Обновленный код XAML выглядит следующим образом:
<Grid x:Name = "ContentPanel" Grid.Row = "1" Margin = "12,0,12,0" >
<TextBox Height = "70" HorizontalAlignment = "Left" Margin = "0,130,0,0"
Name = "manufacturerBox" Text = "{Binding manufacturer,Mode=OneTime}"
VerticalAlignment = "Top" Width = "450" />
<TextBox Height = "72" HorizontalAlignment = "Left" Margin = "0,206,0,0"
Name = "modelBox" Text = "{Binding model,Mode=OneTime}"
VerticalAlignment = "Top" Width = "450" />
<TextBox Height = "72" HorizontalAlignment = "Left" Margin = "6,284,0,0"
Name = "softwareBox" Text = "{Binding Программное обеспечение, Mode = Onetime} "
VerticalAlignment = "Топ" Ширина = "450" />
</ Grid >
Мы до сих пор не указали источник данных (несмотря на то что из XAML узнали, что он имеет свойства manufacturer, model и software) и не привязали его к XAML. Мы сделаем это в коде C# (как именно, мы расскажем далее).
Создание и привязка источника данных
Далее мы создадим PhoneModel.cs и определим свой класс PhoneModel. Обратите внимание на то, что у класса есть свойства C#, которые соответствуют привязанным свойствам в XAML.
namespace DataBindingEx
{
public class PhoneModel
{
public string manufacturer { get; set; }
public string model { get; set; }
public string software { get; set; }
}
}
Далее необходимо создать экземпляр класса PhoneModel и привязать его к XAML, для чего мы воспользуемся свойством DataContext. Рекомендуется создавать привязки после инициализации страницы в обработчике событий MainPage_Loaded(), как показано ниже.
namespace DataBindingEx
{
public partial class MainPage : PhoneApplicationPage
{
// declare PhoneModel object
PhoneModel _phnModel;
// Constructor
public MainPage()
{
InitializeComponent();
// add page load event handler
Loaded += MainPage_Loaded;
}
void MainPage_Loaded(object sender,RoutedEventArgs e)
{
// initialize _phnModel object
_phnModel = new PhoneModel
{
manufacturer = "Nokia",
model = "Lumia 920",
software = "windows phone 8"
};
// call utility method to set DataContext
setDataContext();
}
private void setDataContext()
{
ContentPanel.DataContext = _phnModel;
}
}
}
Обратите внимание на метод setDataContext():
private void setDataContext()
{
ContentPanel.DataContext = _phnModel;
}
ContentPanel — это ссылка на объект Grid, определенный в XAML (Grid x:Name="ContentPanel"). Соответствующую привязку мы создадим путем сопоставления экземпляра PhoneModel со свойством DataContext объекта, на который ссылаемся. Теперь при загрузке страницы свойства будут взяты из нашего экземпляра PhoneModel. В результате приложение будет выглядеть, как показано на рисунке 1.
На данном этапе привязка осуществляется в режиме OneTime (свойства в пользовательском интерфейсе обновляются только при загрузке страницы, изменения в пользовательском интерфейсе не влияют на свойства объекта C#). В следующих разделах объясняется, как сделать так, чтобы пользовательский интерфейс реагировал на изменения в модели, и наоборот (привязка в режиме TwoWay).
Обновление пользовательского интерфейса после изменения свойств в коде
Чтобы организовать обновление пользовательского интерфейса после изменения свойств источника (модели), необходимо сначала выбрать в XAML режим привязки OneWay или TwoWay. Если выбран режим OneWay, все определения TextBox будут выглядеть следующим образом:
<TextBox Height="70" HorizontalAlignment="Left" Margin="0,130,0,0"
Name="manufacturerBox" Text="{Binding manufacturer,Mode=OneWay}"
VerticalAlignment="Top" Width="450" />
Кроме того, чтобы пользовательский интерфейс обновлялся при изменении свойства в коде, необходимо в классе PhoneModel реализовать интерфейс INotifyPropertyChanged. Обновленный код PhoneModel.cs показан ниже:
using System.ComponentModel;
using System.Windows.Data;
namespace DataBindingEx
{
public class PhoneModel:INotifyPropertyChanged
{
private string _manufacturer;
private string _model;
private string _software;
public string manufacturer
{
get { return _manufacturer; }
set
{
_manufacturer = value;
NotifyPropertyChanged("manufacturer");
}
}
public string model
{
get { return _model; }
set
{
_model = value;
NotifyPropertyChanged("model");
}
}
public string software
{
get { return _software;}
set
{
_software = value;
NotifyPropertyChanged("software");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
Приведенный фрагмент кода создает служебный метод с именем NotifyPropertyChanged, который генерирует событие PropertyChanged с именем свойства, подлежащего обновлению в пользовательском интерфейсе. Все свойства должны вызывать служебный метод NotifyPropertyChanged.
Чтобы обновить свойства модели и увидеть, обновляется ли пользовательский интерфейс, мы добавим кнопку и событие типа OnClick, по которому будем менять наши свойства. Код MainPage.xaml.cs будет выглядеть так:
namespace DataBindingEx
{
public partial class MainPage : PhoneApplicationPage
{
PhoneModel _phnModel;
// Constructor
public MainPage()
{
InitializeComponent();
Loaded += MainPage_Loaded;
}
void MainPage_Loaded(object sender,RoutedEventArgs e)
{
_phnModel = new PhoneModel
{
manufacturer = "Nokia",
model = "Lumia 920",
software = "windows phone 8"
};
setDataContext();
}
private void setDataContext()
{
ContentPanel.DataContext = _phnModel;
}
// utility method which changes the PhoneModel properties
private void setPhoneProperties(String manufacturer, String model, String software)
{
_phnModel.manufacturer = manufacturer;
_phnModel.model = model;
_phnModel.software = software;
}
// called when update button is clicked
private void updateBtn_Click(object sender, RoutedEventArgs e)
{
setPhoneProperties("Nokia", "Lumia 900", "windows Phone 7.8");
}
}
}
После нажатия на кнопку экран должен выглядеть как на рисунке 2:
Рисунок 2. Дублирование свойств модели в пользовательском интерфейсе.
Двухсторонняя привязка — обновление модели при внесении изменений в пользовательский интерфейс
Привязка в режиме OneWay (обсуждалась в предыдущем разделе) подходит для элементов управления, используемых только для чтения. Для редактируемых элементов необходимо выбрать вариант TwoWay, чтобы при внесении изменений в пользовательском интерфейсе можно было обновлять модель.
Для создания двухсторонней привязки этого достаточно, нам не придется вносить дополнительные изменения в код C#. Необходимо просто установить для параметра Mode в XAML значение TwoWay, как показано ниже:
{Binding software,Mode=TwoWay}
На самом деле это можно не делать, поскольку для элемента TextBox по умолчанию выбирается именно режим TwoWay. Однако, четко указав режим привязки, вы снижаете вероятность ошибок.
Таким образом, итоговый код XAML выглядит так:
<TextBox Height="70" HorizontalAlignment="Left" Margin="12,80,0,0"
Name="manufacturerBox" Text="{Binding manufacturer,Mode=TwoWay}"
VerticalAlignment="Top" Width="438" />
<TextBox Height="72" HorizontalAlignment="Left" Margin="12,156,0,0"
Name="modelBox" Text="{Binding model, Mode=TwoWay}"
VerticalAlignment="Top" Width="438" />
<TextBox Height="72" HorizontalAlignment="Left" Margin="12,234,0,0"
Name="softwareBox" Text="{Binding software,Mode=TwoWay}"
VerticalAlignment="Top" Width="438" />
Чтобы реализовать привязку в режиме TwoWay в нашем приложении, добавим кнопку Button и три элемента TextBlockв Grid. Затем мы изменим содержимое TextBox и убедимся в том, что обновленные данные передаются обратно в DataContext. Для этого нажмем кнопку Read Model, чтобы прочитать данные из объекта модели и обновить свойство Text объекта TextBlock.
Обновленный код XAML для этого примера:
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<TextBox Height="70" HorizontalAlignment="Left" Margin="12,80,0,0"
Name="manufacturerBox" Text="{Binding manufacturer,Mode=TwoWay}"
VerticalAlignment="Top" Width="438" />
<TextBox Height="72" HorizontalAlignment="Left" Margin="12,156,0,0"
Name="modelBox" Text="{Binding model, Mode=TwoWay}"
VerticalAlignment="Top" Width="438" />
<TextBox Height="72" HorizontalAlignment="Left" Margin="12,234,0,0"
Name="softwareBox" Text="{Binding software,Mode=TwoWay}"
VerticalAlignment="Top" Width="438" />
<Button Content="Update" Height="72" HorizontalAlignment="Left" Margin="9,18,0,0"
Name="changeButton" VerticalAlignment="Top"
Width="441" Click="updateBtn_Click" />
<Button Content="Read Model" Height="72" HorizontalAlignment="Left"
Margin="12,322,0,0" Name="readBtn" VerticalAlignment="Top"
Width="438" Click="readBtn_Click" />
<TextBlock Height="30" HorizontalAlignment="Left"
Margin="32,400,0,0" Name="manufacBlock" Text="TextBlock"
VerticalAlignment="Top" Width="401" />
<TextBlock Height="30" HorizontalAlignment="Left"
Margin="32,452,0,0" Name="modelBlock" Text="TextBlock"
VerticalAlignment="Top" Width="401" />
<TextBlock Height="30" HorizontalAlignment="Left"
Margin="32,506,0,0" Name="softwareBlock"
Text="TextBlock" VerticalAlignment="Top" Width="401" />
</Grid>
Событие OnClick для кнопки Read Button:
private void readBtn_Click(object sender, RoutedEventArgs e)
{
// reading data from model and changing Text of the TextBlocks
manufacBlock.Text = _phnModel.manufacturer;
modelBlock.Text = _phnModel.model;
softwareBlock.Text = _phnModel.software;
}
Если мы все сделали правильно, экран будет выглядеть следующим образом:
Рисунок 3. Привязка данных в Windows Phone в режиме TwoWay.
Заключение
Привязка свойств — один из самых лучших и простых способов интеграции ваших данных с атрибутами пользовательского интерфейса.