Изменение и отображение данных
У созданной нами программы Сумматор имеется недостаток, связанный с тем, что для получения результата нужно каждый раз нажимать на кнопку. Было бы лучше, если бы результат обновлялся автоматически при изменении текста в одном из текстовых полей. Это можно осуществить с помощью событий.
Ранее мы использовали событие элемента Button, которое происходит при нажатии на кнопку. С точки зрения программы событие является вызовом метода. Код XAML, описывающий кнопку, может выглядеть так:
Margin="158,275,0,0" Name="equalsButton" VerticalAlignment="Top"
Width="160" Click="equalsButton_Click" />
При нажатии на кнопку вызывается указанный обработчик события. В нашем случае в нем вызывается метод calculateResult.
private void equalsButton_Click(object sender, RoutedEventArgs e)
{
calculateResult();
}
Можно узнать, какие события может вызывать элемент, щелкнув по нему в редакторе Visual Studio и затем нажав на кнопку События в области свойств. Там же можно назначить, и даже автоматически создать обработчики событий, которые требуется обработать в программе.
Для элемента firstNumberTextBox можно создать обработчик события TextChanged, дважды щелкнув в строке TextChanged в пустом поле справа, и вызвать в нем метод calculateResult:
private void firstNumberTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
calculateResult();
}
То же самое можно сделать для второго текстового поля. Теперь, при изменении текста в любом из этих полей результат будет обновляться. Следовательно, теперь можно удалить кнопку сумма.
Однако, возникает проблема с проверкой допустимости введенных значений. Если пользователь введет текст вместо числа, то программа это обнаружит и выведет на экран сообщение. Однако, это сообщение будет выводиться на экран по два раза при вводе недопустимого символа. То есть, событие происходит дважды — и это известная проблема для приложений Windows Phone. Обойти эту проблему можно, проверяя, были ли изменены данные при последовательных вызовах событий с теми же данными:
string oldFirstNumber = "";
private void firstNumberTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
if (firstNumberTextBox.Text == oldFirstNumber)
return;
oldFirstNumber = firstNumberTextBox.Text;
calculateResult();
}
Этот обработчик вызывает метод calculateResult, только если текст в элементе TextBox действительно изменился.
Привязка данных позволяет связать визуальные элементы с методами, которые работают с этими элементами. Это позволяет связать свойство визуального элемента напрямую с объектом. Все что нужно для этого сделать — это создать особый объект, значения которого будут автоматически передаваться элементам Silverlight.
Привязка данных может работать в любом направлении. Можно связать текст элемента TextBox с приложением таким образом, чтобы при изменении текста в этом текстовом поле приложение было об этом уведомлено. Также можно связать элемент TextBlock с объектом так, чтобы при изменении свойства объекта содержимое элемента TextBlock соответственно обновлялось. Это пример двунаправленной привязки, когда программа может сама изменить элемент и отреагировать на изменения в этом элементе. Можно задать однонаправленную привязку, чтобы программа только выводила на экран результаты при изменении свойства объекта. Наконец, можно связать любые свойства объектов так, чтобы программа могла переместить элемент Silverlight по экрану при изменении свойств X и Y объекта игры.
Создание объекта для привязки . Попробуем создать версию программы Сумматор, использующую привязку данных. Начнем с класса, который сделает всю работу. Нужно определить свойства класса, которые будут связаны со свойствами визуальных элементов. Наш объект будет содержать три свойства:
текст верхнего элемента TextBox;
текст нижнего элемента TextBox;
текст элемента TextBlock.
Можно создать класс AdderClass, имеющий следующую структуру:
public class AdderClass
{
private int topValue;
public int TopValue
{
get
{
return topValue;
}
set
{
topValue = value;
}
}
private int bottomValue;
public int BottomValue
{
get
{
return bottomValue;
}
set
{
bottomValue = value;
}
}
public int AnswerValue
{
get
{
return topValue + bottomValue;
}
}
}
У этого класса три свойства. Первые два предназначены для чтения и записи, чтобы можно было получить текст верхнего и нижнего текстового поля и занести в них новые значения. Третье свойство предназначено только для получения последнего результата без возможности его изменения напрямую. Программа может создать экземпляр этого класса, установить значения свойств TopValue и BottomValue и затем получить результат.
Добавление функций уведомления. Для того чтобы класс можно было связать с объектом Silverlight, в нем должен быть реализован интерфейс INotifyPropertyChanged:
public interface INotifyPropertyChanged
{
// Событие происходит при изменении значений свойства
event PropertyChangedEventHandler PropertyChanged;
}
Класс, который реализует этот интерфейс, должен содержать делегат события, которое будет использовать класс AdderClass для сообщения об изменении значения свойства.
public event PropertyChangedEventHandler PropertyChanged;
Класс PropertyChangedEventHandler используется элементами Silverlight для управления сообщениями событий. Он описан в пространстве имен System.ComponentModel. Если компонент Silverlight нужно связать с каким-либо свойством класса, можно добавить к этому событию делегаты.
Класс AdderClass будет использовать этот делегат для того, чтобы сообщить об изменении одного из свойств в классе. При этом произойдет обновление экрана. Связанные с этими свойствами объекты Silverlight связываются с делегатом, чтобы они могли получить уведомление в случае необходимости. Окончательная версия класса может выглядеть так:
public class AdderClass : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private int topValue;
public int TopValue
{
get
{
return topValue;
}
set
{
topValue = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs
("AnswerValue"));
}
}
}
// то же самое для свойства BottomValue
public int AnswerValue
{
get
{
return topValue + bottomValue;
}
}
}
Класс AdderClass теперь содержит свойство PropertyChanged. Каждый раз при изменении значения свойства он должен проверить значение свойства PropertyChanged. Если оно не равно null, то оно связано с делегатом. Можно рассматривать свойство PropertyChanged как список рассылки. Процесс может подписаться на рассылку и получать уведомления об интересующих событиях, которые происходят в AdderClass. Если никто не подписан на список рассылки (то есть значение PropertyChanged равно null), то нет точки вызова метода для описания изменений. Однако, если оно не равно null, это означает, что есть объект, который интересуют изменения, происходящие в AdderClass.
Вызов метода, который генерирует событие, выглядит следующим образом:
PropertyChanged(this, new PropertyChangedEventArgs("AnswerValue"));
Первый параметр является ссылкой на объект, который генерирует событие. Вторым параметром является значение аргумента, которое содержит имя свойства, значение которого изменилось. Элемент Silverlight использует технологию отражения (т.е. он будет считывать публичные свойства класса AdderClass), чтобы узнать, какие свойства доступны. Если он получит уведомление, что значение свойства AnswerValue изменилось, то он будет обновлять все свойства визуальных элементов, связанные со свойством этого класса.
Когда программа сгенерирует событие, на экран будет выведено обновленное значение свойства AnswerValue. При вызове кода секции get свойства AnswerValue он вычисляет и возвращает результат.
public int AnswerValue
{
get
{
return topValue + bottomValue;
}
}
Следующим шагом будет соединение этого объекта с пользовательским интерфейсом. Пользовательский интерфейс сообщит классу AdderClass, когда значения двух текстовых полей будут изменены пользователем. Когда будут вводиться новые значения полей, экземпляр класса AdderClass должен сообщить пользовательскому интерфейсу, что результат изменился, и его нужно вывести на экран.
Добавление пространства имен на страницу MainPage . Для того чтобы связать созданный объект с данными, нужно связать код объекта с XAML-кодом, описывающим пользовательский интерфейс. Необходимо добавить в код XAML пространство имен, содержащее класс AdderClass:
xmlns:local="clr-namespace:AddingMachine"
Этот атрибут корневого элемента в XAML-файле для страницы MainPage.xaml делает доступными любые классы пространства имен AddingMachine для использования в XAML-коде. Как и в программе на C# в начале файла должны указываться все директивы using, так и в начале файла XAML также должны указываться все используемые пространства имен.
После добавления пространства имен нужно объявить имя класса, который описан в этом пространстве имен и используется в качестве ресурса:
Добавление класса к элементу на странице . После того как пространство имен стало доступным, можно соединить нужный класс из этого пространства имен с элементами страницы. В нашем случае таким элементом будет элемент Grid, который содержит все визуальные элементы страницы. Добавление класса к элементу автоматически делает его доступным для любых вложенных элементов, и таким образом, элементы TextBox и TextBlock могут использовать этот класс.
DataContext="{StaticResource AdderClass}">
Этот код указывает элементу Grid использовать класс AdderClass, указанный в значении атрибута DataContext. В нашем приложении он является статическим ресурсом, в котором класс является частью программы.
Привязка элемента Silverlight к свойству объекта . Теперь класс AdderClass доступен в контексте данных элементам на странице. При этом создается связь между страницей Silverlight и объектом, в котором описано поведение.
Теперь нужно привязать свойства каждого элемента к свойствам объекта. Для этого можно использовать область свойств для каждого элемента. Нужно связать текст элемента firstNumberTextBox со свойством TopValue класса AdderClass. Если щелкнуть по элементу TextBox в редакторе Visual Studio, откроется область свойств для этого элемента, в которой для свойства Text в контекстном меню нужно выбрать пункт Применить привязку данных…. После этого откроется окно, в котором нужно указать, с каким свойством какого объекта требуется связать выбранное свойство элемента.
Выбрав необходимый элемент из списка доступных элементов и их свойств, можно привязать свойство элемента Silverlight к свойству объекта. Обратите внимание, что в разделе Параметры выбран режим TwoWay, при котором при изменении пользователем значения в элементе TextBox объект автоматически был об этом уведомлен.
При связывании с элементом TextBlock доступен только режим OneWay, поскольку нельзя передать в программу данные из объекта TextBlock.
После установки привязки программа будет работать. При этом содержимое файла MainPage.xaml.cs с кодом программы не изменится. Вся работа теперь выполняется в классе, который соединен с визуальными элементами. Изменение содержимого элементов TextBox, используемых для ввода значений, приведет к возникновению события класса AdderClass, которые приведут к изменению содержимого класса AnswerValue, связанного с элементом ResultTextBlock.
Привязка данных через свойство DataContext
Для установки привязки к данным потребовалось проделать большую работу. Однако, существует способ упростить выполнение этих действий.
Установка привязки данных в XAML . Можно просто связать свойство объекта со свойством элемента в XAML-коде, и Silverlight выполнит привязку.
Name="firstNumberTextBox" Text="{Binding TopValue, Mode=TwoWay}"
VerticalAlignment="Top" Width="460" TextAlignment="Center" />
В этом коде указана привязка текста элемента к свойству TopValue объекта. Режим привязки установлен в значение TwoWay, чтобы изменения в программе (при вычислении результата) отображались на экране. Если нужно использовать визуальный элемент только для того, чтобы показать значение (например, результат вычисления), можно использовать режим привязки OneWay.
Установка свойства DataContext . Теперь остается создать экземпляр класса AdderClass и присвоить его свойству DataContext элемента, содержащего элементfirstNumberTextbox. Это можно сделать в конструкторе для основной страницы.
// Конструктор
public MainPage()
{
InitializeComponent();
AdderClass adder = new AdderClass();
ContentGrid.DataContext = adder;
}
Свойство DataContext элемента Silverlight идентифицирует объект, который содержит все свойства, связанные с элементом и с теми элементами, которые в нем содержатся. Элемент ContentGrid содержит элементы TextBox и TextBlock, и теперь каждая привязка, которую эти элементы содержат, будет отображаться на созданный экземпляр класса AdderClass.
В этом примере вся тяжелая работа с областью свойств и ресурсами заменена на небольшое количество XAML-кода и одну строку кода.