Введение в Microsoft Silverlight 2

Создание взаимодействующих с сервером приложений в Silverlight

Разбить на страницы
Показывать лекцию целиком

В данной лекции вы узнаете, как можно создавать взаимодействующие с сервером приложения, используя Silverlight. Лекция разделена на три основных раздела. В первом мы обратимся к архитектуре Silverlight, и вы увидите, как дизайн в ней отделен от реализации, что позволяет создавать сервисы на Personal Hypertext Processor (PHP) или Java, генерирующие XAML, который формирует визуальное представление вашего Silverlight-приложения. Во втором разделе рассматривается сам браузер и его использование для подключения к удаленным сервисам с помощью технологии AJAX. В последней части представлены некоторые классы, доступные в .NET, включая WebClient и WebRequest, и показано их использование для получения данных с сервера и отображения в приложении Silverlight.

Подключение приложений с помощью XAML

Одним из свойств Silverlight, делающим его таким мощным инструментом, является то, что XAML - это XML и, таким образом, он может быть сформирован сервером. В примерах, которые были представлены до сих пор, XAML статичен: документ создается дизайнером в каком-нибудь инструменте, таком как Expression Blend. Однако вы не ограничены лишь статическим XAML и можете создавать целый ряд разнообразнейших сценариев, динамически формируя XAML и передавая его пользователям в ответ на запрос или переход приложения в определенное состояние.

Например, требуется создать приложение прогноза погоды. При использовании статического XAML для этого пришлось бы создать сложную систему, которая периодически формировала бы различные XAML-документы для каждого из местоположений пользователей, а пользователи запрашивали бы и загружали соответствующие документы. Намного проще создать шаблон приложения и заполнять поля подстановки в UI приложения, используя сведения, полученные с сервера.

Серверные приложения могут создаваться на разных языках программирования. В этом разделе обсуждаются PHP и Java. Будет рассмотрено, как XAML генерируется в каждом из этих языков, сначала в общем, а затем на конкретном примере с использованием сведений прогноза погоды.

Silverlight и PHP

PHP (можно скачать на сайте http://www.php.net) - простой, но при этом мощный язык написания сценариев, который может использоваться для создания XAML шаблона. Он обычно принимает PHP-код в качестве входных данных и создает Веб-страницы на выходе. Также динамически можно формировать XAML Если вы не знакомы с PHP, далее представлены базовые приемы работы с ним.

Ваша первая PHP-страница

PHP может использоваться для динамического формирования Веб-страниц на основании входных параметров. Так, например, рассмотрим страницу, созданную с использованием этого HTML-кода:

<HTML>
  <HEAD>
  </HEAD>
  <BODY>
    <H1>Hello, World!</H1>
  </BODY>
</HTML>

Это представляется как простая HTML-страница с фразой "Hello, World!", которую можно увидеть на рис. 10.1.

(рис 10.1) Простая Веб страница "Hello World!"

С помощью PHP эту простую страницу можно сформировать динамически. В данном случае, страница принимает параметр, и препроцессор PHP подставляет его. Ниже представлен PHP-код:

<HTML>
  <HEAD>
  </HEAD>
  <BODY>
    <H1>Hello, 
      <?php echo($   REQUEST['name']);?>!
    </H1>
  <BODY>
</HTML>

Здесь показан код разметки HTML со встроенным PHP-кодом, извлекающим параметр URL name и записывающий его в выходной поток с помощью команды echo. Итак, если вы перейдете на эту страницу, передавая ей значение name, процессор PHP динамически создаст разметку страницы. Например, на рис. 10.2 показано, как будет представлен на экране следующий параметр URL:

http://localhost/phptext/Hello.php?name=Laurence

(рис 10.2) Динамическое формирование страниц на PHP

Учебный пример PHP и XAML

Тот же принцип может применяться и к XAML Не составит большого труда создать в Expression Blend XAML-шаблон представления содержимого и использовать его на PHP-странице. Если необходимо представить повторяющееся содержимое, такое как список, для отображения можно использовать PHP-цикл.

Лучше всего продемонстрировать это на примере. PHP (наряду с Apache, MySQL и Linux) является основным элементом знаменитого набора LAMP, поэтому давайте рассмотрим, как создать приложение PHP/MySQL, которое могло бы выполняться на любом Веб-сервере или операционной системе, поддерживающей LAMP. (Аббревиатура расшифровывается как L inux, A pache, M ySQL и P HP.) Фактически мы будем создавать приложение "Windows IIS MySQL PHP", сокращенно WIMP, что означает "бесхарактерный человек, тряпка" - совершенно никудышнее название.

Создание базы данных на стороне сервера

В данном случае, MySQL используется для создания базы данных имен и адресов на стороне сервера. MySQL можно скачать по адресу http://www.mysql.com. Там можно найти бесплатную версию продукта, идеально подходящую для экспериментов.

Кроме этого, предлагается набор инструментов GUI, таких как MySQL Administrator и MySQL Query Browser, для построения и заполнения баз данных. Инструкции по применению этих инструментальных средств выходят за рамки рассмотрения данного курса, однако исчерпывающую документацию можно найти на Веб-сайте .

На рис. 10.3 показан MySQL Table Editor (Редактор таблиц MySQL) и его использование для описания простой таблицы имен и адресов.

(рис 10.3) Описание таблицы имен и адресов в MySQL

Редактировать содержимое таблицы можно в MySQL Query Browser (Обозреватель запросов MySQL). Просто щелкните кнопку Edit (Редактировать) внизу UI приложения (рис. 10.4) и можно вводить имя и адрес, как показано на рис. 10.4.

(рис 10.4) Добавление данных в таблицу с помощью MySQL Query Browser

Теперь у нас есть данные, которые можно отображать с помощью PHP. Пора связать их с XAML-шаблоном. В следующем разделе мы научимся создавать шаблон в Expression Blend.

Создание XAML-шаблона

Теперь, когда у нас есть данные, подумаем о том, как представить их, используя XAML Expression Blend - замечательный визуальный редактор для XAML На рис. 10.5 приведен пример XAML-шаблона, который может использоваться для вывода на экран данных адреса и имени из таблицы.

(рис 10.5) Создание XAML шаблона в Expression Blend

Полученный в результате XAML представляет коллекцию объектов Canvas, Rectangle и TextBlock. Полный код XAML-шаблона, представленного на рис. 10.5, приводится в Листинге 10.1. Обратите внимание, что корневым узлом этого XAML является Canvas, а все элементы UI располагаются в другом Canvas, который и будет повторяться при отображении множества записей.

<Canvas xmlns="http://schemas.microsoft.com/client/2007"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/"
        xamlWidth="640" Height="480" Background="#FF460608">
  <Canvas Width = "352" Height="128" Canvas.Left="10" Canvas.Top="8">
  <Rectangle Fill="#FF693B3D" Stroke="#FF000000" StrokeThickness="0"
             RadiusX="16" RadiusY="16" Width="352" Height="128" Canvas.Top="1"/>
  <Rectangle Stroke="#FF000000" StrokeThickness="0" RadiusX="8" RadiusY="8" 
             Width = "336" Height="40" Canvas.Left="8" Canvas.Top="8">
    <Rectangle.Fill >
      <LinearGradientBrush EndPoint="0.518,0.175" StartPoint="0.515,0.825" >
        <GradientStop Color="#FF460608" Offset="0"/>
        <GradientStop Color="#FF841316" Offset="1"/>
      </LinearGradientBrush>
    </Rectangle.Fill>
  </Rectangle>
  <TextBlock x:Name="txtName" Width="312" Height="24" Canvas.Left="16" Canvas.Top="16" 
             FontFamily="Arial Unicode MS" FontSize="18" FontWeight="Normal" Foreground="#FFFFFFFF" 
             Text="User Name" TextWrapping ="Wrap"/>
  <Rectangle Stroke="#FF000000" StrokeThickness="0" RadiusX="8" RadiusY="8" 
             Width="336" Height="23" Canvas.Left="8" Canvas.Top="48">
    <Rectangle.Fill >
      <LinearGradientBrush EndPoint="0.518,0.175" StartPoint="0.515,0.825">
        <GradientStop Color="#FF460608" Offset="0"/>
        <GradientStop Color="#FF841316" Offset="1"/>
      </LinearGradientBrush>
    </Rectangle.Fill>
  </Rectangle>
  <TextBlock x:Name="txtAddr1" Width = "312" Height="24" FontFamily="Arial Unicode MS" FontSize="12" 
             FontWeight="Normal" Foreground="#FFFFFFFF" Text="Address1" TextWrapping = "Wrap" 
             Canvas.Left="14" Canvas.Top="48"/>
  <Rectangle Stroke="#FF000000" StrokeThickness="0" RadiusX="8" RadiusY="8" Width="336" Height="23" 
             Canvas.Left="8" Canvas.Top="71">
    <Rectangle.Fill >
      <LinearGradientBrush EndPoint="0.518,0.175" StartPoint="0.515,0.825">
        <GradientStop Color="#FF460608" Offset="0"/>
        <GradientStop Color="#FF841316" Offset="1"/>
      </LinearGradientBrush>
    </Rectangle.Fill>
  </Rectangle>
  <TextBlock x:Name="txtAddr2" Width = "312" Height="24" FontFamily="Arial Unicode MS" 
             FontSize="12" FontWeight="Normal" Foreground="#FFFFFFFF" Text="Address2" TextWrapping ="Wrap" 
             Canvas.Left="15" Canvas.Top="72"/>
  <Rectangle Stroke="#FF000000" StrokeThickness="0" RadiusX="8" RadiusY="8" Width="168" Height="23" 
             Canvas.Left="176" Canvas.Top="94">
    <Rectangle.Fill >
      <LinearGradientBrush EndPoint="0.518,0.175" StartPoint= "0.515,0.825" >
        <GradientStop Color="#FF460608" Offset="0"/>
        <GradientStop Color="#FF841316" Offset="1"/>
      </LinearGradientBrush>
    </Rectangle.Fill>
  </Rectangle>
  <Rectangle Stroke="#FF000000" StrokeThickness="0" RadiusX="8" RadiusY="8" Width="168" Height="23" 
             Canvas.Left="8" Canvas.Top="94">
    <Rectangle.Fill >
      <LinearGradientBrush EndPoint="0.518,0.175" StartPoint="0.515,0.825">
        <GradientStop Color="#FF460608" Offset="0"/>
        <GradientStop Color="#FF841316" Offset="1"/>
      </LinearGradientBrush>
    </Rectangle.Fill>
  </Rectangle>
  <TextBlock x:Name="txtCity" Width="144" Height="24" FontFamily="Arial Unicode MS" FontSize="12" 
             FontWeight="Normal" Foreground="#FFFFFFFF" Text="City" TextWrapping = "Wrap" 
             Canvas.Left="15" Canvas.Top="96"/>
  <TextBlock x:Name="txtState" Width = "144" Height="24" FontFamily="Arial Unicode MS" FontSize="12" 
             FontWeight="Normal" Foreground="#FFFFFFFF" Text="State" TextWrapping = "Wrap" 
             Canvas.Left="182" Canvas.Top="96"/>
</Canvas>
</Canvas>

Свойству Canvas.Top второго Canvas (далее называемого контейнером) задано значение 8 пикселов и свойству Height задано значение 128 пикселов. Следовательно, если под первым экземпляром этого контейнера необходимо разместить другие экземпляры, для второго контейнера Canvas.Top должен быть определен как 136 (128 + 8), для следующего - 264 (128 + 128 + 8) и т.д.

Все элементы UI размещаются в контейнере и все они должны иметь в XAML уникальные имена. Как видите, в этом примере XAML имя каждого TextBlock определятся атрибутом x:Name. При клонировании контейнера XAML должен обеспечивать уникальные имена для этих узлов. Алгоритм присваивания имен будет прост- в конце каждого имени будет добавляться индекс элемента. Итак, например, записанное в шаблоне значение x:Name txtState для первой строки базы данных превратиться в txtState1, для второй строки - txtState2, и т.д.

Как будет показано в следующем разделе, размножить эти элементы в PHP очень просто.

Формирование XAML из PHP и MySQL

Синтаксический разбор страницы выполняет PHP. Это означает, что когда сервер получает запрос на страницу, PHP записывает форматированные данные в буфер ответов. Когда PHP встречает специальный открывающий тег ( <?php ), он передает обработку синтаксическому анализатору PHP. Анализатор выполняет код, выявляемый процессором PHP, пока не встретит закрывающий тег ( ?> ). Это делает связку MySQL и PHP идеальным сочетанием для отображения содержимого, формируемого по шаблону. Шаблон для формирования содержимого просто помещается в файл с расширением .php, а там, где требуется вставить некоторые связанные с данными поля подстановки, добавляется необходимая PHP-логика, заключенная в открывающий и закрывающий теги ( <?php и ?>, соответственно).

PHP-код может содержать циклы. Если после выражения цикла следует некоторое содержимое, оно будет выведено многократно. В теле цикла могут присутствовать открывающий/закрывающий теги РНР. Например, можно сделать следующее:

<?php
 for($i =0; $i<100; $i ++)
  { // Открывающая скобка цикла
    // Выходим из PHP, хотя цикл еще не закрыт!
?>
  <H1>My Text Line is <?php echo $i; ?></H1>
<?php
  } // Теперь закрываем наш цикл
 ?>

Это обеспечит повторение HTML-содержимого (текста, заключенного в элемент <H1> ) 100 раз. Как видите, не обязательно заключать весь текст в теги <?php ... ?>. Это полезное свойство при объединении PHP и XAML

В XAML холст-контейнер может быть помещен в цикл:

<?php
 for($i =0; $i<100; $i ++)
  { // Открывающая скобка цикла
    // Выходим из PHP, хотя цикл еще не закрыт!
?>
<Canvas>
...
</Canvas>
<?php
 } // Теперь закрываем наш цикл
?>

Выполнение этого кода обеспечит запись XAML 100 раз! Это делает PHP мощным языком описания шаблонов для Silverlight.

Возвращаясь к нашему примеру, рассмотрим, как установить подключение к базе данных, выполнить запрос к ней и записать то количество XAML-элементов Canvas, которое соответствует числу возвращенных результатов. Также отредактируем содержимое элементов TextBlock холста-контейнера, чтобы они отображали информацию, полученную из базы данных, а также обеспечим уникальность имен TextBlock.

В итоге должна получиться PHP-страница, формирующая XAML, который будет использоваться как источник для элемента управления Silverlight. Но чтобы было немного интересней, изменим PHP и введем параметр state (штат), который обеспечит возвращение сведений только для людей, проживающих в заданном штате. Это превратит наш код в настоящий динамический генератор XAML

Во-первых, необходимо, чтобы Silverlight распознавал вывод PHP какXML. Для этого задаем тип MIME содержимого вывода. Это делается в PHP с помощью команды header (заголовок):

header('Content-type: text/xml');

Извлечение параметра строки запроса (т.е. http://server/script.php?param =value) в PHP обеспечивается массивом $ REQUEST. Итак, чтобы прочитать значение параметра State, используем следующий код:

$State=$  REQUEST['State'];

В PHP версии 4.x имеется встроенный набор команд MySQL Для версий 5.x и выше набор команд MySQL должен задаваться как расширение. Независимо от используемой версии, синтаксис PHP аналогичен.

Подключение к серверу localhost с использованием имени пользователя user и пароля password выполняется с помощью команды mysql connect:

$con = mysql  connect("localhost", "user", "password");

На сервере может располагаться несколько баз данных. Для выбора необходимой базы данных используется команда mysql select db:

mysql  select db("test", $con); // "test"     имя базы данных

Затем, чтобы выполнить запрос к этой базе данных, используется команда mysql query, в которую передается строка, содержащая фактический запрос SQL Результат запроса будет представлять собой массив массивов значений. Приведем пример:

$sqlString = "SELECT * from addresses"; 
$result = mysql  query($sqlString);

Затем массив mysql fetch array разбивается на строки. С помощью цикла while можно обойти каждую строку, как показано в следующем примере:

while($row = mysql fetch  array($result))
{
...
}

таким образом, исходя из данного сценария, получаем такой алгоритм действий:

Задаем тип MIME text/xml. Принимаем входной параметр (штат). Записываем начальный тег "корневого" Canvas. Используем входной параметр для построения запроса. Выполняем запрос и получаем результирующее множество. Для каждой строки результирующего множества:

  • a. Создаем XAML-шаблона.
  • b. Заполняем атрибуты Text элементов TextBlock данными из соответствующих полей базы данных.
  • c. Заполняем атрибуты x:Name уникальными ID, полученными на основании порядкового номера
  • строки.

    Закрываем тег "корневого" Canvas.

    Код PHP-страницы полностью приведен в листинге 10.2. Разметка PHP в блоке XAML выделена жирным шрифтом.

    <?php
    header('Content-type: text/xml');
    $State=$   REQUEST['State'];
    ?>
    <Canvas xmlns=" http://schemas.microsoft.com/client/2007"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xam l"
            Width="640" Height="480" Background = "#FF460608">
      <?php
      $height=128; $top=8; $i=0;
      $con = mysql  connect("localhost", "root", "root"); 
      mysql  select db("test",$con); 
      $sqlString = "SELECT * from addresses"; 
      if($State! = "") {
        $sqlString = $sqlString . " WHERE State = '" . $State . "'"; }
      $result = mysql  query($sqlString); while($row = mysql fetch  array($result)) { ?>
      <Canvas Width="352" Height="128" Canvas.Left="10" Canvas.Top =" <"?php echo($top="" + ($height*$i="")); ?>" >
        <Rectangle Fill="#FF693B3D" Stroke="#FF000000" StrokeThickness="0" RadiusX="16" RadiusY="16" 
                   Width = "352" Height="128" Canvas.Top="1"/> 
        <Rectangle Stroke="#FF000000" StrokeThickness="0" RadiusX="8" RadiusY="8" 
                   Width="336" Height="40" Canvas.Left="8" Canvas.Top="8">
          <Rectangle.Fill>
            <LinearGradientBrush EndPoint="0.518,0.175" StartPoint="0.515,0.825">
              <GradientStop Color="#FF460608" Offset="0"/>
              <GradientStop Color="#FF841316" Offset="1"/>
            </LinearGradientBrush>
          </Rectangle.Fill>
        </Rectangle>
        <TextBlock x:Name="txtName"<?php echo="" $i=""; ?>" 
                   Width="312" Height="24" Canvas.Left="16" Canvas.Top="16" FontFamily="Arial Unicode MS" 
                   FontSize="18" FontWeight="Normal" Foreground="#FFFFFFFF" Text="<?php echo($row['Name']); ?>" 
                   TextWrapping ="Wrap"/> 
        <Rectangle Stroke="#FF000000" StrokeThickness="0" RadiusX="8" RadiusY="8" Width="336" Height="23" 
                   Canvas.Left="8" Canvas.Top="48">
          <Rectangle.Fill>
            <LinearGradientBrush EndPoint="0.518,0.175" StartPoint="0.515,0.825">
              <GradientStop Color="#FF460608" Offset="0"/>
              <GradientStop Color="#FF841316" Offset="1"/>
            </LinearGradientBrush>
          </Rectangle.Fill>
        </Rectangle> 
        <TextBlock x:Name="txtAddr1"
                   <?php echo="" $i=""; ?>" 
                   Width="312" Height="24" FontFamily="Arial Unicode MS" FontSize="12" 
                   FontWeight="Normal" Foreg round="#FFFFFFFF"
                   Text="<?php echo($row['Address1']); ?>" TextWrapping ="Wrap" Canvas.Left="14" Canvas.Top="48"/> 
        <Rectangle Stroke="#FF000000" StrokeThickness="0" RadiusX="8" RadiusY="8" 
                   Width="336" Height="23" Canvas.Left="8" Canvas.Top="71">
          <Rectangle.Fill>
            <LinearGradientBrush EndPoint="0.518,0.175" StartPoint="0.515,0.825">
              <GradientStop Color="#FF460608" Offset="0"/>
              <GradientStop Color="#FF841316" Offset="1"/>
            </LinearGradientBrush>
          </Rectangle.Fill>
        </Rectangle>
        <TextBlock x:Name="txtAddr2"
                   <?php echo="" $i=""; ?>" Width = "312" Height="24" FontFamily="Arial Unicode MS" 
                   FontSize="12" FontWeight="Normal" Foreground="#FFFFFFFF"
                   Text="<?php echo($row['Address2']); ?>" TextWrapping="Wrap" 
                   Canvas.Left="15" Canvas.Top="72"/> 
        <Rectangle Stroke="#FF000000" StrokeThickness="0" RadiusX="8" RadiusY="8" 
                   Width="168" Height="23" Canvas.Left="176" Canvas.Top="94">
          <Rectangle.Fill>
            <LinearGradientBrush EndPoint="0.518,0.175" StartPoint="0.515,0.825">
              <GradientStop Color="#FF460608" Offset="0"/>
              <GradientStop Color="#FF841316" Offset="1"/>
            </LinearGradientBrush>
          </Rectangle.Fill>
        </Rectangle>
        <Rectangle Stroke="#FF000000" StrokeThickness="0" RadiusX="8" RadiusY="8" 
                   Width="168" Height="23" Canvas.Left="8" Canvas.Top="94">
           <Rectangle.Fill>
             <LinearGradientBrush EndPoint="0.518,0.175" StartPoint="0.515,0.825">
               <GradientStop Color="#FF460608" Offset="0"/>
               <GradientStop Color="#FF841316" Offset="1"/>
             </LinearGradientBrush>
          </Rectangle.Fill>
        </Rectangle>
        <TextBlock x:Name="txtCity"
                    <?php echo="" $i=""; ?>" Width = "144" Height="24" FontFamily="Arial Unicode MS" 
                    FontSize="12" FontWeight="Normal" Foreground="#FFFFFFFF" 
                    Text="<?php echo($row['City']); ?>" TextWrapping= "Wrap" 
                    Canvas.Left="15" Canvas.Top="96"/> 
        <TextBlock x:Name="txtState"
                   <?php echo="" $i=""; ?>" Width="144" Height="24" FontFamily="Arial Unicode MS" 
                   FontSize="12" FontWeight="Normal" Foreground="#FFFFFFFF" 
                   Text="<?php echo($row['State']); ?>" TextWrapping = "Wrap" 
                   Canvas.Left="182" Canvas.Top="96"/>
      </Canvas>
      <?php $i++; }?>
    </Canvas>

    Создание PHP-страницы для отображения Silverlight

    Как было показано в предыдущем разделе, сервер выводит содержимое напрямую, пока не встречает открывающий тег ( <?php ). Встретив этот тег, сервер обращается к интерпретатору PHP, который выполняет код, заключенный в открывающий и закрывающий теги ( ?> ).

    PHP-код обычно используется для создания динамического HTML-кода. Поскольку Silverlight отображается с помощью HTML, HTML-страница просто редактируется: в нее добавляются теги PHP, и меняется имя файла с добавлением расширения .php. Итак, чтобы реализовать отображение Silverlight, используя PHP, необходимо, чтобы страница, включающая Silverlight.js и createSilverlight.js, вызывала createSilverlight для создания экземпляра элемента управления Silverlight.

    Мы хотим, чтобы пользователь мог выбирать имена и адреса в базе данных по штату. Таким образом, эта страница должна принимать параметр и применять его при создании содержимого Silverlight с использованием XAML-источника, сформированного PHP, который был представлен в предыдущем разделе. К счастью, это очень просто сделать в PHP. Чтобы вывести на экран, сформированный XAML, необходимо всего лишь присвоить в качестве значения свойства content элемента управления Silverlight Uniform Resource Indicator (URI) вашего приложения. Silverlight будет подключаться к серверу по этому URI, принимать результирующий XAML и формировать его визуальное представление.

    Далее представлен полный код примера PHP-страницы, формирующей HTML:

    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
      <title>SilverlightJSApplication2</title>
      <script type="text/javascript" src="Silverlight.js">
      </script>
      <script type="text/javascript" src="createSilverlight.js">
      </script>
      <script type="text/javascript">
        function handleLoad(control, userContext, rootElement)
        {
        <?php 
         if(!isset($_REQUEST['State']))
           $State=""; 
         else $State=$_REQUEST['State']; 
        ?>
        control.source = "http://localhost/phptest/xaml.php?State=
          <?php echo($State) ?>"; 
        } 
      </script> 
    </head> 
    <body> 
      <div id="SilverlightControlHost"> 
        <script type="text/javascript">
          createSilverlight(); 
        </script> 
      </div> 
    </body> 
    </html>

    На рис. 10.6 и 10-7 показано, как эта PHP-страница отображается в браузере с использованием Silver-light для адресов в Нью-Йорке (NY в базе данных) и Вашингтоне (WA).

    (рис 10.6) Содержимое, сформированное приложением PHP для адресов в Нью Йорке(рис 10.7) Содержимое, сформированное приложением PHP для адресов в Вашингтоне

    Silverlight и Java

    Язык программирования Java компилируется в байт-код, который либо интерпретируется, либо компилируется методом Just-In-Time (JIT) и вызывается во время выполнения на виртуальной машине. Это делает его идеально подходящим для серверных сред, особенно когда в центре обработки данных представлены различные операционные системы. Java поддерживается практически всеми основными операционными системами.

    Существует много способов создания серверных приложений на Java и ее версии для предприятий, называемой обычно J2EE или просто JEE. Один из методов - Серверные страницы Java (Java Server Pages, JSP), концепция которых аналогична PHP, в которых для разметки необходимого вывода используется код, выполняющийся во время выполнения. Итак, для создания таких страниц используется стандартная разметка HTML, но в нее помещаются теги JSP, которые будут выполняться при отображении страницы. Другой способ - использование Сервлета (Servlet) Java, который является приложением, выполняющимся на сервере и принимающим параметры через HTTP.

    Открытость Silverlight позволяет создавать приложения, объединяющие в себе Silverlight и Java. Для построения страниц, включающих содержимое Silverlight, может использоваться технология JSP и серверные технологии, такие как сервлеты, идеально подходят для формирования XAML, отображаемого браузером.

    Чтобы продемонстрировать создание приложений Silverlight в среде Java, поработаем с обеими технологиями. Сначала рассмотрим, как построить JSP-страницу для размещения элемента управления Silverlight. Затем создадим сервлет для генерирования XAML, визуальное представление которого будет формировать элемент управления Silverlight.

    Создание сервлета для генерирования XAML

    Сервлет - это приложение, расширяющее класс HttpServlet из пространства имен javax.servlet. Он предоставляет две функции, doGet и doPost, используемые для перехвата команд HTTP-GET v\ HTTP-POST, соответственно. Каждая из этих функций принимает объекты HTTPRequest (HTTP-запрос) и HTTPResponse (HTTP-ответ). Первый используется для получения сведений запроса, второй управляет ответом. Как правило, параметры считываются из объекта HTTPRequest, выходные данные записываются в объект HTTPResponse. Далее представлен пример этих функций:

    protected void doGet(HttpServletRequest request, HttpServletResponse response) 
     throws ServletException, IOException {
      // Обрабатываем GET 
     }
    protected void doPost(HttpServletRequest request, HttpServletResponse response) 
      throws ServletException, IOException {
      // Обрабатываем POST 
    }

    В типовом шаблоне проектирования создается вспомогательная функция, принимающая объекты запроса и ответа, и затем методы doGet и doPost вызывают эту функцию. С этой точки зрения, код будет выполняться один и тот же, независимо от метода вызова сервлета.

    В данном примере сервлет принимает XAML-документ как шаблон и использует прикладные программные интерфейсы (API) XML в Java для обработки документа, заполняя его результатами запроса к базе данных.

    Далее представлен простой XAML-документ, используемый в этом учебном примере:

    <Canvas xmlns=" http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Width="200" Height="80" Background="#FF000000">
      <TextBlock x:Name="txtName" Width = "200" Height="24" Canvas.Left="24" 
           Canvas.Top="8" Foreground="#FFEFB9B9" Text="Name" TextWrapping="Wrap"/>
      <TextBlock x:Name="txtCity" Width = "200" Height="24" Canvas.Left="24" 
           Canvas.Top="40" Foreground="#FFEFB9B9" Text="City" TextWrapping="Wrap"/>
      <TextBlock x:Name="txtCountry" Width="200" Height="24" Canvas.Left="24" 
           Canvas.Top="72" Foreground="#FFEFB9B9" Text="Country" TextWrapping="Wrap"/>
    </Canvas>

    Данный XAML включает три элемента TextBlock. Сервлет выполняет запрос к таблице Customers (Покупатели) базы данных Northwind и заполняет элементы TextBlock результатами запроса. Запрос принимает в качестве параметра ID покупателя и использует его для извлечения соответствующей записи покупателя, в которой нас интересуют имя покупателя, город проживания и страна.

    Если у вас еще нет используемой в этом примере базы данных Northwind, ее можно скачать на сайте Microsoft Developer Network (MSDN). База данных Northwind Traders является примером базы данных, которая предоставляется Microsoft свободно для SQL Server. Она содержит базу сведений о фиктивном Интернет-магазине и является полезным источником тестовых данных для таких примеров, какой рассматривается здесь.

    Чтобы заполнить элементы TextBlock, необходимо найти их в XAML-документе. При использовании в Java XML API для поиска узлов по XPath понадобится задать префикс стандартного пространства имен для элементов. Так позже вы заметите, что ссылки на элементы стандартного пространства имен ( <TextBlock>, например) формируются с использованием добавляемого во время выполнения префикса стандартного пространства имен (т.е. <d:TextBlock> ).

    Итак, в Java мы будем использовать API XML для поиска узла, который необходимо редактировать, по имени. Например, TextBlock, содержащий сведения о стране, называется txtCountry. Таким образом, для формирования XAML нашего приложения вспомогательная функция должна выполнять следующие шаги:

    Принять входной параметр. Если он null, присвоить ему значение по умолчанию.

    Задать text/xml в качестве выходного типа MIME.

    Создать объект для XML-документа и загрузить в него XAML-файл шаблона. В этом примере XAML хранится в документе Template.xml.

    Открыть базу данных SQL Server и выполнить запрос к таблице Northwind Customers для поиска покупателя, соответствующего входному параметру.

    Необходимо будет прочитать максимум одну строку, читаем ее.

    Используем XPath для узла ContactName (Имя контакта), чтобы найти его, и замещаем его содержимое значением поля ContactName, полученным из базы данных.

    Используем XPath для узла City (Город), чтобы найти его, и замещаем его содержимое значением поля City, полученным из базы данных.

    Используем XPath для узла Country (Страна), чтобы найти его, и замещаем его содержимое значением поля Country, полученным из базы данных.

    Записываем XML в поток HTTP-ответа.

    Имейте в виду этот алгоритм, рассматривая код листинга 10.3, в котором показана вспомогательная функция, используемая сервлетом для генерирования XAML

    protected void processRequest(HttpServletRequest request,
        HttpServletResponse response)
      throws ServletException, IOException {
      try
      {
        String strID = request.getParameter("ID");
        if(strID==null)
          strID="ALFKI";
        DocumentBuilderFactory factory =
        DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(true);
        response.setContentType("text/xml");
        PrintWriter out = response.getWriter();
        DocumentBuilder builder = factory.newDocumentBuilder();
        String uri = getServletContext().getRealPath("template.xml");
        Document doc = builder.parse(uri);
        DOMSource domSource = new DOMSource(doc);
        StreamResult streamResult = new StreamResult(out);
        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer serializer = tf.newTransformer();
        XPathFactory xpFactory = XPathFactory.newInstance();
        XPath xpath = xpFactory.newXPath();
        xpath.setNamespaceContext(new DefaultNameSpaceContext());
        String strContactName="";
        String strCity="";
        String strCountry="";
        java.lang.Class.forName
          ("com.microsoft.sqlserver.jdbc.SQLServerDriver");
        Connection c = java.sql.DriverManager.getConnection
          ("jdbc:sqlserver://localhost\\SQLEXPRESS;databasename=Northwind;
        user=javauser;password=javauser;");
        String SQL =
          "Select ContactName, City, Country
        from Customers where CustomerID = ?";
        PreparedStatement pstmt = c.prepareStatement(SQL);
        pstmt.setString(1, strID);
        ResultSet rs = pstmt.executeQuery();
        while (rs.next()) 
        {
          strContactName = rs.getString("ContactName");
          strCity=rs.getString("City");
          strCountry=rs.getString("Country");
          String strXPath = "//d:TextBlock[@x:Name='txtName']";
          XPathExpression expr = xpath.compile(strXPath);
          Object result = expr.evaluate(doc,XPathConstants.NODESET);
          NodeList nodes = (NodeList) result;
          Node ndeT1 =
            nodes.item(0).getAttributes().getNamedItem("Text");
          ndeT1.setNodeValue(strContactName);
          strXPath = "//d:TextBlock[@x:Name='txtCity']";
          expr = xpath.compile(strXPath);
          result = expr.evaluate(doc,XPathConstants.NODESET);
          nodes = (NodeList) result;
          ndeT1 =
          nodes.item(0).getAttributes().getNamedItem("Text");
          ndeT1.setNodeValue(strCity);
          strXPath = "//d:TextBlock[@x:Name='txtCountry']";
          expr = xpath.compile(strXPath);
          result = expr.evaluate(doc,XPathConstants.NODESET);
          nodes = (NodeList) result;
          ndeT1 =
            nodes.item(0).getAttributes().getNamedItem("Text");
          ndeT1.setNodeValue(strCountry);
        }
        rs.close();
        pstmt.close();
        serializer.transform(domSource, streamResult);
        out.close();
      }
      catch(Exception ex)
      {
        ex.printStackTrace();
      }
    }

    Результаты выполнения этого сервлета (на сервере приложений Tomcat) представлены на рис. 10.8.

    (рис 10.8) Генерирование XAML с помощью сервлета

    Отображение Silverlight из JSP

    Чтобы получить JSP-страницу, создается HTML-страница и затем те ее части, в которых должна выполняться логика страницы, дополняются с помощью разметки JSP. В данном случае мы хотим отображать Silverlight-приложение. Обычно это делается путем импорта библиотек сценариев Silverlight.js и createSilverlight.js с последующим вызовом createSilverlight в именованном элементе DIV.

    Однако наша JSP-страница будет немного более функциональной: она сможет принимать параметр и использовать его для построения элемента исходного XAML, созданного ранее. Для этого пишем код на Java и встраиваем его с помощью тегов JSP <% и %>. Полный код JSP-страницы представлен в листинге 10.4.

    <%@page contentType="text/html"%>
    <%@page pageEncoding="UTF-8"%>
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
         "http://www.w3.org/TR/html4/loose.dtd">
    <html>
      <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
          <script type="text/javascript" src="js/silverlight.js"></script>
          <script type="text/javascript"
          src="js/createSilverlight.js"></script>
          <script type="text/javascript">
            function handleLoad(control, userContext, rootElement)
            {
            <%
            String strValue="";
            if (request.getParameter("ID") == null) {
             strValue = "ALFKI";
            } else {
             strValue = request.getParameter("ID");
            }
            %>
            control.source = "http://localhost:8084/ScreenCast2/XamlSource?ID=<% out.print(strValue); %>
              ";
              }
            </script>
          <title>JSP Page</title>
        </head>
      <body>
        <h1>Silverlight JSP Page</h1>
        <div id="slContent">
          <script type="text/javascript">
            createSilverlight();
          </script>
        </div>
      </body>
    </html>

    На рис. 10.9 и 10-10 показано, как эта страница выглядит. Заметьте, что параметр AROUT передается в JSP-страницу в строке запроса первой страницы, тогда как ALFKI передается во вторую страницу.

    (рис 10.9) Выполнение JSP с параметром ID AROUT(рис 10.10) Выполнение JSP с параметром ID ALFKI

    Использование JavaScript для создания приложений, взаимодействующих с сервером

    Silverlight приложение выполняется в браузере, поэтому JavaScript, и в частности AJAX, могут использоваться для создания взаимодействующих с сервером Silvelight-приложений. В данном разделе рассматриваются методы, применяемые для этого, включая получение данных от серверного приложения с помощью AJAX и введение их в собственное приложение Silverlight, а также использование ASP.NET AJAX для связи с Веб-сервисом, приема данных с сервера и формирования их представления в Silver-light.

    Использование AJAX

    Мы собираемся создать простое приложение, формирующее данные, которые будут перехватываться AJAX-вызовом и передаваться в Silverlight для отображения. Для этого сначала необходимо создать серверное приложение, которое при вызове будет возвращать текущее время на сервере.

    Создание серверного приложения для получения текущего времени

    Для начала создадим новое Веб-приложение ASP.NET. Добавим в него новый универсальный обработчик ServerTime.ashx. Отредактируем код этого обработчика так, чтобы он возвращал текущее системное время в виде обычного текста. Вот полный код ASHX:

    using System;
    using System.Collections;
    using System.Data;
    using System.Linq;
    using System.Web;
    using System.Web.Services;
    using System.Web.Services.Protocols;
    using System.Xml.Linq;
    
    namespace AJAXSample
    {
      [WebService(Namespace = "http://tempuri.org/")]
      [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1 1)]
      public class ServerTime : IHttpHandler
      {
        public void ProcessRequest(HttpContext context)
        {
          context.Response.ContentType = "text/plain";
          context.Response.Write(System.DateTime.Now.ToShortTimeString());
        }
        public bool IsReusable
        {
          get
          {
            return false;
          }
        }
      }
    }

    При выполнении этот ASHX передаст текущее системное время в браузер, как показано на рис. 10.11.

    (рис 10.11) Выполнение обработчика ServerTime

    Теперь рассмотрим, как можно получить эти данные и отобразить в Silverlight.

    Создание клиентского приложения для отображения времени

    При создании нового Silverlight-приложения с использованием JavaScript в Microsoft Visual Studio будет создано приложение с простой кнопкой, которая выглядит, как представлено на рис. 10.12. Изменим это приложение так, чтобы при загрузке страницы оно обращалось к серверу для получения времени, используя только что созданный нами обработчик ASHX, и затем выводило это время на кнопке.

    (рис 10.12) Базовое приложение Silverlight

    Сделать это очень просто. Добавьте следующий AJAX-код в блок сценария:

    var xmlHttp;
    function getLatestTime() 
    {
      xmlHttp = new XMLHttpRequest();
      xmlHttp.open("GET","http://localhost:42395/ServerTime.ashx",true);
      xmlHttp.onreadystatechange = handleAJAXCallback;
      xmlHttp.send(null); 
    }

    Если вы не знакомы с AJAX, не волнуйтесь, он достаточно прост. Первая строка создает новый объект XMLHttpRequest, который является основой всего, что делает AJAX. Обратите внимание, что мы стараемся не усложнять код, поэтому для более старых браузеров синтаксис создания объекта xmlHttp будет другим. После этого открывается URI универсального обработчика ServerTime, который был создан нами ранее.

    Подсказка Чтобы этот фрагмент кода работал на вашем компьютере, не забудьте заменить URL в предыдущем фрагменте кода соответствующим URL вашего приложения.

    Далее объявляется функция-обработчик события изменения свойства readyState (Состояние готовности) объекта AJAX. В этом случае формируется событие onreadystatechange (обратите внимание на чувствительность к регистру; событие должно быть записано строчными буквами), и обработчик события располагается в функции handleAJAXCallback (Обработка обратного вызова AJAX), которую вы увидите через мгновение. Наконец, вызывается функция send() объекта xmlHttp для установления сетевого соединения.

    Функция handleAJAXCallback вызывается при каждом изменении свойства readyState объекта xmlHttp. Существует пять различных состояний:

  • 0—Запрос еще не инициализирован.
  • 1—Запрос инициализирован.
  • 2—Запрос отправлен.
  • 3—Запрос обрабатывается.
  • 4—Запрос завершен.
  • Нас интересует состояние, свидетельствующее о завершении запроса, поэтому проводим проверку состояния 4. Вот код:

    function handleAJAXCallback()
    {
     if((xmlHttp.readyState == 4)  (xmlHttp.status ==200))
      {
       var SL = document.getElementById("silverlightPlugIn");
       SL.content.findName("txt").Text = xmlHttp.responseText;
      }
    }

    Этот код проверяет значение свойства readyState и HTTP-код состояния (в свойстве status ). Если они равны 4 и 200, соответственно (т.е. соединение было успешным), приложение находит в XAML элемент txt и задает в качестве его значение текст ответа. Обратите внимание, что в случае использования базового шаблона XAML, TextBlock с надписью на кнопке не имеет имени, поэтому не забудьте присвоить ему имя txt, чтобы гарантировать работоспособность этого кода. Свойство responseText (Текст ответа) объекта xmlHttp содержит данные, возвращенные с сервера. В данном случае, это текущее время на сервере.

    И последнее, необходимо обеспечить вызов сценария при загрузке страницы. Для этого мы задаем сценарий onLoad в теге <BODY> страницы. Приведем пример:

    <body onload = "getLatestTime();">

    Как все это выглядит, можно увидеть на странице рис. 10.13.

    (рис 10.13) Использование AJAX для обновления приложения Silverlight

    Это полезная техника для создания высоко-динамичных сайтов на Silverlight с использованием технологий, ориентированных на стандарты. Также показано, что Silverlight не связан с инфраструктурой сервера Microsoft и может размещаться и обновляться с любого сервера.

    В следующем разделе будут рассмотрены Веб-сервисы ASP.NET и то, как с помощью ASP.NET AJAX организовать взаимодействие между ним и Silverlight.

    Использование ASP.NET AJAX и Веб-сервисов

    При создании Веб-сервиса с использованием ASP.NET можно пометить его как используемый в JavaScript, имея в виду, что этот Веб-сервис может автоматически сгенерировать JavaScript-прокси. Этот прокси использует JavaScript Object Notation (JSON), позволяя без труда передавать по сети сложные типы данных, не обращая внимания на вопросы совместимости. Итак, давайте создадим Веб-сервис и посмотрим, как можно с помощью JavaScript извлекать данные из него и помещать в Silver-light.

    В Visual Studio создаем новое приложение и добавляем в него Веб-сервис ASMX. Будем использовать этот Веб-сервис для формирования текущего времени в двух форматах: коротком и длинном. Для этого создадим класс, содержащий две строки, что обеспечит нам структурированный объект с данными. Вот как он выглядит:

    public class DateData 
    {
      public string strShortTime;
      public string strLongTime; 
    }

    Теперь напишем код класса Веб-сервиса, который будет возвращать текущее время в двух форматах с помощью представленного выше класса. Обратите внимание, что класс обозначен как System.Web.Script.Services.ScriptService. Тем самым мы указываем инфраструктуре ASP.NET пометить его как сервис, который может использоваться из JavaScript:

    using System;
    using System.Linq;
    using System.Web;
    using System.Web.Services;
    using System.Web.Services.Protocols;
    using System.Xml.Linq;
    
    [WebService(Namespace = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1 1)]
    [System.Web.Script.Services.ScriptService]
    
    public class Service : System.Web.Services.WebService
    {
     public Service () 
     {
     }
     [WebMethod]
     public DateData getTime()
     {
      DateData dReturn = new DateData();
      dReturn.strLongTime = System.DateTime.Now.ToLongTimeString();
      dReturn.strShortTime = System.DateTime.Now.ToShortTimeString();
      return dReturn;
     }
    }

    При выполнении этого сервиса добавление /js в конце URI обеспечит формирование JavaScript-прокси. Поэтому для работы с этим Веб-сервисом можно либо использовать HTML-страницу, которая включает этот сценарий, либо ASP.NET-страницу с ScriptManager, ссылающимся на этот сервис. Применим здесь второй метод.

    Создадим в решении ASP.NET-страницу и добавим в нее элемент управления ScriptManager. В Script-Manager необходимо добавить ссылку на только что созданный вами Веб-сервис. Делаем это с помощью тега <Services>. Вот пример:

    <asp:ScriptManager ID="ScriptManager1" runat="server">
      <Services>
        <asp:ServiceReference Path="~/Service.asmx" InlineScript="true" />
      </Services>
    </asp:ScriptManager>

    Далее добавляем на страницу Silverlight, как обычно. Но сначала взгляните на XAML, визуальное представление которого будет формировать Silverlight. В демонстрационных целях я сделал его максимально простым:

    <Canvas xmlns="http://schemas.microsoft.com/client/2007"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
      <TextBlock Canvas.Top="0" x:Name="txtShort"></TextBlock>
      <TextBlock Canvas.Top="20" x:Name="txtLong"></TextBlock>
    </Canvas>

    Добавляем этот код в страницу и создаем экземпляр объекта Silverlight, как обычно. Вот код, который может быть добавлен в раздел <body> разметки ASP.NET:

    <div id="silverlightPlugInHost">
      <script type="text/javascript">
        Silverlight.createObjectEx({
         source: 'Scene.xaml',
         parentElement: document.getElementById('silverlightPlugInHost'),
         id: 'silverlightPlugIn',
         properties: {
          width: '100%',
          height: '100%',
          background:'white',
          version: '1.0'
         },
         events: {
          onLoad: handleLoad
         },
         context: null
        });
      </script>

    В этом коде необходимо обратить внимание на две вещи. Во-первых, источник задан как Scene.xaml, поэтому убедитесь, что при вызове своего XAML-файла используете именно это имя. Во-вторых, событие handleLoad определено как событие, формируемое, когда содержимое Silverlight загружено, и сформировано его визуальное представление.

    Итак, рассмотрим это событие handleLoad:

    function handleLoad() 
    {
     Service.getTime(onSuccess, on Failed,""); 
    }

    Веб-сервис назван Service, и JavaScript-прокси принимает его имя. Таким образом, JavaScript-прокси можно вызывать, используя синтаксис ИмяКласса.ВебМетод.Наш Веб-метод назывался getTime (Получить время), поэтому просто вызываем Service.getTime. JavaScript-прокси для общения с Веб-сервисом использует AJAX, и, поскольку AJAX применяет механизм обратного вызова, должны быть описаны функции обратного вызова в случае успеха и неудачи (или истечения времени ожидания). Итак, в данном случае, задаются функции обратного вызова onSuccess (В случае успеха) и onFailed (В случае неудачи), соответственно. Третий параметр - это параметр контекста, который может быть пустым.

    Вот функции onSuccess и onFailed:

    function onSuccess(result)
    {
     var sl = document.getElementById("silverlightPlugIn");
     sl.content.findName("txtShort").Text = result.strShortTime;
     sl.content.findName("txtLong").Text = result.strLongTime;
    }
    function onFailed(result)
    {
     alert("Timed out");
    }

    Эти функции принимают возвращенное в параметре result (результат) значение. Мы используем JSON, поэтому это структурированный объект, так что значениями полей result.strShortTime и re-sult.strLongTime будут строки, содержащие время в укороченном и удлиненном форматах, соответственно. Нам осталось только найти элементы TextBlock по их именам и задать им соответствующие значения.

    Взаимодействие и данные в .NET

    При использовании .NET в Silverlight 2 взаимодействие с сервисами реализовывается посредством двух основных классов: WebClient и WebRequest (Веб-запрос). Как будет показано далее, эти классы обеспечивают гибкие средства доступа к данным. Кроме того, вы увидите, как в Silverlight можно использовать классы Windows Communication Foundation и ADO.NET Data Services (службы данных ADO.NET).

    Класс WebClient

    Класс WebClient является простейшим классом удаленного взаимодействия для Silverlight. При его использовании задается Uri содержимого, доступ к которому необходимо организовать, и затем для загрузки данных вызывается метод DownloadStringAsync (Загрузить строку асинхронно) или OpenReadAsync (Открыть для чтения асинхронно). Первый используется для получения строковых данных, второй -для получения любых данных в виде потока. Для возвращения результатов выполнения этих методов потребуется функция обратного вызова, DownloadStringCompleted (Загрузка строки завершена) или OpenReadCompleted (Чтение завершено), соответственно.

    Попробуем все это на практике. Создадим приложение Silverlight и, как делали это ранее, добавим универсальный обработчик в Веб-приложение и используем его для возвращения текущего времени на сервере. Для этого просто изменим ProcessRequest (Обработать запрос) следующим образом:

    public void ProcessRequest (HttpContext context) 
    {
      context. Response.ContentType = "text/plain";
      context. Response. Write(System.DateTime. Now.ToLongTimeString()); 
    }

    Теперь в Page.xaml.cs можно использовать класс WebClient для общения с сервером. Для этого добавьте в начало своего кода ссылку на пространство имен System.Net:

    using System.Net;

    Теперь, чтобы использовать класс WebClient, необходимо будет просто применить следующий код. Обратите внимание, что при написании кода этого приложения я использовал Cassini, поэтому работал с портом 65144. Вы можете использовать другой порт при выполнении своего приложения, поэтому не забудьте внести в код соответствующие изменения или настройте Cassini на использование конкретного порта.

    public Page()
    {
     InitializeComponent();
     WebClient cl = new WebClient();
     Uri uri = new Uri("http://localhost:65144/WebClientTest Web/Time.ashx");
     cl.DownloadStringCompleted +=
     new DownloadStringCompletedEventHandler(cl DownloadStringCompleted);
     cl.DownloadStringAsync(uri);
    }

    Для события DownloadStringCompleted объявлена функция-обработчик cl DownloadStringCompleted, поэтому по завершении передачи данных будет вызываться эта функция. Рассмотрим ее:

    void cl  DownloadStringCompleted(object sender,
             DownloadStringCompletedEventArgs e) {
    txtTime.Text = e.Result; }

    Как видите, функция получает объект аргументов (в виде класса DownloadStringCompletedEventArgs ), в котором имеется свойство Result, содержащее возвращенную строку.

    WebClient имеет еще несколько полезных атрибутов: свойство IsBusy (Занят), которое возвращает true, если асинхронная передача данных еще не завершена, и метод CancelAync, который может использоваться для отмены выполняющихся асинхронных транзакций. Если это происходит, свойству Cancelled (Отменен) соответствующего обработчика ( DownloadStringCompleted или OpenReadCompleted ) присваивается значение true.

    Класс WebRequest

    Немного больше возможностей, включая использование Silverlight в качестве клиентской части стандартного шаблона HTTP-форм (с HTTP-POST ), предоставляет класс WebRequest. Он действительно полезен с точки зрения обеспечения возможности взаимодействия, потому что методика HTTP-POST широко используется во всех серверных технологиях, так что клиентскую часть на HTML можно заменить Silverlight-приложением без всяких изменений серверной части кода.

    Итак, например, объединим серверный компонент, принимающий запрос HTTP post к трем текстовым полям, t1, t2 и t3. Начнем с создания Silverlight-приложения и добавим универсальный обработчик в Веб-проект. Отредактируем метод обработчика ProcessRequest так, чтобы он принимал значения формы через HTTP-POST. Вот как реализовать прием трех строк из запроса HTTP-POST:

    public void ProcessRequest (HttpContext context) {
     string str1 = "";
     string str2 = "";
     string str3 = "";
     if (context.Request.HttpMethod == "POST")
     {
      str1 = context.Request.Form["t1"];
      str2 = context.Request.Form["t2"];
      str3 = context.Request.Form["t3"];
     }
     context.Response.ContentType = "text/plain";
     context.Response.Write("OK");
    }

    Обычно в таких случаях используется HTML-форма с тремя элементами управления TextField (t1, t2 и t3 ) и кнопкой Submit (Отправить) для передачи этих данных. То же самое можно сделать в Silverlight. Вот XAML для простой формы ввода данных:

    <UserControl x:Class="WebRequestTest.Page" xmlns=" http://schemas.microsoft.com/client/2007" xmlns:x="http://schemas.microsoft.com/winfx/2006/xam l" Width="400" Height="300">

    <UserControl x:Class="WebRequestTest.Page"
                 xmlns="http://schemas.microsoft.com/client/2007"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 Width="400" Height="300">
      <Grid x:Name="LayoutRoot" Background="White">
        <Grid.ColumnDefinitions>
          <ColumnDefinition Width="0.265*"/>
          <ColumnDefinition Width="0.735*"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
          <RowDefinition Height="0.037*"/>
          <RowDefinition Height="0.1*"/>
          <RowDefinition Height="0.05*"/>
          <RowDefinition Height="0.106*"/>
          <RowDefinition Height="0.054*"/>
          <RowDefinition Height="0.09*"/>
          <RowDefinition Height="0.563*"/>
        </Grid.RowDefinitions>
        <TextBox Margin="0,0,25,0" Grid.Column="1"
                 Grid.Row="1" x:Name="t1"/>
        <TextBox Margin="0,0,25,0" Grid.Column="1"
                 Grid.Row="3" x:Name="t2"/>
        <TextBox Margin="0,0,25,0" Grid.Column="1"
                 Grid.Row="5" x:Name="t3"/>
        <TextBlock Margin="8,0,0,0" Grid.Row="1"
                   Text="Field 1" TextWrapping="Wrap"/>
        <TextBlock Margin="8,0,8,0" Grid.Row="3"
                   Text="Field 2" TextWrapping="Wrap"/>
        <TextBlock Margin="8,0,8,0" Grid.Row="5"
                   Text="Field 3" TextWrapping="Wrap"/>
        <Button Height="36" HorizontalAlignment="Left"
                Margin="0,26.9,0,0" VerticalAlignment="Top"
                Width="77" Grid.Column="1" Grid.Row="6"
                Content="Submit" x:Name="b" Click="b_Click" />
      </Grid>
    </UserControl>

    Как эта форма выглядит, можно увидеть на рис. 10.14.

    (рис 10.14) Форма ввода данных на Silverlight

    Пользователь вводит текст в трех полях и затем нажимает кнопку Submit. При этом формируется событие b_Click. Вот код обработки этого события:

    private void b Click(object sender, RoutedEventArgs e) {
      Uri uri = new Uri( "http://localhost:22602/WebRequestTest Web/SubmitForm.ashx");
      WebRequest rq = WebRequest.Create(uri);
      rq. Method = "POST";
      rq.ContentType = "application/x-www-form-urlencoded";
      rq.BeginGetRequestStream(new AsyncCallback(RequestReady), rq);
      rq.BeginGetResponse(new AsyncCallback(ResponseReady), rq); 
    }

    При использовании класса WebResponse сначала необходимо задать вызываемый URI. Это выполняется с помощью объекта Uri; он указывает на сайт, содержащий обработчик форм. Затем этот URI передается в метод Create (Создать) WebRequest для установления связи. Также задается используемый Method (Метод). Это может быть метод HTTP-Verb, такой как POST или GET. В данном случае, выполняется отправка данных формы, поэтому должен применяться POST.

    Далее необходимо задать соответствующий тип содержимого. Опять же, поскольку используется форма, этим типом будет application/x-www-form-urlencoded. Все эти действия выполняются соответственно Веб-стандартам, поэтому возможные варианты значений можно найти в любом хорошем справочнике по Веб.

    Наконец, должен быть задан поток запроса. Это поток, потому что предполагается запись данных в запрос. Вы должны задать метод асинхронного обратного вызова, который будет выполняться после завершения Request, а также ссылку на сам объект запроса. Кроме того, требуется перехватывать ответ формы, поэтому вызывается BeginGetResponse (Начать прием ответа) для начала настройки потока и задается обратный вызов, который будет выполняться, когда поток ответа готов.

    В методе обратного вызова, который вызывается, когда поток запроса готов, можно выполнить запись в поток, используя объект StreamWriter (объект записи в поток). В данном случае, записывается несколько параметров. Согласно протоколу HTTP они должны быть разделены символами :

    t1=значение1t2=значение2t3=значение3.

    Итак, теперь осталось лишь записать имя параметра и свойство Text полей ввода:

    void RequestReady(IAsyncResult aR){
      WebRequest rq = aR.AsyncState as WebRequest;
      Stream rqStream = rq.EndGetRequestStream(aR);
      StreamWriter w = new StreamWriter(rqStream);
      w.Write("t1=" + t1.Text + "");
      w.Write("t2=" + t2.Text + "");
      w.Write("t3=" + t3.Text );
      w.Flush(); 
    }

    Как было показано в универсальном обработчике событий, после того как сервер получает данные, он записывает OK для обозначения состояния. По завершении записи ответа вызывается метод обратного вызова ResponseReady (Ответ готов).

    void ResponseReady(IAsyncResult aR) {
      WebRequest rq = aR.AsyncState as WebRequest;
      using(WebResponse rs = rq.EndGetResponse(aR))
      using (Stream rpStream = rs.GetResponseStream())
      { StreamReader rdr = new StreamReader(rpStream); 
        string postStatus = rdr.ReadToEnd();
      } 
    }

    Этот код просто извлекает поток ответа из результата асинхронного вызова и читает его до конца. В данном случае, это просто состояние, но так можно извлекать ответы с сервера для каждого поля.

    Как видите, применяя данную методику, довольно просто создавать Silverlight-клиентские приложения заменяющие традиционные HTTP-формы!

    Использование SyndicatedFeed для организации доступа к RSS и ATOM

    Двумя наиболее популярными типами данных, доступными в Интернете, являются каналы RSS и ATOM, обычно используемые в блогах или на новостных сайтах. Silverlight предлагает класс SyndicatedFeed (Информационный канал), с помощью которого можно без труда организовать доступ к этим данным.

    SyndicatedFeed очень прост в использовании. Рассмотрим на примере. Но, прежде чем продолжить, остановимся на мгновение на междоменном доступе в Silverlight. При организации доступа к содержимому, находящемуся на других сайтах (не на том, который обслуживает содержимое Silverlight), .NET-классы ищут на сервере файл Crossdomain.xml. Если они находят его, то проводят синтаксический разбор междоменных правил, чтобы выяснить, имеют ли они право на доступ к содержимому. Если они не находят этот файл, они формируют ошибку. Поэтому помните об этом, прежде чем использовать SyndicatedFeed для доступа к данным, или можете столкнуться с проблемами. В сети можно найти много информации о Crossdomain.xml, потому что он де факто является стандартным способом обеспечения междоменного доступа для разных технологий.

    Для данного примера мы будем просто использовать статический XML-документ Doc.rss, хранящийся на сервере.

    Прежде чем приступать к написанию кода, убедитесь в наличии соответствующих ссылок, которые обеспечивают возможность использовать SyndicatedFeed, потоковую передачу данных и XML

    using System.Net;
    using System.Xml;
    using System.IO;
    using System.ServiceModel.Syndication;

    Чтобы создать эту ссылку, вероятно, придется добавить ссылку на System.ServiceModel.Syndication.

    Следующее, что необходимо сделать до написания кода, - сформировать представление данных в XAML

    <UserControl x:Class="RSSClient.Page"
                 xmlns="http://schemas.microsoft.com/client/2007"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 Width="400" Height="300">
      <Canvas x:Name="LayoutRoot" Width="400"
              Height="300" Background="White">
        <ItemsControl x:Name=" rssTitles">
          <ItemsControl.ItemTemplate>
            <DataTemplate>
              <StackPanel Orientation="Vertical">
                <TextBlock FontWeight="Bold"
                Text="{Binding Title.Text}" />
              </StackPanel>
            </DataTemplate>
          </ItemsControl.ItemTemplate>
        </ItemsControl>
      </Canvas>
    </UserControl>

    Здесь в XAML используется объект ItemsControl (Элементы), который в сочетании с ItemTemplate (Шаблон элемента) обеспечит базовую функцию повторения. Этот объект связывается с данными с помощью DataTemplate (Шаблон данных), определяющего, что элемент управления TextBlock связан с данными RSS-канала по Title.

    Возвращаясь к C#-коду, теперь можно создать объект WebClient для загрузки RSS-документа. После того, как RSS-документ будет загружен, его можно будет прочитать с помощью элемента управления SyndicatedFeed. Вот код для создания WebClient, выполняющего доступ к RSS-документу:

    public Page() {
      InitializeComponent();
      WebClient client = new WebClient();
      Uri uri = new Uri("doc.rss",UriKind.Relative);
      client.OpenReadCompleted + = 
         new OpenReadCompletedEventHandler(client OpenReadCompleted);
      client.OpenReadAsync(uri); 
    }

    Здесь задается функция обратного вызова ( client OpenReadCompleted ), которая вызывается по завершении загрузки. Вот код этой функции:

    void client OpenReadCompleted(object sender,
        OpenReadCompletedEventArgs e) { 
      XmlReader r = XmlReader.Create(e.Result); 
      SyndicationFeed feed = SyndicationFeed.Load(r); 
      rssTitles.ItemsSource = feed.Items; 
    }

    Здесь создается XmlReader (объект чтения Xml), который считывает результаты обратного вызова и передает их в SyndicationFeed. Также SyndicationFeed содержит соответствующие коллекции, включая коллекцию Items (Элементы). Эта коллекция содержит (кроме всего прочего) Title для элемента RSS, таким образом, задавая свойство ItemsSource элемента ItemsControl коллекции rssTitles, мы выполняем привязку данных, и заголовки будут формироваться Silverlight. Результаты представлены на рис. 10.15.

    (рис 10.15) Просмотр данных RSS с использованием SyndicationFeed и привязки данных

    Выходя за рамки таких простых каналов, как RSS и ATOM, Silverlight предоставляет возможность использования таких расширенных сервисов, как ASMX и Веб-сервисы WCF. Это будет рассмотрено в следующем разделе.

    Silverlight и WCF

    Пространство имен System.ServiceModel содержит классы, используемые Silverlight для работы с Веб-сервисами . В данном разделе мы поэтапно рассмотрим процесс создания простого сервиса в WCF и его использование в Silverlight.

    Создадим новое приложение Silverlight и добавим новый класс в Веб-проект. Назовем его Person (Человек), и будет создан класс Person.cs.Он будет использоваться для передачи данных между сервисом и клиентом. Для этого необходимо обозначить этот класс как DataContract (Контракт данных) между WCF и Silverlight и пометить каждый его элемент, с которым должен будет работать Silverlight, атрибутом DataMember (Элемент данных).

    Обратите внимание, что понадобится включить System.Runtime.Serialization. Рассмотрим код:

    [DataContract] 
    public class Person {
      public Person(string strName, string strAge)
      {
        Name = strName; Age = strAge;
      }
      [DataMember]
      public string Name { get; set; }
      [DataMember]
      public string Age { get; set; } 
    }

    Сюда также входит конструктор, используемый для инициализации свойств Name и Age (Возраст).

    Далее необходимо добавить в проект шаблон WCF Service (WCF-сервис). Присвоение ему имени Per-sonService обеспечит создание файлов PersonService.cs и IPersonService.cs. Файл IPersonService.cs будет отвечать за создание интерфейса, определяющего контракт сервиса. В нем необходимо будет определить метод сервиса, возвращающий список людей. Сделать это несложно: просто пометьте интерфейс атрибутом ServiceContract и метод, доступный для вызова, атрибутом OperationContract (Контракт операции).

    [ServiceContract]
    public interface IPersonService
    {
      [OperationContract]
      Person[] getPeople(); 
    }

    Реализация сервиса выполняется в классе PersonService.cs.В нем должны быть реализованы, по крайней мере,методы, описанные в OperationContract. Рассмотрим пример:

    public class PersonService : IPersonService {
      public Person[] getPeople() {
        Person[] people = {
          new Person("Derek Snyder","32"), 
          new Person("Yoshi Latimer","19"), 
          new Person("Mark  Steel","31")
        }; 
        return people; 
      } 
    }

    Здесь создается и возвращается вызывающей стороне массив объектов Person, названный people (люди). Silverlight может выполнить привязку к такой коллекции элементов. Прежде, чем двигаться дальше, необходимо убедиться, что WCF правильно сконфигурирован для выполнения базового HTTP-связывания1, которое будет использоваться клиентом Silverlight. Это выполняется в файле Web.config. Найдите следующий код в узле <serviceModel> и убедитесь, что связывание задано как basicHttpBinding (Базовое HTTP-связывание):

    <service behaviorConfiguration="PersonServiceBehavior"
             name="PersonService">
      <endpoint address=""
                binding="basicHttpBinding"
                contract="IPersonService">
        <identity>
          <dns value="localhost"/>
        </identity>
      </endpoint>
      <endpoint address="mex" 
                binding="mexHttpBinding" 
        contract="IMetadataExchange"/>
    </service>

    Протестируйте работоспособность сервиса, щелкнув правой кнопкой мыши файл PersonService.svc и выбрав в контекстном меню View In Browser (Просмотр в браузере). Если все хорошо, на экране вы увидите примерно следующее (рис. 10.16):

    (рис 10.16) Выполнение WCF сервиса

    Если сервис работает правильно, можно добавлять ссылку на него в проект Silverlight. Опять же, все просто: щелкаем правой кнопкой мыши узел References в Solution Explorer и выбираем в контекстном меню Add Service Reference (Добавить ссылку на сервис). Появится диалоговое окно Add Service Reference, как показано на рис. 10.17. Введите адрес сервиса и щелкните кнопку Go (Перейти). Это обеспечит создание прокси-классов для сервиса.

    (рис 10.17) Добавление ссылки на сервис

    Прокси будет создан в пространстве имен, заданном в диалоговом окне. В данном случае, используется имя PSProxy, который через мгновение вы увидите в коде.

    Прежде всего, необходим XAML для определения уровня представления. Это XAML с привязкой к данным, который связывается с результатами вызова сервиса:

    <ItemsControl x:Name=" itemTitles">
      <ItemsControl.ItemTemplate>
        <DataTemplate>
          <StackPanel Orientation="Vertical">
            <StackPanel Orientation="Horizontal">
              <TextBlock FontWeight="Bold" Text="{Binding Name}" />
              <TextBlock Text=" : "></TextBlock>
              <TextBlock FontWeight="Bold" Text="{Binding Age}" />
            </StackPanel>
          </StackPanel>
        </DataTemplate>
      </ItemsControl.ItemTemplate>
    </ItemsControl>

    Как видите, здесь имеется два элемента TextBlock, которые связаны со значениями Name и Age. Рассмотрим вызов сервиса и то, как он используется для передачи данных в это содержимое Silverlight:

    Binding binding = new BasicHttpBinding();
    EndpointAddress endPoint = new EndpointAddress
      ("http://localhost:25101/PersonService Web/PersonService.svc");
    
    PSProxy.PersonServiceClient client = new
      PSProxy.PersonServiceClient(binding, endPoint);
    
    client.getPeopleCompleted += new
      EventHandler<PersonService.PSProxy.getPeopleCompletedEventArgs>
      (client getPeopleCompleted);
    
    client.getPeopleAsync();

    Здесь создается новый класс BasicHttpBinding под именем binding (что соответствует тому, что вы задавали в Web.config для этого сервиса) и EndpointAddress (Адрес конечной точки), указывающий на сам сервис. Затем создается новый экземпляр клиента сервиса (который был сформирован для вас автоматически при создании ссылки на сервис) и описывается функция обратного вызова. Наконец, выполняется вызов функции getPeopleAsync, которая обеспечивает асинхронный вызов метода сервиса. По завершении выполнения метода сервиса, запускается функция обратного вызова.

    Функция обратного вызова получает аргументы, содержащие массив объектов Person, формируемый методом сервиса. Это коллекция, которую можно связать с ItemsControl, представленным ранее. Посмотрим на код:

    void client getPeopleCompleted(object sender,
      PersonService.PSProxy.getPeopleCompletedEventArgs e) 
    {
     itemTitles.ItemsSource = e.Result; 
    }

    Результаты этого представлены на рис. 10.18.

    (рис 10.18) WCF клиент на Silverlight

    Все эти примеры очень просты, но я надеюсь, что они иллюстрируют различные доступные сценарии создания взаимодействующих с сервером приложений и обеспечивают базовые сведения, которые пригодятся при написании реального кода для Silverlight-приложений!

    Заключение

    В данной лекции представлены различные стратегии создания взаимодействующих с сервером приложений на Silverlight. Вы увидели, какXAML может использоваться в качестве инструмента взаимодействия из приложений на базе PHP и Java и как Silverlight может использоваться для связи с этими приложениями. Затем мы рассмотрели AJAX и познакомились с тем, как использовать AJAX и JavaScript для получения данных от серверных приложений, и как открытая природа дерева визуального отображения Silverlight позволяет передавать эти данные в элемент управления Silverlight для дальнейшего отображения. Наконец, вы познакомились с некоторыми новыми классами, предлагаемыми Silver-light 2 для работы с сетью и связи. Рассмотрены классы WebClient и WebRequest. Также вы научились создавать приложения для использования WCF-сервисов через прокси.

    В следующей лекции будут представлены некоторые ориентированные на потребителя технологии Silverlight 2, включая элементы управления для мультимедийного содержимого, элемент управления, обеспечивающий возможность использования ваших приложений на компьютерах с перьевым вводом, и новый элемент управления, обеспечивающий в Silverlight-приложениях функциональность Deep Zoom.

    Страницы:

    В данной лекции вы узнаете, как можно создавать взаимодействующие с сервером приложения, используя Silverlight. Лекция разделена на три основных раздела. В первом мы обратимся к архитектуре Silverlight, и вы увидите, как дизайн в ней отделен от реализации, что позволяет создавать сервисы на Personal Hypertext Processor (PHP) или Java, генерирующие XAML, который формирует визуальное представление вашего Silverlight-приложения. Во втором разделе рассматривается сам браузер и его использование для подключения к удаленным сервисам с помощью технологии AJAX. В последней части представлены некоторые классы, доступные в .NET, включая WebClient и WebRequest, и показано их использование для получения данных с сервера и отображения в приложении Silverlight.

    Подключение приложений с помощью XAML

    Одним из свойств Silverlight, делающим его таким мощным инструментом, является то, что XAML - это XML и, таким образом, он может быть сформирован сервером. В примерах, которые были представлены до сих пор, XAML статичен: документ создается дизайнером в каком-нибудь инструменте, таком как Expression Blend. Однако вы не ограничены лишь статическим XAML и можете создавать целый ряд разнообразнейших сценариев, динамически формируя XAML и передавая его пользователям в ответ на запрос или переход приложения в определенное состояние.

    Например, требуется создать приложение прогноза погоды. При использовании статического XAML для этого пришлось бы создать сложную систему, которая периодически формировала бы различные XAML-документы для каждого из местоположений пользователей, а пользователи запрашивали бы и загружали соответствующие документы. Намного проще создать шаблон приложения и заполнять поля подстановки в UI приложения, используя сведения, полученные с сервера.

    Серверные приложения могут создаваться на разных языках программирования. В этом разделе обсуждаются PHP и Java. Будет рассмотрено, как XAML генерируется в каждом из этих языков, сначала в общем, а затем на конкретном примере с использованием сведений прогноза погоды.

    Silverlight и PHP

    PHP (можно скачать на сайте http://www.php.net) - простой, но при этом мощный язык написания сценариев, который может использоваться для создания XAML шаблона. Он обычно принимает PHP-код в качестве входных данных и создает Веб-страницы на выходе. Также динамически можно формировать XAML Если вы не знакомы с PHP, далее представлены базовые приемы работы с ним.

    Ваша первая PHP-страница

    PHP может использоваться для динамического формирования Веб-страниц на основании входных параметров. Так, например, рассмотрим страницу, созданную с использованием этого HTML-кода:

    <HTML>
      <HEAD>
      </HEAD>
      <BODY>
        <H1>Hello, World!</H1>
      </BODY>
    </HTML>

    Это представляется как простая HTML-страница с фразой "Hello, World!", которую можно увидеть на рис. 10.1.

    (рис 10.1) Простая Веб страница "Hello World!"

    С помощью PHP эту простую страницу можно сформировать динамически. В данном случае, страница принимает параметр, и препроцессор PHP подставляет его. Ниже представлен PHP-код:

    <HTML>
      <HEAD>
      </HEAD>
      <BODY>
        <H1>Hello, 
          <?php echo($   REQUEST['name']);?>!
        </H1>
      <BODY>
    </HTML>

    Здесь показан код разметки HTML со встроенным PHP-кодом, извлекающим параметр URL name и записывающий его в выходной поток с помощью команды echo. Итак, если вы перейдете на эту страницу, передавая ей значение name, процессор PHP динамически создаст разметку страницы. Например, на рис. 10.2 показано, как будет представлен на экране следующий параметр URL:

    http://localhost/phptext/Hello.php?name=Laurence

    (рис 10.2) Динамическое формирование страниц на PHP

    Учебный пример PHP и XAML

    Тот же принцип может применяться и к XAML Не составит большого труда создать в Expression Blend XAML-шаблон представления содержимого и использовать его на PHP-странице. Если необходимо представить повторяющееся содержимое, такое как список, для отображения можно использовать PHP-цикл.

    Лучше всего продемонстрировать это на примере. PHP (наряду с Apache, MySQL и Linux) является основным элементом знаменитого набора LAMP, поэтому давайте рассмотрим, как создать приложение PHP/MySQL, которое могло бы выполняться на любом Веб-сервере или операционной системе, поддерживающей LAMP. (Аббревиатура расшифровывается как L inux, A pache, M ySQL и P HP.) Фактически мы будем создавать приложение "Windows IIS MySQL PHP", сокращенно WIMP, что означает "бесхарактерный человек, тряпка" - совершенно никудышнее название.

    Создание базы данных на стороне сервера

    В данном случае, MySQL используется для создания базы данных имен и адресов на стороне сервера. MySQL можно скачать по адресу http://www.mysql.com. Там можно найти бесплатную версию продукта, идеально подходящую для экспериментов.

    Кроме этого, предлагается набор инструментов GUI, таких как MySQL Administrator и MySQL Query Browser, для построения и заполнения баз данных. Инструкции по применению этих инструментальных средств выходят за рамки рассмотрения данного курса, однако исчерпывающую документацию можно найти на Веб-сайте .

    На рис. 10.3 показан MySQL Table Editor (Редактор таблиц MySQL) и его использование для описания простой таблицы имен и адресов.

    (рис 10.3) Описание таблицы имен и адресов в MySQL

    Редактировать содержимое таблицы можно в MySQL Query Browser (Обозреватель запросов MySQL). Просто щелкните кнопку Edit (Редактировать) внизу UI приложения (рис. 10.4) и можно вводить имя и адрес, как показано на рис. 10.4.

    (рис 10.4) Добавление данных в таблицу с помощью MySQL Query Browser

    Теперь у нас есть данные, которые можно отображать с помощью PHP. Пора связать их с XAML-шаблоном. В следующем разделе мы научимся создавать шаблон в Expression Blend.

    Создание XAML-шаблона

    Теперь, когда у нас есть данные, подумаем о том, как представить их, используя XAML Expression Blend - замечательный визуальный редактор для XAML На рис. 10.5 приведен пример XAML-шаблона, который может использоваться для вывода на экран данных адреса и имени из таблицы.

    (рис 10.5) Создание XAML шаблона в Expression Blend

    Полученный в результате XAML представляет коллекцию объектов Canvas, Rectangle и TextBlock. Полный код XAML-шаблона, представленного на рис. 10.5, приводится в Листинге 10.1. Обратите внимание, что корневым узлом этого XAML является Canvas, а все элементы UI располагаются в другом Canvas, который и будет повторяться при отображении множества записей.

    <Canvas xmlns="http://schemas.microsoft.com/client/2007"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/"
            xamlWidth="640" Height="480" Background="#FF460608">
      <Canvas Width = "352" Height="128" Canvas.Left="10" Canvas.Top="8">
      <Rectangle Fill="#FF693B3D" Stroke="#FF000000" StrokeThickness="0"
                 RadiusX="16" RadiusY="16" Width="352" Height="128" Canvas.Top="1"/>
      <Rectangle Stroke="#FF000000" StrokeThickness="0" RadiusX="8" RadiusY="8" 
                 Width = "336" Height="40" Canvas.Left="8" Canvas.Top="8">
        <Rectangle.Fill >
          <LinearGradientBrush EndPoint="0.518,0.175" StartPoint="0.515,0.825" >
            <GradientStop Color="#FF460608" Offset="0"/>
            <GradientStop Color="#FF841316" Offset="1"/>
          </LinearGradientBrush>
        </Rectangle.Fill>
      </Rectangle>
      <TextBlock x:Name="txtName" Width="312" Height="24" Canvas.Left="16" Canvas.Top="16" 
                 FontFamily="Arial Unicode MS" FontSize="18" FontWeight="Normal" Foreground="#FFFFFFFF" 
                 Text="User Name" TextWrapping ="Wrap"/>
      <Rectangle Stroke="#FF000000" StrokeThickness="0" RadiusX="8" RadiusY="8" 
                 Width="336" Height="23" Canvas.Left="8" Canvas.Top="48">
        <Rectangle.Fill >
          <LinearGradientBrush EndPoint="0.518,0.175" StartPoint="0.515,0.825">
            <GradientStop Color="#FF460608" Offset="0"/>
            <GradientStop Color="#FF841316" Offset="1"/>
          </LinearGradientBrush>
        </Rectangle.Fill>
      </Rectangle>
      <TextBlock x:Name="txtAddr1" Width = "312" Height="24" FontFamily="Arial Unicode MS" FontSize="12" 
                 FontWeight="Normal" Foreground="#FFFFFFFF" Text="Address1" TextWrapping = "Wrap" 
                 Canvas.Left="14" Canvas.Top="48"/>
      <Rectangle Stroke="#FF000000" StrokeThickness="0" RadiusX="8" RadiusY="8" Width="336" Height="23" 
                 Canvas.Left="8" Canvas.Top="71">
        <Rectangle.Fill >
          <LinearGradientBrush EndPoint="0.518,0.175" StartPoint="0.515,0.825">
            <GradientStop Color="#FF460608" Offset="0"/>
            <GradientStop Color="#FF841316" Offset="1"/>
          </LinearGradientBrush>
        </Rectangle.Fill>
      </Rectangle>
      <TextBlock x:Name="txtAddr2" Width = "312" Height="24" FontFamily="Arial Unicode MS" 
                 FontSize="12" FontWeight="Normal" Foreground="#FFFFFFFF" Text="Address2" TextWrapping ="Wrap" 
                 Canvas.Left="15" Canvas.Top="72"/>
      <Rectangle Stroke="#FF000000" StrokeThickness="0" RadiusX="8" RadiusY="8" Width="168" Height="23" 
                 Canvas.Left="176" Canvas.Top="94">
        <Rectangle.Fill >
          <LinearGradientBrush EndPoint="0.518,0.175" StartPoint= "0.515,0.825" >
            <GradientStop Color="#FF460608" Offset="0"/>
            <GradientStop Color="#FF841316" Offset="1"/>
          </LinearGradientBrush>
        </Rectangle.Fill>
      </Rectangle>
      <Rectangle Stroke="#FF000000" StrokeThickness="0" RadiusX="8" RadiusY="8" Width="168" Height="23" 
                 Canvas.Left="8" Canvas.Top="94">
        <Rectangle.Fill >
          <LinearGradientBrush EndPoint="0.518,0.175" StartPoint="0.515,0.825">
            <GradientStop Color="#FF460608" Offset="0"/>
            <GradientStop Color="#FF841316" Offset="1"/>
          </LinearGradientBrush>
        </Rectangle.Fill>
      </Rectangle>
      <TextBlock x:Name="txtCity" Width="144" Height="24" FontFamily="Arial Unicode MS" FontSize="12" 
                 FontWeight="Normal" Foreground="#FFFFFFFF" Text="City" TextWrapping = "Wrap" 
                 Canvas.Left="15" Canvas.Top="96"/>
      <TextBlock x:Name="txtState" Width = "144" Height="24" FontFamily="Arial Unicode MS" FontSize="12" 
                 FontWeight="Normal" Foreground="#FFFFFFFF" Text="State" TextWrapping = "Wrap" 
                 Canvas.Left="182" Canvas.Top="96"/>
    </Canvas>
    </Canvas>

    Свойству Canvas.Top второго Canvas (далее называемого контейнером) задано значение 8 пикселов и свойству Height задано значение 128 пикселов. Следовательно, если под первым экземпляром этого контейнера необходимо разместить другие экземпляры, для второго контейнера Canvas.Top должен быть определен как 136 (128 + 8), для следующего - 264 (128 + 128 + 8) и т.д.

    Все элементы UI размещаются в контейнере и все они должны иметь в XAML уникальные имена. Как видите, в этом примере XAML имя каждого TextBlock определятся атрибутом x:Name. При клонировании контейнера XAML должен обеспечивать уникальные имена для этих узлов. Алгоритм присваивания имен будет прост- в конце каждого имени будет добавляться индекс элемента. Итак, например, записанное в шаблоне значение x:Name txtState для первой строки базы данных превратиться в txtState1, для второй строки - txtState2, и т.д.

    Как будет показано в следующем разделе, размножить эти элементы в PHP очень просто.

    Формирование XAML из PHP и MySQL

    Синтаксический разбор страницы выполняет PHP. Это означает, что когда сервер получает запрос на страницу, PHP записывает форматированные данные в буфер ответов. Когда PHP встречает специальный открывающий тег ( <?php ), он передает обработку синтаксическому анализатору PHP. Анализатор выполняет код, выявляемый процессором PHP, пока не встретит закрывающий тег ( ?> ). Это делает связку MySQL и PHP идеальным сочетанием для отображения содержимого, формируемого по шаблону. Шаблон для формирования содержимого просто помещается в файл с расширением .php, а там, где требуется вставить некоторые связанные с данными поля подстановки, добавляется необходимая PHP-логика, заключенная в открывающий и закрывающий теги ( <?php и ?>, соответственно).

    PHP-код может содержать циклы. Если после выражения цикла следует некоторое содержимое, оно будет выведено многократно. В теле цикла могут присутствовать открывающий/закрывающий теги РНР. Например, можно сделать следующее:

    <?php
     for($i =0; $i<100; $i ++)
      { // Открывающая скобка цикла
        // Выходим из PHP, хотя цикл еще не закрыт!
    ?>
      <H1>My Text Line is <?php echo $i; ?></H1>
    <?php
      } // Теперь закрываем наш цикл
     ?>

    Это обеспечит повторение HTML-содержимого (текста, заключенного в элемент <H1> ) 100 раз. Как видите, не обязательно заключать весь текст в теги <?php ... ?>. Это полезное свойство при объединении PHP и XAML

    В XAML холст-контейнер может быть помещен в цикл:

    <?php
     for($i =0; $i<100; $i ++)
      { // Открывающая скобка цикла
        // Выходим из PHP, хотя цикл еще не закрыт!
    ?>
    <Canvas>
    ...
    </Canvas>
    <?php
     } // Теперь закрываем наш цикл
    ?>

    Выполнение этого кода обеспечит запись XAML 100 раз! Это делает PHP мощным языком описания шаблонов для Silverlight.

    Возвращаясь к нашему примеру, рассмотрим, как установить подключение к базе данных, выполнить запрос к ней и записать то количество XAML-элементов Canvas, которое соответствует числу возвращенных результатов. Также отредактируем содержимое элементов TextBlock холста-контейнера, чтобы они отображали информацию, полученную из базы данных, а также обеспечим уникальность имен TextBlock.

    В итоге должна получиться PHP-страница, формирующая XAML, который будет использоваться как источник для элемента управления Silverlight. Но чтобы было немного интересней, изменим PHP и введем параметр state (штат), который обеспечит возвращение сведений только для людей, проживающих в заданном штате. Это превратит наш код в настоящий динамический генератор XAML

    Во-первых, необходимо, чтобы Silverlight распознавал вывод PHP какXML. Для этого задаем тип MIME содержимого вывода. Это делается в PHP с помощью команды header (заголовок):

    header('Content-type: text/xml');

    Извлечение параметра строки запроса (т.е. http://server/script.php?param =value) в PHP обеспечивается массивом $ REQUEST. Итак, чтобы прочитать значение параметра State, используем следующий код:

    $State=$  REQUEST['State'];

    В PHP версии 4.x имеется встроенный набор команд MySQL Для версий 5.x и выше набор команд MySQL должен задаваться как расширение. Независимо от используемой версии, синтаксис PHP аналогичен.

    Подключение к серверу localhost с использованием имени пользователя user и пароля password выполняется с помощью команды mysql connect:

    $con = mysql  connect("localhost", "user", "password");

    На сервере может располагаться несколько баз данных. Для выбора необходимой базы данных используется команда mysql select db:

    mysql  select db("test", $con); // "test"     имя базы данных

    Затем, чтобы выполнить запрос к этой базе данных, используется команда mysql query, в которую передается строка, содержащая фактический запрос SQL Результат запроса будет представлять собой массив массивов значений. Приведем пример:

    $sqlString = "SELECT * from addresses"; 
    $result = mysql  query($sqlString);

    Затем массив mysql fetch array разбивается на строки. С помощью цикла while можно обойти каждую строку, как показано в следующем примере:

    while($row = mysql fetch  array($result))
    {
    ...
    }

    таким образом, исходя из данного сценария, получаем такой алгоритм действий:

    Задаем тип MIME text/xml. Принимаем входной параметр (штат). Записываем начальный тег "корневого" Canvas. Используем входной параметр для построения запроса. Выполняем запрос и получаем результирующее множество. Для каждой строки результирующего множества:

  • a. Создаем XAML-шаблона.
  • b. Заполняем атрибуты Text элементов TextBlock данными из соответствующих полей базы данных.
  • c. Заполняем атрибуты x:Name уникальными ID, полученными на основании порядкового номера
  • строки.

    Закрываем тег "корневого" Canvas.

    Код PHP-страницы полностью приведен в листинге 10.2. Разметка PHP в блоке XAML выделена жирным шрифтом.

    <?php
    header('Content-type: text/xml');
    $State=$   REQUEST['State'];
    ?>
    <Canvas xmlns=" http://schemas.microsoft.com/client/2007"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xam l"
            Width="640" Height="480" Background = "#FF460608">
      <?php
      $height=128; $top=8; $i=0;
      $con = mysql  connect("localhost", "root", "root"); 
      mysql  select db("test",$con); 
      $sqlString = "SELECT * from addresses"; 
      if($State! = "") {
        $sqlString = $sqlString . " WHERE State = '" . $State . "'"; }
      $result = mysql  query($sqlString); while($row = mysql fetch  array($result)) { ?>
      <Canvas Width="352" Height="128" Canvas.Left="10" Canvas.Top =" <"?php echo($top="" + ($height*$i="")); ?>" >
        <Rectangle Fill="#FF693B3D" Stroke="#FF000000" StrokeThickness="0" RadiusX="16" RadiusY="16" 
                   Width = "352" Height="128" Canvas.Top="1"/> 
        <Rectangle Stroke="#FF000000" StrokeThickness="0" RadiusX="8" RadiusY="8" 
                   Width="336" Height="40" Canvas.Left="8" Canvas.Top="8">
          <Rectangle.Fill>
            <LinearGradientBrush EndPoint="0.518,0.175" StartPoint="0.515,0.825">
              <GradientStop Color="#FF460608" Offset="0"/>
              <GradientStop Color="#FF841316" Offset="1"/>
            </LinearGradientBrush>
          </Rectangle.Fill>
        </Rectangle>
        <TextBlock x:Name="txtName"<?php echo="" $i=""; ?>" 
                   Width="312" Height="24" Canvas.Left="16" Canvas.Top="16" FontFamily="Arial Unicode MS" 
                   FontSize="18" FontWeight="Normal" Foreground="#FFFFFFFF" Text="<?php echo($row['Name']); ?>" 
                   TextWrapping ="Wrap"/> 
        <Rectangle Stroke="#FF000000" StrokeThickness="0" RadiusX="8" RadiusY="8" Width="336" Height="23" 
                   Canvas.Left="8" Canvas.Top="48">
          <Rectangle.Fill>
            <LinearGradientBrush EndPoint="0.518,0.175" StartPoint="0.515,0.825">
              <GradientStop Color="#FF460608" Offset="0"/>
              <GradientStop Color="#FF841316" Offset="1"/>
            </LinearGradientBrush>
          </Rectangle.Fill>
        </Rectangle> 
        <TextBlock x:Name="txtAddr1"
                   <?php echo="" $i=""; ?>" 
                   Width="312" Height="24" FontFamily="Arial Unicode MS" FontSize="12" 
                   FontWeight="Normal" Foreg round="#FFFFFFFF"
                   Text="<?php echo($row['Address1']); ?>" TextWrapping ="Wrap" Canvas.Left="14" Canvas.Top="48"/> 
        <Rectangle Stroke="#FF000000" StrokeThickness="0" RadiusX="8" RadiusY="8" 
                   Width="336" Height="23" Canvas.Left="8" Canvas.Top="71">
          <Rectangle.Fill>
            <LinearGradientBrush EndPoint="0.518,0.175" StartPoint="0.515,0.825">
              <GradientStop Color="#FF460608" Offset="0"/>
              <GradientStop Color="#FF841316" Offset="1"/>
            </LinearGradientBrush>
          </Rectangle.Fill>
        </Rectangle>
        <TextBlock x:Name="txtAddr2"
                   <?php echo="" $i=""; ?>" Width = "312" Height="24" FontFamily="Arial Unicode MS" 
                   FontSize="12" FontWeight="Normal" Foreground="#FFFFFFFF"
                   Text="<?php echo($row['Address2']); ?>" TextWrapping="Wrap" 
                   Canvas.Left="15" Canvas.Top="72"/> 
        <Rectangle Stroke="#FF000000" StrokeThickness="0" RadiusX="8" RadiusY="8" 
                   Width="168" Height="23" Canvas.Left="176" Canvas.Top="94">
          <Rectangle.Fill>
            <LinearGradientBrush EndPoint="0.518,0.175" StartPoint="0.515,0.825">
              <GradientStop Color="#FF460608" Offset="0"/>
              <GradientStop Color="#FF841316" Offset="1"/>
            </LinearGradientBrush>
          </Rectangle.Fill>
        </Rectangle>
        <Rectangle Stroke="#FF000000" StrokeThickness="0" RadiusX="8" RadiusY="8" 
                   Width="168" Height="23" Canvas.Left="8" Canvas.Top="94">
           <Rectangle.Fill>
             <LinearGradientBrush EndPoint="0.518,0.175" StartPoint="0.515,0.825">
               <GradientStop Color="#FF460608" Offset="0"/>
               <GradientStop Color="#FF841316" Offset="1"/>
             </LinearGradientBrush>
          </Rectangle.Fill>
        </Rectangle>
        <TextBlock x:Name="txtCity"
                    <?php echo="" $i=""; ?>" Width = "144" Height="24" FontFamily="Arial Unicode MS" 
                    FontSize="12" FontWeight="Normal" Foreground="#FFFFFFFF" 
                    Text="<?php echo($row['City']); ?>" TextWrapping= "Wrap" 
                    Canvas.Left="15" Canvas.Top="96"/> 
        <TextBlock x:Name="txtState"
                   <?php echo="" $i=""; ?>" Width="144" Height="24" FontFamily="Arial Unicode MS" 
                   FontSize="12" FontWeight="Normal" Foreground="#FFFFFFFF" 
                   Text="<?php echo($row['State']); ?>" TextWrapping = "Wrap" 
                   Canvas.Left="182" Canvas.Top="96"/>
      </Canvas>
      <?php $i++; }?>
    </Canvas>

    Создание PHP-страницы для отображения Silverlight

    Как было показано в предыдущем разделе, сервер выводит содержимое напрямую, пока не встречает открывающий тег ( <?php ). Встретив этот тег, сервер обращается к интерпретатору PHP, который выполняет код, заключенный в открывающий и закрывающий теги ( ?> ).

    PHP-код обычно используется для создания динамического HTML-кода. Поскольку Silverlight отображается с помощью HTML, HTML-страница просто редактируется: в нее добавляются теги PHP, и меняется имя файла с добавлением расширения .php. Итак, чтобы реализовать отображение Silverlight, используя PHP, необходимо, чтобы страница, включающая Silverlight.js и createSilverlight.js, вызывала createSilverlight для создания экземпляра элемента управления Silverlight.

    Мы хотим, чтобы пользователь мог выбирать имена и адреса в базе данных по штату. Таким образом, эта страница должна принимать параметр и применять его при создании содержимого Silverlight с использованием XAML-источника, сформированного PHP, который был представлен в предыдущем разделе. К счастью, это очень просто сделать в PHP. Чтобы вывести на экран, сформированный XAML, необходимо всего лишь присвоить в качестве значения свойства content элемента управления Silverlight Uniform Resource Indicator (URI) вашего приложения. Silverlight будет подключаться к серверу по этому URI, принимать результирующий XAML и формировать его визуальное представление.

    Далее представлен полный код примера PHP-страницы, формирующей HTML:

    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
      <title>SilverlightJSApplication2</title>
      <script type="text/javascript" src="Silverlight.js">
      </script>
      <script type="text/javascript" src="createSilverlight.js">
      </script>
      <script type="text/javascript">
        function handleLoad(control, userContext, rootElement)
        {
        <?php 
         if(!isset($_REQUEST['State']))
           $State=""; 
         else $State=$_REQUEST['State']; 
        ?>
        control.source = "http://localhost/phptest/xaml.php?State=
          <?php echo($State) ?>"; 
        } 
      </script> 
    </head> 
    <body> 
      <div id="SilverlightControlHost"> 
        <script type="text/javascript">
          createSilverlight(); 
        </script> 
      </div> 
    </body> 
    </html>

    На рис. 10.6 и 10-7 показано, как эта PHP-страница отображается в браузере с использованием Silver-light для адресов в Нью-Йорке (NY в базе данных) и Вашингтоне (WA).

    (рис 10.6) Содержимое, сформированное приложением PHP для адресов в Нью Йорке(рис 10.7) Содержимое, сформированное приложением PHP для адресов в Вашингтоне

    Silverlight и Java

    Язык программирования Java компилируется в байт-код, который либо интерпретируется, либо компилируется методом Just-In-Time (JIT) и вызывается во время выполнения на виртуальной машине. Это делает его идеально подходящим для серверных сред, особенно когда в центре обработки данных представлены различные операционные системы. Java поддерживается практически всеми основными операционными системами.

    Существует много способов создания серверных приложений на Java и ее версии для предприятий, называемой обычно J2EE или просто JEE. Один из методов - Серверные страницы Java (Java Server Pages, JSP), концепция которых аналогична PHP, в которых для разметки необходимого вывода используется код, выполняющийся во время выполнения. Итак, для создания таких страниц используется стандартная разметка HTML, но в нее помещаются теги JSP, которые будут выполняться при отображении страницы. Другой способ - использование Сервлета (Servlet) Java, который является приложением, выполняющимся на сервере и принимающим параметры через HTTP.

    Открытость Silverlight позволяет создавать приложения, объединяющие в себе Silverlight и Java. Для построения страниц, включающих содержимое Silverlight, может использоваться технология JSP и серверные технологии, такие как сервлеты, идеально подходят для формирования XAML, отображаемого браузером.

    Чтобы продемонстрировать создание приложений Silverlight в среде Java, поработаем с обеими технологиями. Сначала рассмотрим, как построить JSP-страницу для размещения элемента управления Silverlight. Затем создадим сервлет для генерирования XAML, визуальное представление которого будет формировать элемент управления Silverlight.

    Создание сервлета для генерирования XAML

    Сервлет - это приложение, расширяющее класс HttpServlet из пространства имен javax.servlet. Он предоставляет две функции, doGet и doPost, используемые для перехвата команд HTTP-GET v\ HTTP-POST, соответственно. Каждая из этих функций принимает объекты HTTPRequest (HTTP-запрос) и HTTPResponse (HTTP-ответ). Первый используется для получения сведений запроса, второй управляет ответом. Как правило, параметры считываются из объекта HTTPRequest, выходные данные записываются в объект HTTPResponse. Далее представлен пример этих функций:

    protected void doGet(HttpServletRequest request, HttpServletResponse response) 
     throws ServletException, IOException {
      // Обрабатываем GET 
     }
    protected void doPost(HttpServletRequest request, HttpServletResponse response) 
      throws ServletException, IOException {
      // Обрабатываем POST 
    }

    В типовом шаблоне проектирования создается вспомогательная функция, принимающая объекты запроса и ответа, и затем методы doGet и doPost вызывают эту функцию. С этой точки зрения, код будет выполняться один и тот же, независимо от метода вызова сервлета.

    В данном примере сервлет принимает XAML-документ как шаблон и использует прикладные программные интерфейсы (API) XML в Java для обработки документа, заполняя его результатами запроса к базе данных.

    Далее представлен простой XAML-документ, используемый в этом учебном примере:

    <Canvas xmlns=" http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Width="200" Height="80" Background="#FF000000">
      <TextBlock x:Name="txtName" Width = "200" Height="24" Canvas.Left="24" 
           Canvas.Top="8" Foreground="#FFEFB9B9" Text="Name" TextWrapping="Wrap"/>
      <TextBlock x:Name="txtCity" Width = "200" Height="24" Canvas.Left="24" 
           Canvas.Top="40" Foreground="#FFEFB9B9" Text="City" TextWrapping="Wrap"/>
      <TextBlock x:Name="txtCountry" Width="200" Height="24" Canvas.Left="24" 
           Canvas.Top="72" Foreground="#FFEFB9B9" Text="Country" TextWrapping="Wrap"/>
    </Canvas>

    Данный XAML включает три элемента TextBlock. Сервлет выполняет запрос к таблице Customers (Покупатели) базы данных Northwind и заполняет элементы TextBlock результатами запроса. Запрос принимает в качестве параметра ID покупателя и использует его для извлечения соответствующей записи покупателя, в которой нас интересуют имя покупателя, город проживания и страна.

    Если у вас еще нет используемой в этом примере базы данных Northwind, ее можно скачать на сайте Microsoft Developer Network (MSDN). База данных Northwind Traders является примером базы данных, которая предоставляется Microsoft свободно для SQL Server. Она содержит базу сведений о фиктивном Интернет-магазине и является полезным источником тестовых данных для таких примеров, какой рассматривается здесь.

    Чтобы заполнить элементы TextBlock, необходимо найти их в XAML-документе. При использовании в Java XML API для поиска узлов по XPath понадобится задать префикс стандартного пространства имен для элементов. Так позже вы заметите, что ссылки на элементы стандартного пространства имен ( <TextBlock>, например) формируются с использованием добавляемого во время выполнения префикса стандартного пространства имен (т.е. <d:TextBlock> ).

    Итак, в Java мы будем использовать API XML для поиска узла, который необходимо редактировать, по имени. Например, TextBlock, содержащий сведения о стране, называется txtCountry. Таким образом, для формирования XAML нашего приложения вспомогательная функция должна выполнять следующие шаги:

    Принять входной параметр. Если он null, присвоить ему значение по умолчанию.

    Задать text/xml в качестве выходного типа MIME.

    Создать объект для XML-документа и загрузить в него XAML-файл шаблона. В этом примере XAML хранится в документе Template.xml.

    Открыть базу данных SQL Server и выполнить запрос к таблице Northwind Customers для поиска покупателя, соответствующего входному параметру.

    Необходимо будет прочитать максимум одну строку, читаем ее.

    Используем XPath для узла ContactName (Имя контакта), чтобы найти его, и замещаем его содержимое значением поля ContactName, полученным из базы данных.

    Используем XPath для узла City (Город), чтобы найти его, и замещаем его содержимое значением поля City, полученным из базы данных.

    Используем XPath для узла Country (Страна), чтобы найти его, и замещаем его содержимое значением поля Country, полученным из базы данных.

    Записываем XML в поток HTTP-ответа.

    Имейте в виду этот алгоритм, рассматривая код листинга 10.3, в котором показана вспомогательная функция, используемая сервлетом для генерирования XAML

    protected void processRequest(HttpServletRequest request,
        HttpServletResponse response)
      throws ServletException, IOException {
      try
      {
        String strID = request.getParameter("ID");
        if(strID==null)
          strID="ALFKI";
        DocumentBuilderFactory factory =
        DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(true);
        response.setContentType("text/xml");
        PrintWriter out = response.getWriter();
        DocumentBuilder builder = factory.newDocumentBuilder();
        String uri = getServletContext().getRealPath("template.xml");
        Document doc = builder.parse(uri);
        DOMSource domSource = new DOMSource(doc);
        StreamResult streamResult = new StreamResult(out);
        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer serializer = tf.newTransformer();
        XPathFactory xpFactory = XPathFactory.newInstance();
        XPath xpath = xpFactory.newXPath();
        xpath.setNamespaceContext(new DefaultNameSpaceContext());
        String strContactName="";
        String strCity="";
        String strCountry="";
        java.lang.Class.forName
          ("com.microsoft.sqlserver.jdbc.SQLServerDriver");
        Connection c = java.sql.DriverManager.getConnection
          ("jdbc:sqlserver://localhost\\SQLEXPRESS;databasename=Northwind;
        user=javauser;password=javauser;");
        String SQL =
          "Select ContactName, City, Country
        from Customers where CustomerID = ?";
        PreparedStatement pstmt = c.prepareStatement(SQL);
        pstmt.setString(1, strID);
        ResultSet rs = pstmt.executeQuery();
        while (rs.next()) 
        {
          strContactName = rs.getString("ContactName");
          strCity=rs.getString("City");
          strCountry=rs.getString("Country");
          String strXPath = "//d:TextBlock[@x:Name='txtName']";
          XPathExpression expr = xpath.compile(strXPath);
          Object result = expr.evaluate(doc,XPathConstants.NODESET);
          NodeList nodes = (NodeList) result;
          Node ndeT1 =
            nodes.item(0).getAttributes().getNamedItem("Text");
          ndeT1.setNodeValue(strContactName);
          strXPath = "//d:TextBlock[@x:Name='txtCity']";
          expr = xpath.compile(strXPath);
          result = expr.evaluate(doc,XPathConstants.NODESET);
          nodes = (NodeList) result;
          ndeT1 =
          nodes.item(0).getAttributes().getNamedItem("Text");
          ndeT1.setNodeValue(strCity);
          strXPath = "//d:TextBlock[@x:Name='txtCountry']";
          expr = xpath.compile(strXPath);
          result = expr.evaluate(doc,XPathConstants.NODESET);
          nodes = (NodeList) result;
          ndeT1 =
            nodes.item(0).getAttributes().getNamedItem("Text");
          ndeT1.setNodeValue(strCountry);
        }
        rs.close();
        pstmt.close();
        serializer.transform(domSource, streamResult);
        out.close();
      }
      catch(Exception ex)
      {
        ex.printStackTrace();
      }
    }

    Результаты выполнения этого сервлета (на сервере приложений Tomcat) представлены на рис. 10.8.

    (рис 10.8) Генерирование XAML с помощью сервлета

    Отображение Silverlight из JSP

    Чтобы получить JSP-страницу, создается HTML-страница и затем те ее части, в которых должна выполняться логика страницы, дополняются с помощью разметки JSP. В данном случае мы хотим отображать Silverlight-приложение. Обычно это делается путем импорта библиотек сценариев Silverlight.js и createSilverlight.js с последующим вызовом createSilverlight в именованном элементе DIV.

    Однако наша JSP-страница будет немного более функциональной: она сможет принимать параметр и использовать его для построения элемента исходного XAML, созданного ранее. Для этого пишем код на Java и встраиваем его с помощью тегов JSP <% и %>. Полный код JSP-страницы представлен в листинге 10.4.

    <%@page contentType="text/html"%>
    <%@page pageEncoding="UTF-8"%>
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
         "http://www.w3.org/TR/html4/loose.dtd">
    <html>
      <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
          <script type="text/javascript" src="js/silverlight.js"></script>
          <script type="text/javascript"
          src="js/createSilverlight.js"></script>
          <script type="text/javascript">
            function handleLoad(control, userContext, rootElement)
            {
            <%
            String strValue="";
            if (request.getParameter("ID") == null) {
             strValue = "ALFKI";
            } else {
             strValue = request.getParameter("ID");
            }
            %>
            control.source = "http://localhost:8084/ScreenCast2/XamlSource?ID=<% out.print(strValue); %>
              ";
              }
            </script>
          <title>JSP Page</title>
        </head>
      <body>
        <h1>Silverlight JSP Page</h1>
        <div id="slContent">
          <script type="text/javascript">
            createSilverlight();
          </script>
        </div>
      </body>
    </html>

    На рис. 10.9 и 10-10 показано, как эта страница выглядит. Заметьте, что параметр AROUT передается в JSP-страницу в строке запроса первой страницы, тогда как ALFKI передается во вторую страницу.

    (рис 10.9) Выполнение JSP с параметром ID AROUT(рис 10.10) Выполнение JSP с параметром ID ALFKI

    Использование JavaScript для создания приложений, взаимодействующих с сервером

    Silverlight приложение выполняется в браузере, поэтому JavaScript, и в частности AJAX, могут использоваться для создания взаимодействующих с сервером Silvelight-приложений. В данном разделе рассматриваются методы, применяемые для этого, включая получение данных от серверного приложения с помощью AJAX и введение их в собственное приложение Silverlight, а также использование ASP.NET AJAX для связи с Веб-сервисом, приема данных с сервера и формирования их представления в Silver-light.

    Использование AJAX

    Мы собираемся создать простое приложение, формирующее данные, которые будут перехватываться AJAX-вызовом и передаваться в Silverlight для отображения. Для этого сначала необходимо создать серверное приложение, которое при вызове будет возвращать текущее время на сервере.

    Создание серверного приложения для получения текущего времени

    Для начала создадим новое Веб-приложение ASP.NET. Добавим в него новый универсальный обработчик ServerTime.ashx. Отредактируем код этого обработчика так, чтобы он возвращал текущее системное время в виде обычного текста. Вот полный код ASHX:

    using System;
    using System.Collections;
    using System.Data;
    using System.Linq;
    using System.Web;
    using System.Web.Services;
    using System.Web.Services.Protocols;
    using System.Xml.Linq;
    
    namespace AJAXSample
    {
      [WebService(Namespace = "http://tempuri.org/")]
      [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1 1)]
      public class ServerTime : IHttpHandler
      {
        public void ProcessRequest(HttpContext context)
        {
          context.Response.ContentType = "text/plain";
          context.Response.Write(System.DateTime.Now.ToShortTimeString());
        }
        public bool IsReusable
        {
          get
          {
            return false;
          }
        }
      }
    }

    При выполнении этот ASHX передаст текущее системное время в браузер, как показано на рис. 10.11.

    (рис 10.11) Выполнение обработчика ServerTime

    Теперь рассмотрим, как можно получить эти данные и отобразить в Silverlight.

    Создание клиентского приложения для отображения времени

    При создании нового Silverlight-приложения с использованием JavaScript в Microsoft Visual Studio будет создано приложение с простой кнопкой, которая выглядит, как представлено на рис. 10.12. Изменим это приложение так, чтобы при загрузке страницы оно обращалось к серверу для получения времени, используя только что созданный нами обработчик ASHX, и затем выводило это время на кнопке.

    (рис 10.12) Базовое приложение Silverlight

    Сделать это очень просто. Добавьте следующий AJAX-код в блок сценария:

    var xmlHttp;
    function getLatestTime() 
    {
      xmlHttp = new XMLHttpRequest();
      xmlHttp.open("GET","http://localhost:42395/ServerTime.ashx",true);
      xmlHttp.onreadystatechange = handleAJAXCallback;
      xmlHttp.send(null); 
    }

    Если вы не знакомы с AJAX, не волнуйтесь, он достаточно прост. Первая строка создает новый объект XMLHttpRequest, который является основой всего, что делает AJAX. Обратите внимание, что мы стараемся не усложнять код, поэтому для более старых браузеров синтаксис создания объекта xmlHttp будет другим. После этого открывается URI универсального обработчика ServerTime, который был создан нами ранее.

    Подсказка Чтобы этот фрагмент кода работал на вашем компьютере, не забудьте заменить URL в предыдущем фрагменте кода соответствующим URL вашего приложения.

    Далее объявляется функция-обработчик события изменения свойства readyState (Состояние готовности) объекта AJAX. В этом случае формируется событие onreadystatechange (обратите внимание на чувствительность к регистру; событие должно быть записано строчными буквами), и обработчик события располагается в функции handleAJAXCallback (Обработка обратного вызова AJAX), которую вы увидите через мгновение. Наконец, вызывается функция send() объекта xmlHttp для установления сетевого соединения.

    Функция handleAJAXCallback вызывается при каждом изменении свойства readyState объекта xmlHttp. Существует пять различных состояний:

  • 0—Запрос еще не инициализирован.
  • 1—Запрос инициализирован.
  • 2—Запрос отправлен.
  • 3—Запрос обрабатывается.
  • 4—Запрос завершен.
  • Нас интересует состояние, свидетельствующее о завершении запроса, поэтому проводим проверку состояния 4. Вот код:

    function handleAJAXCallback()
    {
     if((xmlHttp.readyState == 4)  (xmlHttp.status ==200))
      {
       var SL = document.getElementById("silverlightPlugIn");
       SL.content.findName("txt").Text = xmlHttp.responseText;
      }
    }

    Этот код проверяет значение свойства readyState и HTTP-код состояния (в свойстве status ). Если они равны 4 и 200, соответственно (т.е. соединение было успешным), приложение находит в XAML элемент txt и задает в качестве его значение текст ответа. Обратите внимание, что в случае использования базового шаблона XAML, TextBlock с надписью на кнопке не имеет имени, поэтому не забудьте присвоить ему имя txt, чтобы гарантировать работоспособность этого кода. Свойство responseText (Текст ответа) объекта xmlHttp содержит данные, возвращенные с сервера. В данном случае, это текущее время на сервере.

    И последнее, необходимо обеспечить вызов сценария при загрузке страницы. Для этого мы задаем сценарий onLoad в теге <BODY> страницы. Приведем пример:

    <body onload = "getLatestTime();">

    Как все это выглядит, можно увидеть на странице рис. 10.13.

    (рис 10.13) Использование AJAX для обновления приложения Silverlight

    Это полезная техника для создания высоко-динамичных сайтов на Silverlight с использованием технологий, ориентированных на стандарты. Также показано, что Silverlight не связан с инфраструктурой сервера Microsoft и может размещаться и обновляться с любого сервера.

    В следующем разделе будут рассмотрены Веб-сервисы ASP.NET и то, как с помощью ASP.NET AJAX организовать взаимодействие между ним и Silverlight.

    Использование ASP.NET AJAX и Веб-сервисов

    При создании Веб-сервиса с использованием ASP.NET можно пометить его как используемый в JavaScript, имея в виду, что этот Веб-сервис может автоматически сгенерировать JavaScript-прокси. Этот прокси использует JavaScript Object Notation (JSON), позволяя без труда передавать по сети сложные типы данных, не обращая внимания на вопросы совместимости. Итак, давайте создадим Веб-сервис и посмотрим, как можно с помощью JavaScript извлекать данные из него и помещать в Silver-light.

    В Visual Studio создаем новое приложение и добавляем в него Веб-сервис ASMX. Будем использовать этот Веб-сервис для формирования текущего времени в двух форматах: коротком и длинном. Для этого создадим класс, содержащий две строки, что обеспечит нам структурированный объект с данными. Вот как он выглядит:

    public class DateData 
    {
      public string strShortTime;
      public string strLongTime; 
    }

    Теперь напишем код класса Веб-сервиса, который будет возвращать текущее время в двух форматах с помощью представленного выше класса. Обратите внимание, что класс обозначен как System.Web.Script.Services.ScriptService. Тем самым мы указываем инфраструктуре ASP.NET пометить его как сервис, который может использоваться из JavaScript:

    using System;
    using System.Linq;
    using System.Web;
    using System.Web.Services;
    using System.Web.Services.Protocols;
    using System.Xml.Linq;
    
    [WebService(Namespace = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1 1)]
    [System.Web.Script.Services.ScriptService]
    
    public class Service : System.Web.Services.WebService
    {
     public Service () 
     {
     }
     [WebMethod]
     public DateData getTime()
     {
      DateData dReturn = new DateData();
      dReturn.strLongTime = System.DateTime.Now.ToLongTimeString();
      dReturn.strShortTime = System.DateTime.Now.ToShortTimeString();
      return dReturn;
     }
    }

    При выполнении этого сервиса добавление /js в конце URI обеспечит формирование JavaScript-прокси. Поэтому для работы с этим Веб-сервисом можно либо использовать HTML-страницу, которая включает этот сценарий, либо ASP.NET-страницу с ScriptManager, ссылающимся на этот сервис. Применим здесь второй метод.

    Создадим в решении ASP.NET-страницу и добавим в нее элемент управления ScriptManager. В Script-Manager необходимо добавить ссылку на только что созданный вами Веб-сервис. Делаем это с помощью тега <Services>. Вот пример:

    <asp:ScriptManager ID="ScriptManager1" runat="server">
      <Services>
        <asp:ServiceReference Path="~/Service.asmx" InlineScript="true" />
      </Services>
    </asp:ScriptManager>

    Далее добавляем на страницу Silverlight, как обычно. Но сначала взгляните на XAML, визуальное представление которого будет формировать Silverlight. В демонстрационных целях я сделал его максимально простым:

    <Canvas xmlns="http://schemas.microsoft.com/client/2007"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
      <TextBlock Canvas.Top="0" x:Name="txtShort"></TextBlock>
      <TextBlock Canvas.Top="20" x:Name="txtLong"></TextBlock>
    </Canvas>

    Добавляем этот код в страницу и создаем экземпляр объекта Silverlight, как обычно. Вот код, который может быть добавлен в раздел <body> разметки ASP.NET:

    <div id="silverlightPlugInHost">
      <script type="text/javascript">
        Silverlight.createObjectEx({
         source: 'Scene.xaml',
         parentElement: document.getElementById('silverlightPlugInHost'),
         id: 'silverlightPlugIn',
         properties: {
          width: '100%',
          height: '100%',
          background:'white',
          version: '1.0'
         },
         events: {
          onLoad: handleLoad
         },
         context: null
        });
      </script>

    В этом коде необходимо обратить внимание на две вещи. Во-первых, источник задан как Scene.xaml, поэтому убедитесь, что при вызове своего XAML-файла используете именно это имя. Во-вторых, событие handleLoad определено как событие, формируемое, когда содержимое Silverlight загружено, и сформировано его визуальное представление.

    Итак, рассмотрим это событие handleLoad:

    function handleLoad() 
    {
     Service.getTime(onSuccess, on Failed,""); 
    }

    Веб-сервис назван Service, и JavaScript-прокси принимает его имя. Таким образом, JavaScript-прокси можно вызывать, используя синтаксис ИмяКласса.ВебМетод.Наш Веб-метод назывался getTime (Получить время), поэтому просто вызываем Service.getTime. JavaScript-прокси для общения с Веб-сервисом использует AJAX, и, поскольку AJAX применяет механизм обратного вызова, должны быть описаны функции обратного вызова в случае успеха и неудачи (или истечения времени ожидания). Итак, в данном случае, задаются функции обратного вызова onSuccess (В случае успеха) и onFailed (В случае неудачи), соответственно. Третий параметр - это параметр контекста, который может быть пустым.

    Вот функции onSuccess и onFailed:

    function onSuccess(result)
    {
     var sl = document.getElementById("silverlightPlugIn");
     sl.content.findName("txtShort").Text = result.strShortTime;
     sl.content.findName("txtLong").Text = result.strLongTime;
    }
    function onFailed(result)
    {
     alert("Timed out");
    }

    Эти функции принимают возвращенное в параметре result (результат) значение. Мы используем JSON, поэтому это структурированный объект, так что значениями полей result.strShortTime и re-sult.strLongTime будут строки, содержащие время в укороченном и удлиненном форматах, соответственно. Нам осталось только найти элементы TextBlock по их именам и задать им соответствующие значения.

    Взаимодействие и данные в .NET

    При использовании .NET в Silverlight 2 взаимодействие с сервисами реализовывается посредством двух основных классов: WebClient и WebRequest (Веб-запрос). Как будет показано далее, эти классы обеспечивают гибкие средства доступа к данным. Кроме того, вы увидите, как в Silverlight можно использовать классы Windows Communication Foundation и ADO.NET Data Services (службы данных ADO.NET).

    Класс WebClient

    Класс WebClient является простейшим классом удаленного взаимодействия для Silverlight. При его использовании задается Uri содержимого, доступ к которому необходимо организовать, и затем для загрузки данных вызывается метод DownloadStringAsync (Загрузить строку асинхронно) или OpenReadAsync (Открыть для чтения асинхронно). Первый используется для получения строковых данных, второй -для получения любых данных в виде потока. Для возвращения результатов выполнения этих методов потребуется функция обратного вызова, DownloadStringCompleted (Загрузка строки завершена) или OpenReadCompleted (Чтение завершено), соответственно.

    Попробуем все это на практике. Создадим приложение Silverlight и, как делали это ранее, добавим универсальный обработчик в Веб-приложение и используем его для возвращения текущего времени на сервере. Для этого просто изменим ProcessRequest (Обработать запрос) следующим образом:

    public void ProcessRequest (HttpContext context) 
    {
      context. Response.ContentType = "text/plain";
      context. Response. Write(System.DateTime. Now.ToLongTimeString()); 
    }

    Теперь в Page.xaml.cs можно использовать класс WebClient для общения с сервером. Для этого добавьте в начало своего кода ссылку на пространство имен System.Net:

    using System.Net;

    Теперь, чтобы использовать класс WebClient, необходимо будет просто применить следующий код. Обратите внимание, что при написании кода этого приложения я использовал Cassini, поэтому работал с портом 65144. Вы можете использовать другой порт при выполнении своего приложения, поэтому не забудьте внести в код соответствующие изменения или настройте Cassini на использование конкретного порта.

    public Page()
    {
     InitializeComponent();
     WebClient cl = new WebClient();
     Uri uri = new Uri("http://localhost:65144/WebClientTest Web/Time.ashx");
     cl.DownloadStringCompleted +=
     new DownloadStringCompletedEventHandler(cl DownloadStringCompleted);
     cl.DownloadStringAsync(uri);
    }

    Для события DownloadStringCompleted объявлена функция-обработчик cl DownloadStringCompleted, поэтому по завершении передачи данных будет вызываться эта функция. Рассмотрим ее:

    void cl  DownloadStringCompleted(object sender,
             DownloadStringCompletedEventArgs e) {
    txtTime.Text = e.Result; }

    Как видите, функция получает объект аргументов (в виде класса DownloadStringCompletedEventArgs ), в котором имеется свойство Result, содержащее возвращенную строку.

    WebClient имеет еще несколько полезных атрибутов: свойство IsBusy (Занят), которое возвращает true, если асинхронная передача данных еще не завершена, и метод CancelAync, который может использоваться для отмены выполняющихся асинхронных транзакций. Если это происходит, свойству Cancelled (Отменен) соответствующего обработчика ( DownloadStringCompleted или OpenReadCompleted ) присваивается значение true.

    Класс WebRequest

    Немного больше возможностей, включая использование Silverlight в качестве клиентской части стандартного шаблона HTTP-форм (с HTTP-POST ), предоставляет класс WebRequest. Он действительно полезен с точки зрения обеспечения возможности взаимодействия, потому что методика HTTP-POST широко используется во всех серверных технологиях, так что клиентскую часть на HTML можно заменить Silverlight-приложением без всяких изменений серверной части кода.

    Итак, например, объединим серверный компонент, принимающий запрос HTTP post к трем текстовым полям, t1, t2 и t3. Начнем с создания Silverlight-приложения и добавим универсальный обработчик в Веб-проект. Отредактируем метод обработчика ProcessRequest так, чтобы он принимал значения формы через HTTP-POST. Вот как реализовать прием трех строк из запроса HTTP-POST:

    public void ProcessRequest (HttpContext context) {
     string str1 = "";
     string str2 = "";
     string str3 = "";
     if (context.Request.HttpMethod == "POST")
     {
      str1 = context.Request.Form["t1"];
      str2 = context.Request.Form["t2"];
      str3 = context.Request.Form["t3"];
     }
     context.Response.ContentType = "text/plain";
     context.Response.Write("OK");
    }

    Обычно в таких случаях используется HTML-форма с тремя элементами управления TextField (t1, t2 и t3 ) и кнопкой Submit (Отправить) для передачи этих данных. То же самое можно сделать в Silverlight. Вот XAML для простой формы ввода данных:

    <UserControl x:Class="WebRequestTest.Page" xmlns=" http://schemas.microsoft.com/client/2007" xmlns:x="http://schemas.microsoft.com/winfx/2006/xam l" Width="400" Height="300">

    <UserControl x:Class="WebRequestTest.Page"
                 xmlns="http://schemas.microsoft.com/client/2007"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 Width="400" Height="300">
      <Grid x:Name="LayoutRoot" Background="White">
        <Grid.ColumnDefinitions>
          <ColumnDefinition Width="0.265*"/>
          <ColumnDefinition Width="0.735*"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
          <RowDefinition Height="0.037*"/>
          <RowDefinition Height="0.1*"/>
          <RowDefinition Height="0.05*"/>
          <RowDefinition Height="0.106*"/>
          <RowDefinition Height="0.054*"/>
          <RowDefinition Height="0.09*"/>
          <RowDefinition Height="0.563*"/>
        </Grid.RowDefinitions>
        <TextBox Margin="0,0,25,0" Grid.Column="1"
                 Grid.Row="1" x:Name="t1"/>
        <TextBox Margin="0,0,25,0" Grid.Column="1"
                 Grid.Row="3" x:Name="t2"/>
        <TextBox Margin="0,0,25,0" Grid.Column="1"
                 Grid.Row="5" x:Name="t3"/>
        <TextBlock Margin="8,0,0,0" Grid.Row="1"
                   Text="Field 1" TextWrapping="Wrap"/>
        <TextBlock Margin="8,0,8,0" Grid.Row="3"
                   Text="Field 2" TextWrapping="Wrap"/>
        <TextBlock Margin="8,0,8,0" Grid.Row="5"
                   Text="Field 3" TextWrapping="Wrap"/>
        <Button Height="36" HorizontalAlignment="Left"
                Margin="0,26.9,0,0" VerticalAlignment="Top"
                Width="77" Grid.Column="1" Grid.Row="6"
                Content="Submit" x:Name="b" Click="b_Click" />
      </Grid>
    </UserControl>

    Как эта форма выглядит, можно увидеть на рис. 10.14.

    (рис 10.14) Форма ввода данных на Silverlight

    Пользователь вводит текст в трех полях и затем нажимает кнопку Submit. При этом формируется событие b_Click. Вот код обработки этого события:

    private void b Click(object sender, RoutedEventArgs e) {
      Uri uri = new Uri( "http://localhost:22602/WebRequestTest Web/SubmitForm.ashx");
      WebRequest rq = WebRequest.Create(uri);
      rq. Method = "POST";
      rq.ContentType = "application/x-www-form-urlencoded";
      rq.BeginGetRequestStream(new AsyncCallback(RequestReady), rq);
      rq.BeginGetResponse(new AsyncCallback(ResponseReady), rq); 
    }

    При использовании класса WebResponse сначала необходимо задать вызываемый URI. Это выполняется с помощью объекта Uri; он указывает на сайт, содержащий обработчик форм. Затем этот URI передается в метод Create (Создать) WebRequest для установления связи. Также задается используемый Method (Метод). Это может быть метод HTTP-Verb, такой как POST или GET. В данном случае, выполняется отправка данных формы, поэтому должен применяться POST.

    Далее необходимо задать соответствующий тип содержимого. Опять же, поскольку используется форма, этим типом будет application/x-www-form-urlencoded. Все эти действия выполняются соответственно Веб-стандартам, поэтому возможные варианты значений можно найти в любом хорошем справочнике по Веб.

    Наконец, должен быть задан поток запроса. Это поток, потому что предполагается запись данных в запрос. Вы должны задать метод асинхронного обратного вызова, который будет выполняться после завершения Request, а также ссылку на сам объект запроса. Кроме того, требуется перехватывать ответ формы, поэтому вызывается BeginGetResponse (Начать прием ответа) для начала настройки потока и задается обратный вызов, который будет выполняться, когда поток ответа готов.

    В методе обратного вызова, который вызывается, когда поток запроса готов, можно выполнить запись в поток, используя объект StreamWriter (объект записи в поток). В данном случае, записывается несколько параметров. Согласно протоколу HTTP они должны быть разделены символами :

    t1=значение1t2=значение2t3=значение3.

    Итак, теперь осталось лишь записать имя параметра и свойство Text полей ввода:

    void RequestReady(IAsyncResult aR){
      WebRequest rq = aR.AsyncState as WebRequest;
      Stream rqStream = rq.EndGetRequestStream(aR);
      StreamWriter w = new StreamWriter(rqStream);
      w.Write("t1=" + t1.Text + "");
      w.Write("t2=" + t2.Text + "");
      w.Write("t3=" + t3.Text );
      w.Flush(); 
    }

    Как было показано в универсальном обработчике событий, после того как сервер получает данные, он записывает OK для обозначения состояния. По завершении записи ответа вызывается метод обратного вызова ResponseReady (Ответ готов).

    void ResponseReady(IAsyncResult aR) {
      WebRequest rq = aR.AsyncState as WebRequest;
      using(WebResponse rs = rq.EndGetResponse(aR))
      using (Stream rpStream = rs.GetResponseStream())
      { StreamReader rdr = new StreamReader(rpStream); 
        string postStatus = rdr.ReadToEnd();
      } 
    }

    Этот код просто извлекает поток ответа из результата асинхронного вызова и читает его до конца. В данном случае, это просто состояние, но так можно извлекать ответы с сервера для каждого поля.

    Как видите, применяя данную методику, довольно просто создавать Silverlight-клиентские приложения заменяющие традиционные HTTP-формы!

    Использование SyndicatedFeed для организации доступа к RSS и ATOM

    Двумя наиболее популярными типами данных, доступными в Интернете, являются каналы RSS и ATOM, обычно используемые в блогах или на новостных сайтах. Silverlight предлагает класс SyndicatedFeed (Информационный канал), с помощью которого можно без труда организовать доступ к этим данным.

    SyndicatedFeed очень прост в использовании. Рассмотрим на примере. Но, прежде чем продолжить, остановимся на мгновение на междоменном доступе в Silverlight. При организации доступа к содержимому, находящемуся на других сайтах (не на том, который обслуживает содержимое Silverlight), .NET-классы ищут на сервере файл Crossdomain.xml. Если они находят его, то проводят синтаксический разбор междоменных правил, чтобы выяснить, имеют ли они право на доступ к содержимому. Если они не находят этот файл, они формируют ошибку. Поэтому помните об этом, прежде чем использовать SyndicatedFeed для доступа к данным, или можете столкнуться с проблемами. В сети можно найти много информации о Crossdomain.xml, потому что он де факто является стандартным способом обеспечения междоменного доступа для разных технологий.

    Для данного примера мы будем просто использовать статический XML-документ Doc.rss, хранящийся на сервере.

    Прежде чем приступать к написанию кода, убедитесь в наличии соответствующих ссылок, которые обеспечивают возможность использовать SyndicatedFeed, потоковую передачу данных и XML

    using System.Net;
    using System.Xml;
    using System.IO;
    using System.ServiceModel.Syndication;

    Чтобы создать эту ссылку, вероятно, придется добавить ссылку на System.ServiceModel.Syndication.

    Следующее, что необходимо сделать до написания кода, - сформировать представление данных в XAML

    <UserControl x:Class="RSSClient.Page"
                 xmlns="http://schemas.microsoft.com/client/2007"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 Width="400" Height="300">
      <Canvas x:Name="LayoutRoot" Width="400"
              Height="300" Background="White">
        <ItemsControl x:Name=" rssTitles">
          <ItemsControl.ItemTemplate>
            <DataTemplate>
              <StackPanel Orientation="Vertical">
                <TextBlock FontWeight="Bold"
                Text="{Binding Title.Text}" />
              </StackPanel>
            </DataTemplate>
          </ItemsControl.ItemTemplate>
        </ItemsControl>
      </Canvas>
    </UserControl>

    Здесь в XAML используется объект ItemsControl (Элементы), который в сочетании с ItemTemplate (Шаблон элемента) обеспечит базовую функцию повторения. Этот объект связывается с данными с помощью DataTemplate (Шаблон данных), определяющего, что элемент управления TextBlock связан с данными RSS-канала по Title.

    Возвращаясь к C#-коду, теперь можно создать объект WebClient для загрузки RSS-документа. После того, как RSS-документ будет загружен, его можно будет прочитать с помощью элемента управления SyndicatedFeed. Вот код для создания WebClient, выполняющего доступ к RSS-документу:

    public Page() {
      InitializeComponent();
      WebClient client = new WebClient();
      Uri uri = new Uri("doc.rss",UriKind.Relative);
      client.OpenReadCompleted + = 
         new OpenReadCompletedEventHandler(client OpenReadCompleted);
      client.OpenReadAsync(uri); 
    }

    Здесь задается функция обратного вызова ( client OpenReadCompleted ), которая вызывается по завершении загрузки. Вот код этой функции:

    void client OpenReadCompleted(object sender,
        OpenReadCompletedEventArgs e) { 
      XmlReader r = XmlReader.Create(e.Result); 
      SyndicationFeed feed = SyndicationFeed.Load(r); 
      rssTitles.ItemsSource = feed.Items; 
    }

    Здесь создается XmlReader (объект чтения Xml), который считывает результаты обратного вызова и передает их в SyndicationFeed. Также SyndicationFeed содержит соответствующие коллекции, включая коллекцию Items (Элементы). Эта коллекция содержит (кроме всего прочего) Title для элемента RSS, таким образом, задавая свойство ItemsSource элемента ItemsControl коллекции rssTitles, мы выполняем привязку данных, и заголовки будут формироваться Silverlight. Результаты представлены на рис. 10.15.

    (рис 10.15) Просмотр данных RSS с использованием SyndicationFeed и привязки данных

    Выходя за рамки таких простых каналов, как RSS и ATOM, Silverlight предоставляет возможность использования таких расширенных сервисов, как ASMX и Веб-сервисы WCF. Это будет рассмотрено в следующем разделе.

    Silverlight и WCF

    Пространство имен System.ServiceModel содержит классы, используемые Silverlight для работы с Веб-сервисами . В данном разделе мы поэтапно рассмотрим процесс создания простого сервиса в WCF и его использование в Silverlight.

    Создадим новое приложение Silverlight и добавим новый класс в Веб-проект. Назовем его Person (Человек), и будет создан класс Person.cs.Он будет использоваться для передачи данных между сервисом и клиентом. Для этого необходимо обозначить этот класс как DataContract (Контракт данных) между WCF и Silverlight и пометить каждый его элемент, с которым должен будет работать Silverlight, атрибутом DataMember (Элемент данных).

    Обратите внимание, что понадобится включить System.Runtime.Serialization. Рассмотрим код:

    [DataContract] 
    public class Person {
      public Person(string strName, string strAge)
      {
        Name = strName; Age = strAge;
      }
      [DataMember]
      public string Name { get; set; }
      [DataMember]
      public string Age { get; set; } 
    }

    Сюда также входит конструктор, используемый для инициализации свойств Name и Age (Возраст).

    Далее необходимо добавить в проект шаблон WCF Service (WCF-сервис). Присвоение ему имени Per-sonService обеспечит создание файлов PersonService.cs и IPersonService.cs. Файл IPersonService.cs будет отвечать за создание интерфейса, определяющего контракт сервиса. В нем необходимо будет определить метод сервиса, возвращающий список людей. Сделать это несложно: просто пометьте интерфейс атрибутом ServiceContract и метод, доступный для вызова, атрибутом OperationContract (Контракт операции).

    [ServiceContract]
    public interface IPersonService
    {
      [OperationContract]
      Person[] getPeople(); 
    }

    Реализация сервиса выполняется в классе PersonService.cs.В нем должны быть реализованы, по крайней мере,методы, описанные в OperationContract. Рассмотрим пример:

    public class PersonService : IPersonService {
      public Person[] getPeople() {
        Person[] people = {
          new Person("Derek Snyder","32"), 
          new Person("Yoshi Latimer","19"), 
          new Person("Mark  Steel","31")
        }; 
        return people; 
      } 
    }

    Здесь создается и возвращается вызывающей стороне массив объектов Person, названный people (люди). Silverlight может выполнить привязку к такой коллекции элементов. Прежде, чем двигаться дальше, необходимо убедиться, что WCF правильно сконфигурирован для выполнения базового HTTP-связывания1, которое будет использоваться клиентом Silverlight. Это выполняется в файле Web.config. Найдите следующий код в узле <serviceModel> и убедитесь, что связывание задано как basicHttpBinding (Базовое HTTP-связывание):

    <service behaviorConfiguration="PersonServiceBehavior"
             name="PersonService">
      <endpoint address=""
                binding="basicHttpBinding"
                contract="IPersonService">
        <identity>
          <dns value="localhost"/>
        </identity>
      </endpoint>
      <endpoint address="mex" 
                binding="mexHttpBinding" 
        contract="IMetadataExchange"/>
    </service>

    Протестируйте работоспособность сервиса, щелкнув правой кнопкой мыши файл PersonService.svc и выбрав в контекстном меню View In Browser (Просмотр в браузере). Если все хорошо, на экране вы увидите примерно следующее (рис. 10.16):

    (рис 10.16) Выполнение WCF сервиса

    Если сервис работает правильно, можно добавлять ссылку на него в проект Silverlight. Опять же, все просто: щелкаем правой кнопкой мыши узел References в Solution Explorer и выбираем в контекстном меню Add Service Reference (Добавить ссылку на сервис). Появится диалоговое окно Add Service Reference, как показано на рис. 10.17. Введите адрес сервиса и щелкните кнопку Go (Перейти). Это обеспечит создание прокси-классов для сервиса.

    (рис 10.17) Добавление ссылки на сервис

    Прокси будет создан в пространстве имен, заданном в диалоговом окне. В данном случае, используется имя PSProxy, который через мгновение вы увидите в коде.

    Прежде всего, необходим XAML для определения уровня представления. Это XAML с привязкой к данным, который связывается с результатами вызова сервиса:

    <ItemsControl x:Name=" itemTitles">
      <ItemsControl.ItemTemplate>
        <DataTemplate>
          <StackPanel Orientation="Vertical">
            <StackPanel Orientation="Horizontal">
              <TextBlock FontWeight="Bold" Text="{Binding Name}" />
              <TextBlock Text=" : "></TextBlock>
              <TextBlock FontWeight="Bold" Text="{Binding Age}" />
            </StackPanel>
          </StackPanel>
        </DataTemplate>
      </ItemsControl.ItemTemplate>
    </ItemsControl>

    Как видите, здесь имеется два элемента TextBlock, которые связаны со значениями Name и Age. Рассмотрим вызов сервиса и то, как он используется для передачи данных в это содержимое Silverlight:

    Binding binding = new BasicHttpBinding();
    EndpointAddress endPoint = new EndpointAddress
      ("http://localhost:25101/PersonService Web/PersonService.svc");
    
    PSProxy.PersonServiceClient client = new
      PSProxy.PersonServiceClient(binding, endPoint);
    
    client.getPeopleCompleted += new
      EventHandler<PersonService.PSProxy.getPeopleCompletedEventArgs>
      (client getPeopleCompleted);
    
    client.getPeopleAsync();

    Здесь создается новый класс BasicHttpBinding под именем binding (что соответствует тому, что вы задавали в Web.config для этого сервиса) и EndpointAddress (Адрес конечной точки), указывающий на сам сервис. Затем создается новый экземпляр клиента сервиса (который был сформирован для вас автоматически при создании ссылки на сервис) и описывается функция обратного вызова. Наконец, выполняется вызов функции getPeopleAsync, которая обеспечивает асинхронный вызов метода сервиса. По завершении выполнения метода сервиса, запускается функция обратного вызова.

    Функция обратного вызова получает аргументы, содержащие массив объектов Person, формируемый методом сервиса. Это коллекция, которую можно связать с ItemsControl, представленным ранее. Посмотрим на код:

    void client getPeopleCompleted(object sender,
      PersonService.PSProxy.getPeopleCompletedEventArgs e) 
    {
     itemTitles.ItemsSource = e.Result; 
    }

    Результаты этого представлены на рис. 10.18.

    (рис 10.18) WCF клиент на Silverlight

    Все эти примеры очень просты, но я надеюсь, что они иллюстрируют различные доступные сценарии создания взаимодействующих с сервером приложений и обеспечивают базовые сведения, которые пригодятся при написании реального кода для Silverlight-приложений!

    Заключение

    В данной лекции представлены различные стратегии создания взаимодействующих с сервером приложений на Silverlight. Вы увидели, какXAML может использоваться в качестве инструмента взаимодействия из приложений на базе PHP и Java и как Silverlight может использоваться для связи с этими приложениями. Затем мы рассмотрели AJAX и познакомились с тем, как использовать AJAX и JavaScript для получения данных от серверных приложений, и как открытая природа дерева визуального отображения Silverlight позволяет передавать эти данные в элемент управления Silverlight для дальнейшего отображения. Наконец, вы познакомились с некоторыми новыми классами, предлагаемыми Silver-light 2 для работы с сетью и связи. Рассмотрены классы WebClient и WebRequest. Также вы научились создавать приложения для использования WCF-сервисов через прокси.

    В следующей лекции будут представлены некоторые ориентированные на потребителя технологии Silverlight 2, включая элементы управления для мультимедийного содержимого, элемент управления, обеспечивающий возможность использования ваших приложений на компьютерах с перьевым вводом, и новый элемент управления, обеспечивающий в Silverlight-приложениях функциональность Deep Zoom.

    Вернуться к учебному плану