Часто задаваемые вопросы по SilverLight - часть 2
ОГЛАВЛЕНИЕ
В этой статье обсуждаются 12 вопросов, касающихся связывания, размещения, использования сервисов WCF, и как подключаться к базе данных при помощи Silverlight.
Введение
В этой статье обсуждаются 12 вопросов, касающихся связывания, размещения, использования сервисов WCF, и как подключаться к базе данных при помощи Silverlight. Статья начинается со связываний и описывает одностороннее, двустороннее и однократное связывания. Затем статья переходит к обсуждению трех различных способов размещения и позиционирования элементов управления silverlight. Статья завершается описанием того, как использовать сервисы WCF в Silverlight, и то, как можно работать с базой данных.
Объяснение одностороннего и двустороннего связываний
Одностороннее связывание
Это название для характера поведения. В одностороннем связывании данные текут только из объекта в пользовательский интерфейс, а не наоборот. Например, у вас может быть текстовое поле с названием ‘TxtYear’, связанное с объектом, имеющим свойство ‘Year(год)’. Поэтому когда значение объекта меняется, это отразится в пользовательском интерфейсе Silverlight, но пользовательский интерфейс не может изменить свойство года в объекте.

Это процедура из трех этапов по реализации одностороннего связывания. Сначала создайте класс, который вы хотите связать с пользовательским интерфейсом Silverlight. Ниже приведен пример простого класса под названием ‘ClsDate’ со свойством ‘Year (год)’.
public class clsDate
{
private int _intYear;
public int Year
{
set
{
_intYear = value;
}
get
{
return _intYear;
}
}
На втором этапе необходимо связать свойство ‘Year’ с текстовым полем пользовательского интерфейса silver light. Чтобы связать свойство, нужно задать ‘связывание путь=Year’ в свойстве «текст» объекта пользовательского интерфейса текстовое поле. ‘Year’ – это свойство, которое связывается с объектом пользовательского интерфейса текстовое поле.
<TextBox x:Name="txtCurrentYear" Text="{Binding Path=Year}" Height="30" Width="150" VerticalAlignment="Center" HorizontalAlignment="Center"></TextBox>
Последний этап – связывание контекста данных текстового поля с только что созданным объектом даты.
public partial class Page : UserControl
{
public Page()
{
InitializeComponent();
clsDate objDate = new clsDate();
objDate.Year = DateTime.Now.Year;
txtCurrentYear.DataContext = objDate;
}
}
Двустороннее связывание
Двустороннее связывание обеспечивает синхронизацию данных между пользовательским интерфейсом и объектами. Поэтому любое изменение в объекте отражается в интерфейсе, и любое изменение в интерфейсе отражается в объекте.
Чтобы реализовать двустороннее связывание, необходимо два дополнительных шага в дополнение к тем, которые предусмотрены для одностороннего связывания. Первое необходимое изменение – это задание режима как ‘TwoWay (двустороннего)’, как показано в следующем фрагменте кода XAML.
<TextBox x:Name="txtEnterAge" Text="{Binding Path=Age, Mode=TwoWay}" Height="30" Width="150" VerticalAlignment="Center" HorizontalAlignment="Center"></TextBox> Второе необходимое изменение – реализация интерфейса ‘INotifyPropertyChanged (уведомление об изменении свойства)’. Ниже приведен класс, показывающий, как реализовать интерфейс ‘INotifyPropertyChanged’. Учтите, что нужно импортировать пространство имен ‘System.ComponentModel’.
public class clsDate : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private int _intYear;
public int Year
{
set
{
_intYear = value;
OnPropertyChanged("Year");
}
get
{
return _intYear;
}
}
private void OnPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this,new PropertyChangedEventArgs(property));
}
}}
Связывание данных с контекстом данных – это обязательный шаг, который необходимо выполнить.
Что такое однократное связывание?
При однократном связывании данные перетекают из объекта в интерфейс только один раз. Отсутствует механизм отслеживания, обновляющий данные на обеих сторонах. Однократное связывание отличается повышенной производительностью по сравнению с двумя описанными ранее видами связывания. Такое связывание хорошо подходит для отчетов, когда данные загружаются только один раз и просматриваются.
<TextBox x:Name="txtEnterAge" Text="{Binding Path=Age, Mode=OneTime}" Height="30" Width="150" VerticalAlignment="Center" HorizontalAlignment="Center"></TextBox> Простой пример одностороннего и двустороннего связываний
Ниже приведен простой пример программы, в которой есть два текстовых поля, одно из которых принимает возраст, а второе текстовое поле вычисляет приблизительную дату рождения.
Ниже приведен простой класс, имеющий два этих свойства. Был реализован интерфейс ‘INotifyPropertyChanged’, поэтому можно реализовать двустороннюю связь для свойства года.
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
//
using System.ComponentModel;
namespace SilverLightBinding
{
public class clsDate : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private int _intYear;
private int _intAge;
public int Year
{
set
{
_intYear = value;
OnPropertyChanged("Year");
}
get
{
return _intYear;
}
}
public int Age
{
set
{
_intAge = value;
Year = DateTime.Now.Year - _intAge;
}
get
{
return _intAge;
}
}
private void OnPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this,
new PropertyChangedEventArgs(property));
}
}
}
}
Наконец, объекты интерфейса SilverLight были связаны со свойствами класса. Ниже приводится фрагмент кода XAML для этой же задачи. Нужно учесть, что ‘Age (возраст)’ связывается при помощи двустороннего режима, так как нужно изменять то же самое из пользовательского интерфейса.
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Center"> Enter your age in the below text box</TextBlock>
<TextBox x:Name="txtEnterAge" Text="{Binding Path=Age, Mode=TwoWay}" Height="30" Width="150" VerticalAlignment="Center" HorizontalAlignment="Center"></TextBox>
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Center">Your approximate birth date</TextBlock>
<TextBox x:Name="txtCurrentYear" Text="{Binding Path=Year}" Height="30" Width="150" VerticalAlignment="Center" HorizontalAlignment="Center"></TextBox>
Различные способы выполнения размещения доступны в SilverLight
В Silverlight предусмотрено три способа для управления размещения панели Canvas (холст), Grid (сетка) и Stack (стек). Каждая из этих методологий подходит для различных ситуаций согласно требованиям к размещению. Все эти элементы управления размещением наследуются от класса Панель. В следующих разделах тщательно разбирается каждый из них, чтобы можно было понять, как они работают.
Как на самом деле работает размещение Canvas (холст)?
Canvas – это простейшая методология для управления размещением. Она поддерживает абсолютное позиционирование при помощи координат ‘X’ и ‘Y’. ‘Canvas.Left (холст левый)’ помогает задать координату X, в то время как ‘Canvas.Top (холст верхний)’ помогает задать координаты ‘Y’.
Ниже приведен простой фрагмент кода, показывающий, как прямоугольный объект устанавливается в нужное положение с координатами (50,150) при помощи ‘Canvas’.
<Canvas x:Name="MyCanvas">
<Rectangle Fill="Blue" Width="100"
Height="100"
Canvas.Left="50"
Canvas.Top="150"/>
</Canvas>
Ниже показано, как выглядит изображение на экране. Когда вы выполните код, программа просмотра XAML поместит прямоугольный объект в позицию с абсолютными координатами ‘X” и ‘Y’.
Как можно реализовать размещение сетки?
Размещение по сетке помогает вам помещать элементы управления при помощи строк и столбцов. Это очень похоже на определенную в HTML таблицу.
Ниже показана простая таблица с двумя столбцами и двумя строками, заданная при помощи ‘Grid’. Показано, как выглядит табличное представление данных на экране. При помощи ‘Grid.ColumnDefinition (задание столбца сетки)’ были определены два столбца, и при помощи ‘Grid.RowDefinition (задание строки сетки)’ были определены две строки. Затем были созданы 4 текстовых поля, которые после этого были определены в каждой секции при помощи ‘Grid.Column’ и ‘Grid.Row’. Например, чтобы указать позицию первого столбца, необходимо установить ‘Grid.Column’ в ‘0’ и ‘Grid.Row’ в ‘0’. Для всех секций использовался одна и та же схема, и можно увидеть, что все текстовые поля размещены в соответствующих секциях.
Как можно реализовать размещение стека?
Название соответствует поведению. Стек позволяет расположить ваши элементы интерфейса вертикально или горизонтально.
Например, ниже показаны 4 элемента, которые размещены в элементе панели стека. Можно видеть, как стек выравнивает / ставит элементы один над другим. Можно расставить элементы интерфейса горизонтально или вертикально в зависимости от типа вашего размещения.
Различные шаги процесса использования сервиса WCF в Silverlight
Использование сервиса WCF – это процедура из 4 шагов.
Шаг 1: Создание вашего сервиса WCF
Первый шаг – это создание вашего сервиса WCF. При создании сервиса WCF он создает по умолчанию функцию ‘GetData’, которая принимает целочисленное значение и возвращает строку, содержащую сообщение “Вы ввели 10” , в случае если вы передали функции ‘10’ в качестве значения. Мы попробуем использовать этот сервис в silverlight в следующих шагах.
public class Service1 : IService1
{
public string GetData(int value)
{
return string.Format("You entered: {0}", value);
}
}
Шаг 2: Включение функции междоменной работы для вашего сервиса WCF
Например, сервис WCF и веб-приложение silver light будут размещены на разных веб-сайтах IIS (информационный интернет-сервер). Иными словами, они будут размещены в разных доменах. Когда идет речь о разных вебсайтах, это означает, что они размещены в разных доменах. Например, возможно, что ваше веб-приложение silver light размещено в одном домене типа http://www.xyz.com/, а ваш сервис WCF размещен в другом домене, т.е. http://www.pqr.com/ .
Нужно включить функцию междоменной работы для сервиса WCF, чтобы другие домены могли использовать сервис WCF.
Рисунок: Междоменная работа
Нужно создать два файла XML (clientaccesspolicy.xml и crossdomain.xml) в корневом каталоге сервиса WCF, чтобы включить возможность междоменной работы.
Ниже приведен фрагмент кода XML для clientaccesspolicy.xml.
<?xml version="1.0" encoding="utf-8" ?>
<access-policy>
<cross-domain-access>
<policy>
<allow-from http-request-headers="*">
<domain uri="*"/>
</allow-from>
<grant-to>
<resource include-subpaths="true" path="/"/>
</grant-to>
</policy>
</cross-domain-access>
</access-policy>
Ниже приведен фрагмент кода XML для crossdomain.xml
<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
<allow-http-request-headers-from domain="*" headers="*"/>
</cross-domain-policy>
Шаг 3: Добавление ссылки на сервис WCF и вызов сервиса
Создайте простое приложение silverlight и добавьте ссылку на сервис в ваш проект silverlight. Чтобы вызывать сервис WCF, нужно задать обработчики событий. Использование сервиса WCF – это процедура из трех шагов. На первом шаге ссылайтесь на пространство имен. На втором шаге создайте объект вашего сервиса WCF. На последнем шаге нужно создать обработчик события, который будет получать результаты, отправленные сервисом WCF.
Важно помнить, что функция ‘GetData’ вызывается асинхронно.
Шаг 4: Добавление ссылки на сервис WCF и вызов сервиса
В конце концов, компилируйте программу и наслаждайтесь результатом работы.

Почему нельзя использовать ADO.NET напрямую в SilverLight?
Silverlight не имеет ADO.NET
Ниже перечислены различные компоненты, образующие подключаемый модуль SilverLight. Важно учитывать то, что в его состав не входит ADO.NET. Иными словами, нельзя напрямую вызывать код ADO.NET из приложения SilverLight. Нужно помнить, что в него входит компонент WCF. То есть можно вызывать сервис WCF.
Можно создать сервис WCF, выполняющий работу с базой данных, и приложение silverlight будет вызывать его же. Также нельзя возвращать объекты ADO.NET в виде набора данных и т.д., так как SilverLight не сможет их понять.
Ниже описаны 7 важных шагов, которые необходимо выполнить, чтобы использовать сервис базы данных WCF в silverlight.
Работа с базой данных при помощи SilverLight
Шаг 1: Создание сервиса и контракта с сервисом данных
Ниже приводится простая таблица заказчиков, имеющая 3 поля: ‘CustomerId’ -являющееся идентификационным столбцом, ‘CustomerCode’ -хранящее код заказчика, и ‘CustomerName’ - хранящее имя заказчика. Мы выполним простой запрос на выборку при помощи WCF и затем отобразим данные на сетке SilverLight.
| Поле | Тип данных |
| CustomerId | int (целый) |
| CustomerCode | nvarchar(50) |
| CustomerName | nvarchar(50) |
В соответствии с заданной выше таблицей заказчиков сначала необходимо определить контракт данных WCF. Ниже приведен контракт данных заказчика WCF.
[DataContract]
public class clsCustomer
{
private string _strCustomer;
private string _strCustomerCode;
[DataMember]
public string Customer
{
get { return _strCustomer; }
set { _strCustomer = value; }
}
[DataMember]
public string CustomerCode
{
get { return _strCustomerCode; }
set { _strCustomerCode = value; }
}
}
Также нужно определить контракт с сервисом WCF, который будет реализован конкретными классами WCF.
[ServiceContract]
public interface IServiceCustomer
{
[OperationContract]
clsCustomer getCustomer(int intCustomer);
}
Шаг 2: Программирование сервиса WCF
Теперь, после определения контракта данных и контракта сервиса, необходимо реализовать контракт сервиса. Мы реализовали функцию ‘getCustomer’, которая будет возвращать контракт данных ‘clsCustomer’. Функция ‘getCustomer’ устанавливает простое соединение с ADO.NET и извлекает информацию о заказчике при помощи SQL-запроса на выборку.
public class ServiceCustomer : IServiceCustomer
{
public clsCustomer getCustomer(int intCustomer)
{
SqlConnection objConnection = new SqlConnection();
DataSet ObjDataset = new DataSet();
SqlDataAdapter objAdapater = new SqlDataAdapter();
SqlCommand objCommand = new SqlCommand("Select * from Customer where CustomerId=" + intCustomer.ToString());
objConnection.ConnectionString = System.Configuration.ConfigurationManager.ConnectionStrings["ConnStr"].ToString();
objConnection.Open();
objCommand.Connection = objConnection;
objAdapater.SelectCommand = objCommand;
objAdapater.Fill(ObjDataset);
clsCustomer objCustomer = new clsCustomer();
objCustomer.CustomerCode = ObjDataset.Tables[0].Rows[0][0].ToString();
objCustomer.Customer = ObjDataset.Tables[0].Rows[0][1].ToString();
objConnection.Close();
return objCustomer;
}
}
Шаг 3: Копирование файлов CrossDomain.xml и ClientAccessPolicy.XML
Этот сервис WCF будет вызываться с внешнего домена, поэтому нужно активировать междоменную политику в WCF путем создания файлов ‘CrossDomain.xml’ и ‘ClientAccessPolicy.xml’. Ниже приводятся оба фрагмента кода. Первый фрагмент кода для междоменной политики, а второй для политики доступа клиентов.<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
<allow-http-request-headers-from domain="*" headers="*"/>
</cross-domain-policy>
<?xml version="1.0" encoding="utf-8" ?>
<access-policy>
<cross-domain-access>
<policy>
<allow-from http-request-headers="*">
<domain uri="*"/>
</allow-from>
<grant-to>
<resource include-subpaths="true" path="/"/>
</grant-to>
</policy>
</cross-domain-access>
</access-policy>
Шаг 4:- Замена связываний WCF на ‘basicHttpBinding’
Silverlight использует и генерирует сервер-посредник только для ‘basicHttpBinding’, поэтому необходимо изменить связывания конечной точки соответствующим образом.
<endpoint address="" binding="basicHttpBinding" contract="WCFDatabaseService.IServiceCustomer">
Шаг 5: Добавление ссылки на сервис
Необходимо использовать ссылку на сервис в приложении silverlight при помощи меню ‘Добавить ссылку на сервис’. Щелкните правой кнопкой мыши по проекту silverlight и выберите ‘Добавить ссылку на сервис’.
Шаг 6: Задание сетки для имени заказчика и кода заказчика
Теперь на стороне silverlight будет создана ‘сетка’, имеющая два столбца: один для ‘кода заказчика’, а другой для ‘имени заказчика’. Также были заданы связывания при помощи ‘Пути связывания’ в текстовом поле.
<Grid x:Name="LayoutRoot" Background="White">
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="20"></RowDefinition>
<RowDefinition Height="20"></RowDefinition>
</Grid.RowDefinitions>
<TextBlock x:Name="LblCustomerCode" Grid.Column="0" Grid.Row="0" Text="Customer Code"></TextBlock>
<TextBlock x:Name="TxtCustomerCode" Grid.Column="1" Grid.Row="0" Text="{Binding Path=CustomerCode}"></TextBlock>
<TextBlock x:Name="LblCustomerName" Grid.Column="0" Grid.Row="1" Text="Customer Name"></TextBlock>
<TextBlock x:Name="TxtCustomerName" Grid.Column="1" Grid.Row="1" Text="{Binding Path=Customer}"></TextBlock>
</Grid>
Шаг 7: Связывание сервиса WCF с сеткой
Теперь сетка создана, и нужно связать сервис WCF с сеткой. Поэтому заново изучите код файла XAML и создайте объект сервиса WCF. Нужно учитывать две важные вещи при вызове сервиса WCF через SilverLight: (-)
- Необходимо вызывать WCF асинхронно, поэтому вызывается ‘getCustomerAsynch’. Учтите, что эта функция создается сервисом WCF для выполнения асинхронных вызовов метода / функции.
- Как только функция завершает свою работу с сервисом WCF, она отправляет сообщение обратно клиенту silverlight. Поэтому необходимо иметь какой-то вид метода-делегата, который может облегчить это взаимодействие. Был создан метод ‘getCustomerCompleted’, который захватывает аргументы и связывает результаты с сеткой ‘datacontext (контекст данных)’.
public partial class Page : UserControl
{
public Page()
{
InitializeComponent();
ServiceCustomerClient obj = new ServiceCustomerClient();
obj.getCustomerCompleted += new EventHandler<getCustomerCompletedEventArgs>(DisplayResults);
obj.getCustomerAsync(1);
}
void DisplayResults(object sender, getCustomerCompletedEventArgs e)
{
LayoutRoot.DataContext = e.Result;
}
}
Теперь вы можете запустить проект и увидеть, как клиент silverlight использует и отображает данные.
