Expression Blend для разработчиков Silverlight

ОГЛАВЛЕНИЕ

Теперь разработчикам гораздо легче создать приложение Silverlight 2. Проект, который начнем разрабатывать, похож на Silverlight сервис по обмену мгновенными сообщениями (чат) разработанный ScottGu и показанный на рисунке 5-1

Рисунок 5-1. Завершенное приложение

Ознакомление

Для начала вам понадобится запустить Expression Blend. Первое, что вы увидите, будет окошко нового проекта (New Project). Выбрав Silverlight 2 Application (Приложение Silverlight 2), установите язык программирования в Visual Basic и выберите месторасположение вашего нового проекта, который мы назовем BlendForSilverlight (как это показано на рисунке 5-2)

 

Рисунок 5-2. Создание нового проекта

Файлы Solution

Исследуйте правый верхний уголок окна. Проект и файлы , созданные Blend, являются идентичными тем, которые создает Visual Studio 2008. Более того, между ними нельзя производить экспорт/импорт - вы можете открыть обе среды разработки одновременно и они обе будут работать идеально (смотри рисунок 5-3)

 

Рисунок 5-3. Файлы Blend 

Быстрый обзор 

Правый верхний угол содержит не только файлы, но и меню свойств, меню ресурсов, а под ними находится окно данных. В центре расположена "Art Board" (поверхность разработки и Xaml-разметка, которая может быть разделена так, как это показано на рисунке 5-4)

 

Рисунок 5-4. Поверхность разработки и режим разделения

Я добавил подсветку в правом верхнем углу для того, чтобы выделить те элементы управления, которые позволяют переключаться между режимом дизайна, Xaml и режимом разделения. Данный угол содержит панель Interaction и инструментарий. Многие инструменты обладают маленькой пометкой в нижнем правом уголке, которая означает то, что нажав на нее вы сможете увидеть все соответствующие инструменты. К примеру, развернув меню инструмента табличной сетки вы увидите все элементы управления внешним видом, как это показано на рисунке 5-5

 

Рисунок 5-5. Развернутая закладка инструмента Grid

Закладка с двойной угловой скобкой осуществляет доступ ко всем элементам управления, включая любые пользовательские элементы управления, специализированные элементы управления и посреднические элементы (вам понадобится нажать на опцию Show All) как это показано на рисунке 5-6

 

Рисунок 5-6. Отображение закладки со всеми элементами управления

Обратите внимание на то, что в Asset Library, отображенной в данной закладке вы можете осуществить поиск необходимого элемента управления и нажав на кнопку с зависимой фиксацией вы также также можете выбрать библиотеку поиска элемента.


Приложения для обмена сообщениями (чат) - первые шаги

Мы начнем с перехода в режим дизайнера, затем зажмем клавишу Сontrol и затем при помощи колесика мыши увеличим отображаемое таким образом, чтобы вы смогли разглядеть всю табличную сетку.

В случае отсутствия колесика на вашей мышке вы можете изменить план просмотра при помощи значения (либо вписав любое значение) в нижней части окна дизайнера или при помощи опций меню View®Zoom In и View®Zoom Out 

Вы можете переместить поверхность нажав на инструмент "рука" и затем при помощи мыши щелкать и перетаскивать окошко, либо выполнить это при помощи полос прокрутки. Взгляните на правый верхний угол вашей табличной сетки (рисунок 5-7)

Рисунок 5-7. Правый верхний угол табличной сетки

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

Рисунок 5-8. Создание строк при помощи мыши

Сразу же после того, как вы щелкнете по рамке, появится иконка открытого замка над и под созданной строкой. Я добавлю две линии - одну для верхней строки, и вторую для нижней, тем самым явно создав строку между ними, при этом у нас будет три замка как это показано на рисунке 5-9.

Рисунок 5-9. Создание нижней строки

Обратите внимание на то, что сразу после создания строки отображаются не только иконки замков, но и курсор изменяет свою форму чтобы указать вам на то, что вы можете переместить, а также изменить размер строк.

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

Рисунок 5-10. Средняя строка имеет динамические размеры

Обратите внимание на то, что в определении средней строки свойство высоты (Height) установлено в значение * (звезда) - строка будет занимать все оставшееся пространство после распределения всех других строк. 

Создание колонок 

Давайте создадим две колонки: справа маленьку, которая будет обладать фиксированными размерами, а слева колонку побольше, обладающую динамическими размерами. Вы можете теперь это выполнить без посторонней помощи.

Настройка фонового цвета

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

  1. Щелкните по табличной сетке (Layout Root) в панели Interaction 
  2. Щелкните по панели свойств и раскройте закладку кистей (Brushes). Щелкните по кисти фона (Background) 
  3. Щелкните по кнопки градиентной кисти (Gradient Brush) 
  4. Добавьте некоторые остановки путем щелканья во время заливки градиентом 
  5. Затем манипулируя шкалой цвета 
  6. Передвигайте точку цвета в конкретный цвет 

Рисунок 5-11. Добавление градиентной заливки фона

Добавление кнопки отправки сообщений 

Следующим шагом будет добавление кнопки к правому нижнему углу. Нажмите на кнопку в инструментарии и перетащите ее в соответствующее место на форме, затем щелкните по окошку свойств. Далее откройте закладку Layout и установите ширину (Width) и высоту (Height) в значение Auto и установите небольшое значение (к примеру 4) для отступа (Margin) со всех сторон. Это заполнит пространство, при этом отделив его от колонки и строки. 

Значение, которое вы занесете в закладке свойств не будет использовано пока вы не покинете поле заполнения (то есть, нажмете клавишу tab)

Кое-что все же необходимо упомянуть об Layout Panel (Рисунок 5-12)

Рисунок 5-12. Установка внешнего вида кнопки при помощи панели внешнего вида (Layout) 

Для начала вам понадобится уставить ширину и/или высоту в значение auto нажав на кнопку, расположенную справа от поля. Во-вторых, при установке конкретных значений (не стандартных) вы увидите белую точку рядом с измененным свойством (всего на рисунке 5-12 вы можете заметить 5 точек). 

Исследуйте Xaml, как только вы установите данные значения, 

<Button HorizontalAlignment="Stretch" Margin="4,4,4,4" VerticalAlignment="Stretch" Grid.Column="1" Grid.Row="2" Content="Button"/>

Blend преобразовал ваши значения в Xaml для того, чтобы в результате у элемента был вид, который хотели ему придать. Тем не менее, мы забыли изменить свойство "Content" с Button на "Send". Простейшим путем будет написание слова Content в свойство Search box,

В позиции "co" вы получите "Column", "Horizontal Content", "Data Content" и "Content" как это показано на рисунке 5-13

Рисунок 5-13. Поиск с "co"

Пересмотрите свой поиск и вы сможете немного уменшить уровень сложности, как это показано на рисунке 5-14,

Рисунок 5-14. Преобразованный поиск

В любом случае, удалите слово Button из поля Content и впишите слово Search. Следующим шагом будет установка размера шрифта в значение 18 и его тип в Verdana, а также установка его свойства в Bold. (Сделать это необходимо в Xaml!) 

Некоторые элементы управления имеют текстовое свойство (text) и многие имеют свойство содержимого (content). Те, которые обладают свойством content зачастую заполняют его текстом, но его можно заполнить любым значением, а также любым элементом управления.

Рисунок 5-15. Настройка типа шрифта (Font) и многое другое


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

Исследуя финальное приложение, указанное в начале данной статьи, мы можем заметить, что оно обладает прямоугольной областью с округлыми углами (рисунок 5-16)

Рисунок 5-16. Область ввода сообщения

Это не так легко реализовать, поскольку TextBox не имеет свойства для установки округлых углов. Тем не менее, это можно выполнить, используя элемент управления Border. Нашим заданием теперь будет - расположить элемент TextBox в пределах элемента Border и позволить последнему обрабатывать внешний вид, а TextBox пусть обрабатывает текст!

Первым шагом будет заполнение ячейки табличной сетки элементом Border. Щелкните по закладке Grid и разверните ее щелкнув по элементу Border, затем перетащите его в соответствующую позицию в сетке (мы изменим размеры соответственно в окошке свойств). Как только элемент будет расположен в табличной сетке установите его ширину (Width) и высоту (Height) в значение Auto, а отступ (Margin) в значение 4 со всех сторон (Рисунок 5-17)

Рисунок 5-17. Установка размеров элемента Border в панели внешнего вида (Layout)

Давайте установим фоновый цвет, вручную вписав в окне свойств Brushes значения 255, 255, 255, при этом Alpha (opacity) установите в 45%. Я рекомендую выполнить это постепенно - установив значение красного цвета, получите красный фон, затем, при установке зеленого в 255 фон станет светло- желтым, и, наконец, при установке синего цвета в значение 255 фон станет светло-голубым. Теперь, если изменить Alpha до 45%, получится эффект тени (Рисунок 5-18)

Рисунок 5-18. Настройка фоновой кисти

Если вы внимательно исследуете настройки для RGBA , то вы увидите значение #72FFFFFF , эквивалентное четырем независимым значениям (при этом значение alpha будет первым). Наш элемент управления Border также нуждается в настройке кисти Border (на рисунке 5-18 она установлена в "No Brush"). Значения, которые нам так необходимы, являются 100% для Alpha и цвет должен быть черным (00,00,00).

Кисть Border появится тогда, когда мы установим толщину (по умолчанию она равна нулю), что мы можем сделать в панели Appearance. Давайте установим ее в значение 2. В данном месте мы также установим округлые углы установив значения в 5 как это показано на рисунке 5-19.

Рисунок 5-19. Установка толщины элемента Border и его округлых углов 

Добавление элемента Text Box

Рамка теперь имеет округлые углы, но, как мы уже поняли, элемент Border не может принимать ввод текста. Для этого нам понадобится текстовое поле, но нам не нужно накрывать только что настроенный фон. Все что нам надо сделать, так это расположить текстовое поле над элементом Border, при этом он должен быть прозрачным!

Для того, чтобы расположить текстовое поле в элементе Border, щелкните дважды по элементу Border в панели Interaction, тем самым оповещая Blend о том, что это теперь контейнер для следующего элемента управления. То есть, добавляемый элемент управления будет наследником элемента Border. Желтая рамка будет отображена от Layout Root к Border в качестве оповещения о том, что он теперь является активным контейнером. При перетаскивании текстового поля он будет расположен так, как это показано на рисунке 5-20, как в режиме дизайнера, так и в режиме Xaml. 

Рисунок 5-20. TextBox вложенный в элемент Border

Обзор ключевых моментов

Мы воспользовались некоторыми новыми возможностями и пора сделать обзор ключевых моментов по порядку:

  • Была настроена рамка с фоновым цветом и округлыми углами 
  • Текстовое поле было полностью вложено в пределах данной рамки, при этом его прозрачность равна 0 (делая его полностью прозрачным). Это придаст текстовому полю фон самойрамки, а также ее закругленные углы 
  • Для того, чтобы убедиться в том, что текстовое поле было вложено в пределах элемента Border, мы дважды щелкнули по рамке и панели Interaction, и желтое обрамление указывает на то, что элемент теперь является контейнером. 

Создание верхней строки

Мы проделаем то же самое для верхней строки, за исключением использования элемента TextBox - мы будем использовать textblock, установив тип шрифта в Comic Sans MS, его размер в 48, а само свойство text установите в ваше имя.

Для настройки правого верхнего угла начните с двойного щелчка по LayoutRoot для того, чтобы элемент стал контейнером, затем щелкните по элементу управления Image в Chevron. Установите свойство источника в любой файл изображения на вашем диске.

Создание пространства для общения

Финалом создания нашего чат-приложения будет средняя часть: пространство общения, которое будет элементом ListBox. Мы расположим его в элементе Border, который будет расстилаться по обеим колонкам. Вы можете расположить его вручную, растянув рамку по двум колонкам при помощи мышки.

Вы заметите со временем, что ColumnSpan был установлен в значение 2, и все, что вам понадобится, так это установка отступов (Margin) для того, чтобы рамка была корректно расположена (то есть все отступы должны быть равны 4.)

Вы можете установить фоновый цвет в сплошной синий ( FF03588D сойдет), и опять так мы придадим углам округлую форму, установив радиус в 5 для всех, а также установив рамку в черный цвет и ее толщину в 2.

Опять щелкните дважды по новой рамке и перетащите элемент ListBox (из соответствующей закладки) внутрь нового элемента Border, при этом установите его таким образом, чтобы он полностью заполнял рамку.

Добавление информации

Простейшим способом добавления тестовой информации является использование Xaml. В данном месте у нас есть хорошая возможность переключиться на Visual Studio, тем самым у нас все файлы будут в Blend, но вы можете оставить blend открытым и переключиться на проект в Visual Studio 2008.

Если ваш проект уже был открыт, то вы будете оповещены о том, что он был изменен, примите изменения и после вы заметите, что все файлы отображенные в Page.xaml очень похожи на те, что вы видели в Blend.

Рисунок 5-21. Исследование проекта в Visual Studio

Теперь вы с легкостью можете заполнить ваш чат информацией,

<ListBox Height="Auto" Width="Auto" x:Name="Conversation" >
    <ListBoxItem Content="Jesse: Is this working?" />
    <ListBoxItem Content="Scott: Of course." />
    <ListBoxItem Content="Jesse: I'm following the directions." />
    <ListBoxItem Content="Scott: Then you should be fine." />
    <ListBoxItem Content="Jesse: Great, thanks." />
  </ListBox>

  Сохраните файл и перейдите в Blend (нет необходимости закрывать Visual Studio 2008). Сделав это, Blend оповестит вас о том, что файлы были изменены,

Рисунок 5-22. Blend оповещает об изменениях в файлах

Примите их, и теперь вы сможете увидеть тестовую информацию. Вы можете нажать F5 для запуска программы в качестве предварительной проверки. Вы также заметите, что изображение не отобразится, пока вы не скопируете его в каталог bin/debug. Это так сложно сделать, тем не менее, нам придется осуществить привязку к источнику для файла изображения. 

Построение бизнес-уровня

Для получения поддержки обмена сообщениями мы построим сервис, который определит имя удаленного пользователя, его изображение и которое будет обрабатывать сообщения. Поскольку мы более заинтересованы в Silverlight, чем в построении сервиса (как минимум, в пределах данной статьи) мы упростим все созданием класса ChatSession, который будет отвечать за все сетевые действия, сокеты и другое, а также поддерживать набор объектов Message. Как вы уже наверняка догадались, мы также создадим класс Message для представления каждого сообщения в качестве "переданного" сервисом.

Поскольку Visual Studio 2008 больше подходит для программирования, чем Blend, то мы выполним всю работу в Visual Studio 2008, но мы перейдем в него, щелкнув правой кнопкой мыши по Solution в Blend и выбрав Edit в Visual Studio (Рисунок 5-23).

Рисунок 5-23. Переход к Visual Studio из Blend

Как только вы окажетесь в Visual Studio 2008 щелкните правой кнопкой мыши по проекту и выберите пункт Add Class. В диалоговом окне нового элемента выберите класс и назовите ваш новый класс ChatMessage, как это показано на рисунке 5-24

Рисунок 5-24. Добавление класса Chat Message

Класс Chat Message не так уж и сложен и составлен всего из двух свойств,

public class ChatMessage
{
   private string privateUserName;
   public string UserName
    {
     get
     {
     return privateUserName;
     }
     set
     {
       privateUserName = value;
      }
     }
     private string privateText;
     public string Text
     {
      get
      {
       return privateText;
      }
      set
      {
       privateText = value;
      }
    }
}

Класс ChatSession немного сложнее, но мы все значительно упростим. Мы начнем с маркировки класса как реализации INotifyPropertyChanged, что обеспечивает обновление полей пользовательского интерфейса одновременно с изменением информации.

public class ChatSession : INotifyPropertyChanged

Данный интерфейс требует только наличия PropertyChangedEventHandler , который реализует событие PropertyChanged,

public event PropertyChangedEventHandler PropertyChanged; 

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

private string privateRemoteUserName;
public string RemoteUserName
{
    get
   {
    return privateRemoteUserName;
   }
   set
   {
    privateRemoteUserName = value;
   }
   }
   private string privateRemoteAvatarUrl;
   public string RemoteAvatarUrl
   {
   get
   {
   return privateRemoteAvatarUrl;
   }
   internal set
   {
    privateRemoteAvatarUrl = value;
   }
}

Далее нам надо создать набор ChatMessages. Что нам нужно, так это вызывать событие каждый раз, когда изменяется набор (то есть добавлено сообщение) и мы хотим, чтобы LlistBox реагировал на данное событие. Мы можем создать все это, но структура облегчает нам задачу. Listbox уже знает, как реагировать на события PropertyChanged, и существует специальный тип ObservableCollection(of) , который запускает именно это событие каждый раз при изменении набора.

Это гораздо упрощает весь процесс,

private ObservableCollection
  privateMessageHistory;
   public ObservableCollection
    MessageHistory
     {
      get
      {
       return privateMessageHistory;
      }
      internal set
      {
       privateMessageHistory = value;
      }
     }

Теперь процесс отсылки сообщений очень прост, так как добавляется новое сообщение Chat, где текст взят из окна для сообщений и пользовательское имя взято из текущего имени пользователя (на данный момент оно задано явно)

public void SendMessage(string message)
{
    // Посылка на удаленный чат-сервер через сокеты
    MessageHistory.Add(new ChatMessage {Text = message, UserName = "Me"});
}

В качестве упражнения вы можете попробовать переслать сообщение по сети.

До того, как мы сможем посылать сообщения, нам понадобится осуществить соединение с удаленным пользователем посредством получения имени удаленного пользователя, его изображения и инициализировать просмотриваемый набор сообщений,

public void ConnectWithRemoteUser(string remoteUserNameParam)
{
      // На выполнение: 1) Привязать стэк сокетов для получения уведомлений о полученных сообщениях
      
   //    2) Привязка к удаленному изображению вместо явного задания
  
      RemoteUserName = remoteUserNameParam;
      RemoteAvatarUrl = "billg.jpg";
      MessageHistory = new ObservableCollection
  ();
    
       // Уведомление всех прослушивающих о том, что свойства были изменены
       if (PropertyChanged != null)
         
   PropertyChanged(this, new PropertyChangedEventArgs("RemoteUserName"));
       if (PropertyChanged != null)
      
      PropertyChanged(this, new
   PropertyChangedEventArgs("RemoteAvatarUrl"));
       if (PropertyChanged != null)
      
      PropertyChanged(this, new
   PropertyChangedEventArgs("MessageHistory"));
}

Наконец-то мы добавим метод, который поможет нам с имитацией обмена мгновенными сообщениями (но мы вызываем данный метод, когда находимся в режиме дизайнера, а не в то время, когда запущена программа)

public void PopulateWithDummyData()
{
    RemoteUserName = "BillG";
    RemoteAvatarUrl = "billg.jpg";
  
    MessageHistory = new ObservableCollection
  ();
     MessageHistory.Add(new ChatMessage {UserName = "BillG", Text = "How is your video going?"});
     MessageHistory.Add(new ChatMessage {UserName = "BillG", Text = "Hmmm....you there?"});
     MessageHistory.Add(new ChatMessage {UserName = "BillG", Text = "Hello???"});
     MessageHistory.Add(new ChatMessage {UserName = "Jesse", Text = "Sorry Bill - working on a video..."});
     MessageHistory.Add(new ChatMessage {UserName = "BillG", Text = "Oh - ok."});
}

Чтобы удостоверится в том, что информация находится в нужном месте в режиме дизайнера, мы протестируем конструктор ChatSession,

public ChatSession()
{
    if (HtmlPage.IsEnabled == false)
   {
    PopulateWithDummyData();
   }
 }

Привязка данных (DataBinding)

Расположив данные классы (вы создали сборку для проверки того, что все верно, не так ли?) мы теперь готовы к осуществлению привязки данных. Давайте выполним это в Blend (!)

Удостоверьтесь в том, что вы сохранили все файлы в Visual Studio 2008, но не закрывайте его, а просто переключитесь к Blend. Ответьте «да» на вопрос о принятии изменений. Под панелью проекта вы должны увидеть панель информации (Data), в пределах которой есть закладка, названная CLR Object. Нажмите на нее, и вы увидите Blend для Silverlight , который откроется для того, чтобы отобразить диалоговое окно, позволяющее вам добавлять CLR-объекты в качестве источника данных. Обратите внимание, что ChatMessage и ChatSession также в списке. Нажмите на ChatSession и затем нажмите OK, и в вашем списке Data появится Chat SessionDS.

Рисунок 5-25. Добавления ChatSession в качестве источника данных (Data Source)  

Теперь все очень просто - нажмите на RemoteUserName и перетащите его на поле с названием в верхней части окна дизайнера и бросьте его как это показано на рисунке 5-26

Рисунок 5-26. Привязка RemoteUserName к TextBlock при помощи перетаскивания

Как только вы его отпустите, Blend поймет, что вы привязываете данные, и спросит сначала о том, какое поле вы хотите привязать (угадывая то, что вы наверняка хотите привязать к Text), и затем предложит (если вы раскроете диалоговое окно) дополнительные опции, например такие , как двусторонняя привязка данных (тем самым, в случае обновления пользовательского интерфейса, изменения будут записаны в источник данных)

Рисунок 5-27. Создание привязки данных (DataBinding)

Когда вы нажмете OK пользовательский интерфейс сразу же будет обновлен привязанной информацией.  

Рисунок 5-28. Привязанная информация

К сожалению, ваш список не знает о том, как необходимо отображать Chat Message (и вправду, с чего ему это знать?). Но мы можем исправить это, используя шаблон данных (Data template),

<ListBox.ItemTemplate>
    <DataTemplate>
      <StackPanel Orientation="Horizontal" Margin="5">
        <TextBlock FontFamily="Comic Sans MS" FontSize="16" 
           Foreground="red" Text="{Binding UserName}"/>
         <TextBlock Text=": "/>
         <TextBlock FontFamily="Comic Sans MS" FontSize="16" 
           Text="{Binding Text}"/>
      </StackPanel>
    </DataTemplate>
  </ListBox.ItemTemplate>

 Это научит ListBox отображать каждый элемент списка, указывая на то, что каждый будет отображен в трех элементах TextBlock, выровненных горизонтально при помощи элемента StackPanel 

Рисунок 5-29. Шаблон в действии


Добавление шаблонов

Listbox и особенно кнопка Send выглядят не совсем так, как нам бы хотелось. Blend теперь предоставляет лучшее решение, которое мы рассмотрим в следующей статье, поэтому на данном этапе мы завершаем данную статью.

Скачать исходники примеров 

Jesse Liberty