Создание приложения Windows Forms : клиент чата на WCF
Для создания клиентского приложения выполним аналогичные пункту № 4 данной лабораторной работы действия.
Создаём новый проект в текущем решении (LWP 16). Для этого выполним следующие действия: Файл -> Создать -> Проект. В окне открывшемся окне «Создать проект», в поле Решение, выберем: Добавить в решение. В качестве проекта выберем Приложение Windows Forms. Имя будет: LWP 16- ChatClient. Жмём ОК.
Сразу же добавим необходимые библиотеки. Должен быть выбрать текущий проект в обозревателе решений (LWP 16- ChatClient). Выполним Проект -> Добавить ссылку... -> в окне Добавить ссылку переходим на вкладку . NET и ищем System . ServiceModel. Если ОС на которой запущена среда разработки Windows 7, добавим также ещё одну библиотеку: Microsoft Speech Object Library (вкладка COM):
Рис. 5. 2. Добавить ссылку: добавляем новую ссылку на библиотеку Microsoft Speech Object Library
Переименуем форму проекта клиентского приложений в обозревателе решений (ПКМ на иконке формы -> Переименовать). Новое имя формы будет LWP 16 MainClient. После переименования, автоматически изменится свойство ( Name ) формы.
Теперь изменим свойства формы LWP 16 MainClient:
Text | изменим с Form 1 на Чат на WCF ( C #) :: Клиент |
^ Поменяем заголовок формы (то, что отображается в шапке приложения слева).
MaximizeBox | изменим с True на False |
^ Уберём кнопку Развернуть.
Icon | изменим изображение (иконку) приложения |
^ Необходим файл значка *. ico.
FormBorderStyle | > изменим с Sizable на FixedDialog |
^ Сделаем окно «неизменяем» по размерам.
|
|
Size | изменим со значений 290; 290 на 300; 125 |
^ Поменяем размер формы.
Расставим элементы с панели инструментов как показано на рисунке ниже:
Рис. 5. 1. Расстановка элементов на форме приложения-клиента
Здесь у нас в первой группе (GroupBox) «Пользовательские данные»: один Label, один TextBox и Button.
Свойства элементов следующие:
GroupBox:
( Name): | GB_UserDetails |
Text: | Пользовательские данные |
Label:
( Name): | L_LoginInfo |
Text: | Введите имя для входа и нажмите на «Войти в чат» |
TextBox:
( Name): | TB_UserName |
Button:
( Name): | B_Login |
Text: | Войти в чат |
Во второй группе элементов один GroupBox и один ListBox:
GroupBox:
( Name): | GB_UserList |
Text: | Пользователи в сети |
ListBox:
( Name): | LB_Users |
Последняя группа содержит один RichTextBox, TextBox и две кнопки.
GroupBox:
( Name): | GB_MessageWindow |
Text: | Сообщения чата |
RichTextBox:
( Name): | RTB_Messages |
ReadOnly: | True |
Multiline | True |
TextBox:
( Name): | TB_SendMessage |
Button:
( Name): | B_Send |
Text: | Отправить |
Button:
( Name): | B_WakeUp |
Text: | ! |
Перейдём к редактированию кода формы. В начале файла с кодом для формы LWP 16 MainClient . cs объявим:
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Runtime.InteropServices;
using SpeechLib; // Для работы имитации голоса (в Windows XP не использовать)
|
|
Найдём:
namespace LWP16_ChatClient
{
Добавим после:
[ServiceContract(CallbackContract = typeof(ILWP16Service))]
public interface ILWP16Service // Интерфейс подключения к службе сервера
{
[OperationContract(IsOneWay = true)] // Операция не возвращает ответовное сообщение
void Join(string memberName); // Объявление метода присоединения к чату (по имени)
[OperationContract(IsOneWay = true)]
void SendMessage(string memberName, string message); // Объявление метода отсылки сообщения (по имени и тексту)
[OperationContract(IsOneWay = true)]
void WakeUp(string memberName, string wakeup); // Объявление метода отсылки сообщения (по имени и тексту)
[OperationContract(IsOneWay = true)]
void Leave(string memberName); // Объявление метода выхода из чата (по имени)
[OperationContract(IsOneWay = true)]
void ImageFF(string memberName, Bitmap image); // Объявление метода выхода из чата (по имени)
}
public interface ILWP16Channel : ILWP16Service, IClientChannel
{
}
Найдём:
public partial class LWP16MainClient : Form
{
Заменим на:
public partial class LWP16MainClient : Form, ILWP16Service
{
[DllImport("kernel32.dll")]
public static extern bool Beep(int BeepFreq, int BeepDuration);
private delegate void UserJoined(string name);
private delegate void UserSendMessage(string name, string message);
private delegate void UserWakeUp(string name, string wakeup);
private delegate void UserLeft(string name);
private static event UserJoined NewJoin; // Экземпляр события присоединения к чату через делегат
|
|
private static event UserSendMessage MessageSent; // Экземпляр события отсылки сообщения через делегат
private static event UserWakeUp NewWakeUp; // Экземпляр события сообщения типа: "разбудить чат"
private static event UserLeft RemoveUser; // Экземпляр события выхода из чата через делегат
private string userName; // Переменная имени пользователя в чате
private ILWP16Channel channel; // Экземпляр интерфейса чата для канала
// class ServiceModel.DuplexChannelFactory<TChannel>
private DuplexChannelFactory<ILWP16Channel> factory; // Объект средства приёма и передаче "дуплексных" сообщений по каналам в обе стороны
Метод LWP16MainClient() измени так:
public LWP16MainClient()
{
InitializeComponent();
this.AcceptButton = B_Login; // Привязываем событие Нажатия Enter с кнопкой "Войти в чат"
}
После добавим:
public LWP16MainClient(string userName)
{
this.userName = userName;
}
// Метод присоединения к чату (по имени)
void LWP16Client_NewJoin(string name)
{
RTB_Messages.AppendText("\r\n");
RTB_Messages.AppendText(name + " присоединился: [" + DateTime.Now.ToString() + "]"); // Добавляем в RichTextBox строчку c именем и датой входа пользователя
LB_Users.Items.Add(name); // Добавляем нового пользователя в ListBox
}
// Метод отсылки сообщения (по имени и тексту)
|
|
void LWP16Client_MessageSent(string name, string message)
{
if (!LB_Users.Items.Contains(name)) // Если имени нет в ListBox при получении сообщения в RichTextBox
{
LB_Users.Items.Add(name); // Добавляет нового пользователя в ListBox
}
RTB_Messages.AppendText("\r\n");
RTB_Messages.AppendText(name + " говорит: " + message + " [" + DateTime.Now.ToString() + "]"); // Добавляем в RichTextBox строчку с именем и сообщением
if (message == "WakeUp") { Beep(500, 100); }
}
// Метод присоединения к чату (по имени)
void LWP16Client_WakeUp(string name, string wakeup)
{
if (!LB_Users.Items.Contains(name)) // Если имени нет в ListBox при получении сообщения в RichTextBox
{
LB_Users.Items.Add(name); // Добавляет нового пользователя в ListBox
}
RTB_Messages.AppendText("\r\n");
RTB_Messages.AppendText(name + " попытался разбудить чат: [" + DateTime.Now.ToString() + "]"); // Добавляем в RichTextBox строчку c именем и датой входа пользователя
if (wakeup == "WakeUp")
{
Beep(500, 100);
Beep(500, 100);
SpVoice voice = new SpVoice();
voice.Speak("Wake Up Mate", SpeechVoiceSpeakFlags.SVSFDefault);
}
}
// Метод выхода из чата (по имени)
void LWP16Client_RemoveUser(string name)
{
try
{
RTB_Messages.AppendText("\r\n");
RTB_Messages.AppendText(name + " вышел: [" + DateTime.Now.ToString() + "]"); // Добавляем в RichTextbox строчку с именем и датой выхода пользователя
LB_Users.Items.Remove(name); // Удаляем по имени из ListBox
}
catch (Exception ex)
{
System.Diagnostics.Trace.WriteLine(ex.ToString());
}
}
void Online(object sender, EventArgs e)
{
RTB_Messages.AppendText("\r\nВ сети: " + this.userName);
}
void Offline(object sender, EventArgs e)
{
RTB_Messages.AppendText("\r\nНе в сети: " + this.userName);
}
#region ILWP16Service Основные методы
public void Join(string memberName)
{
if (NewJoin != null)
{
NewJoin(memberName);
}
}
public void SendMessage(string memberName, string message)
{
if (MessageSent != null)
{
MessageSent(memberName, message);
}
}
public void WakeUp(string memberName, string wakeup)
{
if (NewWakeUp != null)
{
NewWakeUp(memberName, wakeup);
}
}
public new void Leave(string memberName)
{
if (RemoveUser != null)
{
RemoveUser(memberName);
}
}
#endregion
Перейдём к событиям формы. Для кнопки «Войти в чат» событие Click будет содержать код:
private void B_Login_Click(object sender, EventArgs e)
{
if (!string.IsNullOrEmpty(TB_UserName.Text.Trim()))
{
try
{
NewJoin += new UserJoined(LWP16Client_NewJoin); // Переопределяем вызов метода подключения к чату через экзмепляр события
MessageSent += new UserSendMessage(LWP16Client_MessageSent); // Переопределяем вызов метода отсылки сообщения в чат через экзмепляр события
NewWakeUp += new UserWakeUp(LWP16Client_WakeUp); // Переопределяем вызов метода "разбудить чат" в чат через экзмепляр события
RemoveUser += new UserLeft(LWP16Client_RemoveUser); // Переопределяем вызов метода выхода из чата через экзмепляр события
channel = null;
this.userName = TB_UserName.Text.Trim(); // Удаляем пробелы из имени пользователя
// class ServiceModel.InstanceContext
InstanceContext context = new InstanceContext(new LWP16MainClient(TB_UserName.Text.Trim()));
factory = new DuplexChannelFactory<ILWP16Channel>(context, "ChatEndPoint"); // Получаем данные из app.config и передаём данные duplex-каналу
channel = factory.CreateChannel(); // Создаём канал и передаём его экземпляру интерфейса чата
// class ServiceModel.IOnlineStatus
IOnlineStatus status = channel.GetProperty<IOnlineStatus>(); // Определяем экземпляр для индикации доступности объекта по каналу
status.Offline += new EventHandler(Offline); // Вызов метода Offline (если в чате больше никого)
status.Online += new EventHandler(Online); // Вызов метода Online (если в чате больше одного пользователя)
channel.Open(); // Открываем канал
channel.Join(this.userName); // Вызываем метод Join() с текущим именем пользователя введённым в TB_Username
GB_MessageWindow.Enabled = true; // Включаем группу "Сообщения чата"
GB_UserList.Enabled = true; // Включаем группу "Список пользователей"
GB_UserDetails.Enabled = false; // Гасим группу "Данные для входа"
this.AcceptButton = B_Send; // Enter = "Отослать"
RTB_Messages.AppendText("*****************************ДОБРО ПОЖАЛОВАТЬ В ЧАТ*****************************\r\n");
TB_SendMessage.Select();
TB_SendMessage.Focus();
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
}
События Click кнопок «Отправить» и «!»:
private void B_Send_Click(object sender, EventArgs e)
{
channel.SendMessage(this.userName, TB_SendMessage.Text.Trim()); // Вызываем метод SendMessage() и отсылаем имя пользователя и сообщение
TB_SendMessage.Clear(); // Очищаем TB_SendMessage и далее передаём фокус на элемент и делаем его активным
TB_SendMessage.Select();
TB_SendMessage.Focus();
}
private void B_WakeUp_Click(object sender, EventArgs e)
{
channel.WakeUp(this.userName, "WakeUp"); // Вызываем метод WakeUp() и отсылаем имя пользователя и текст "WakeUp"
TB_SendMessage.Clear(); // Очищаем TB_SendMessage и далее передаём фокус на элемент и делаем его активным
TB_SendMessage.Select();
TB_SendMessage.Focus();
}
Событие FormClosing формы LWP16MainClient:
private void LWP16MainClient_FormClosing(object sender, FormClosingEventArgs e)
{
try
{
if (channel != null)
{
channel.Leave(this.userName); // Если закрываем форму, вызваем метод Leave() и удаляем пользователя
channel.Close(); // Закрываем канал между сервером и клиентом
}
if (factory != null)
{
factory.Close(); // Закрываем duplex-канал
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
Теперь разъясним принцип работы всего того что было тут наворочено. После запуска приложения-сервера, происходит инициализация канала по определённому адресу. Был выбран локальный адрес и порт 5433, а также абсолютное имя пространства имён LWP 16_ ChatServer для адресации по этому порту. Имя для адресации в строке конфигурации клиента:
<endpoint name="ChatEndPoint" address="net.p2p://LWP16-Сhat/LWP16_ChatServer"
И...
<custom address="net.tcp://localhost:5433/LWP16_ChatServer"
Может быть любым, но! Главное чтобы оно совпадало с серверной строкой адреса (в файле конфигурации приложения-сервера). То есть, запущенное приложение-клиент должно содержать в файле конфигурации этот абсолютный адрес.
После ввода имени, становится доступной кнопка «Войти в чат». После нажатия на кнопку, в приложении-клиенте происходит вызов (через делегат и событие) метода интерфейса: Join(string memberName), содержащее переданное из текстового поля строку с именем. И далее выполняется: LWP16Client_NewJoin(string name), где в RichTextBox добавляется запись о присоединении пользователя с таким-то именем к чату и временем присоединения. Имя также добавляется в ListBox. Если пользователь в уже не один, срабатывает метод Online (), которые сразу же оповещает и пользователя и другого пользователя о том, что они «в сети». Метод срабатывает в том случае, если в сети больше одного пользователя, после нажатия кнопки «Войти в чат» пользователя ещё не вошедшего в чат. Вызывает событие после получения по каналу «индикатора события доступности»:
IOnlineStatus status = channel.GetProperty<IOnlineStatus>();
Подключение третьего пользователя не вызывает срабатывание метода у двух предыдущих, но «в сети» отображается у нового пользователя, которому выдаётся оповещение о том что в канале он не один. Также любое подключение вызывает у всех клиентов срабатывание события NewJoin и следовательно Join (<имя подключившегося>).
Сообщение отсылается по такому же событийному механизму, но содержит также текст сообщения, которое пользователь набрал у себя в клиенте. Отправка идёт после нажатия кнопки «Отправить». В ListBox новый пользователи чата добавляются лишь по факту совершения события и появления записи с именем в RichTextBox, потом если в сети один клиент, то об имени уже находящегося в канале пользователя новый клиент узнает только после совершения тем пользователем действий.
Аналогичным образом работает событие WakeUp, которое передаёт чату имя пользователя нажавшего кнопку «!» и текст «WakeUp», который не отображается в чате. После получения такого сообщения, все клиенты издают системный звук (чтобы разбудить тех кто находится в чате). А также, если ОС на которой запущен клиент это Windows Vista или Windows 7, то приложение проговаривает заранее заготовленную фразу. Также системный звук раздаётся, если написать в чат непосредственно слово WakeUp.
Выход из чата (через закрытие формы и вызов события FormClosing) тоже выдаёт оповещения, а также происходит удаление имени вышедшего из ListBox всех клиентов.
Наше приложение-клиент готово. Можно компилировать и проверять работоспособность.
Завершающая часть
Компилируем приложения (Release) и запускаем. В качестве основного запускаемого объекта решения (Запускаемый проект) ставим приложение сервер. Стартуем сервер нажатием кнопки «Запуск». Запускаем копию приложения клиента. Вводим имя и жмём «Войти в чат»:
Рис. 6. 1. Результат работы приложения клиента после запуска сервера: вход первого пользователя в чат и отсылка сообщения
Запускаем второй клиент, входим в чат и пишем сообщение:
Рис. 6. 2. Результат работы приложения клиента после запуска сервера: вход второго пользователя в чат и его действия
Закрывает первый клиент, запускаем третий клиент и пишем со второго и третьего клиентов:
Рис. 6. 3. Результат работы приложения клиента после запуска сервера: вход третьего пользователя в чат и его действия
Дата добавления: 2019-09-13; просмотров: 430; Мы поможем в написании вашей работы! |
Мы поможем в написании ваших работ!