Expression Blend для разработчиков Silverlight - Создание пространства для ввода
ОГЛАВЛЕНИЕ
Создание пространства для ввода
Исследуя финальное приложение, указанное в начале данной статьи, мы можем заметить, что оно обладает прямоугольной областью с округлыми углами (рисунок 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. Шаблон в действии