Silverlight4配合WCF net.tcp实现在线聊天应用攻略2
2010-07-01 01:33 NicolasZhang 阅读(1875) 评论(14) 收藏 举报接上篇,完成了开发文档中的例子后我学习了一下小笨蛋写的例子,但是发现他并没有把应用写完,我就狗尾续貂吧再来说道说道。
上代码,在VS2010中创建一个Silverlight4的项目并使用ASP.NET网站承载。先实现服务端功能,由于要使用WCF net.tcp Activation服务所以在Web项目引用中添加对System.ServiceModel.Activation的引用。
首先在Web项目新建一个接口,定义WCF服务接口和回调接口用以规约服务端与客户端通信的方式:
IDuplexService.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.Threading;
namespace MyWebChat.Web
{
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "IDuplexService" in both code and config file together.
/// <summary>
/// WCF服务接口。定义Session模式为必须使用Session,定义回调契约类型为IChatCallBack
/// </summary>
[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IChatCallback))]
public interface IChatService
{
//定义操作契约
//IsOneWay = false,等待服务器完成对方法处理
//IsInitiating = true,启动Session会话,
//IsTerminating = false,设置服务器发送回复后不关闭会话
/// <summary>
/// 用户加入
/// </summary>
/// <param name="name">用户昵称</param>
/// <returns>返回当前在线用户数组</returns>
[OperationContract(IsOneWay = false, IsInitiating = true, IsTerminating = false)]
string[] Join(string name);
/// <summary>
/// 用户群发消息
/// </summary>
/// <param name="senderName">消息发送者昵称</param>
/// <param name="msg">消息内容</param>
[OperationContract(IsOneWay = true, IsInitiating = false, IsTerminating = false)]
void Say(string senderName, string msg);
/// <summary>
/// 用户发送私聊消息
/// </summary>
/// <param name="to">私聊接受者</param>
/// <param name="msg">消息内容</param>
[OperationContract(IsOneWay = true, IsInitiating = false, IsTerminating = false)]
void Whisper(string to, string msg);
//设置IsTerminating =true,用户离开后关闭Session
/// <summary>
/// 用户离开聊天室
/// </summary>
[OperationContract(IsOneWay = true, IsInitiating = false, IsTerminating = true)]
void Leave();
}
/// <summary>
/// 双向通信的回调接口
/// </summary>
public interface IChatCallback
{
/// <summary>
/// 客户端接收群发消息
/// </summary>
/// <param name="senderName"></param>
/// <param name="message"></param>
[OperationContract(IsOneWay = true)]
void Receive(string senderName, string message);
/// <summary>
/// 客户端接收私聊消息
/// </summary>
/// <param name="senderName">发送者昵称</param>
/// <param name="message">消息内容</param>
[OperationContract(IsOneWay = true)]
void ReceiveWhisper(string senderName, string message);
/// <summary>
/// 用户进入
/// </summary>
/// <param name="name">进入的用户昵称</param>
[OperationContract(IsOneWay = true)]
void UserEnter(string name);
/// <summary>
/// 用户离开
/// </summary>
/// <param name="name">离开的用户昵称</param>
[OperationContract(IsOneWay = true)]
void UserLeave(string name);
/// <summary>
/// 更新在线用户列表
/// </summary>
/// <param name="list">在线用户列表</param>
[OperationContract(IsOneWay = true)]
void UpdateOnlineUserList(string[] list);
}
/// <summary>
/// 设定消息的类型
/// </summary>
public enum MessageType { UserEnter, UserLeave, Receive, ReceiveWhisper, UpdateOnlineUserList };
/// <summary>
/// 定义一个本例的事件消息类. 创建包含有关事件的其他有用的信息的变量,只要派生自EventArgs即可。
/// </summary>
public class ChatEventArgs : EventArgs
{
public MessageType msgType;
public string name;
public string message;
}
}
接着是最重要的部分,创建一个Silverlight-enabled WCF Service用以实现刚才定义的服务端接口。小笨蛋的注释写的比较详细我又加了一些应该比较清楚了: IDuplexService.svc.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.Threading;
using System.ServiceModel.Activation;
namespace MyWebChat.Web
{
//// InstanceContextMode.PerSession 服务器为每个客户会话创建一个新的上下文对象。ConcurrencyMode.Multiple 异步的多线程实例
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession, ConcurrencyMode = ConcurrencyMode.Multiple)]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class ChatService : IChatService
{
private static Object syncObj = new Object();////定义一个静态对象用于线程部份代码块的锁定,用于lock操作
IChatCallback callback = null; //双向通信的回调接口
public delegate void ChatEventHandler(object sender, ChatEventArgs e);//定义用于把处理程序赋予给事件的委托。
public static event ChatEventHandler ChatEvent;//定义事件
static Dictionary<string, ChatEventHandler> chatters = new Dictionary<string, ChatEventHandler>();//创建一个静态Dictionary(表示键和值)集合(字典),用于记录在线成员,Dictionary<(Of <(TKey, TValue>)>) 泛型类
private string name;
private ChatEventHandler myEventHandler = null;
/// <summary>
/// 用户加入
/// </summary>
/// <param name="name">登录用户的昵称</param>
/// <returns>当前用户昵称列表</returns>
public string[] Join(string name)
{
bool userAdded = false;
myEventHandler = new ChatEventHandler(MyEventHandler);//将MyEventHandler方法作为参数传递给委托
lock (syncObj)//线程的同步性,同步访问多个线程的任何变量,利用lock(独占锁),确保数据访问的唯一性。
{
//判断在线用户列表中是否已经存在此用户昵称,昵称为空和null的情况可以从客户端判断
if (name != null && name != "" && !chatters.ContainsKey(name))
{
this.name = name;
chatters.Add(name, MyEventHandler);//把当前用户加入到在线用户列表中
userAdded = true;
}
}
if (userAdded)
{
/*
* 获取当前操作客户端实例的通道给IChatCallback接口的实例callback,此通道是一个定义为IChatCallback类型的泛类型,
* 通道的类型是事先服务契约协定好的双工机制。
*/
callback = OperationContext.Current.GetCallbackChannel<IChatCallback>();
//实例化事件消息类ChatEventArgs
ChatEventArgs e = new ChatEventArgs
{
msgType = MessageType.UserEnter,
name = name
};
BroadcastMessage(e);
UpdateOnlineUserList();//更新其他用户的在线用户列表
ChatEvent += myEventHandler;
string[] list = new string[chatters.Count];
//以下代码返回当前进入聊天室成员的称列表
lock (syncObj)
{
chatters.Keys.CopyTo(list, 0);//将字典中记录的用户信息复制到数组中返回。
}
return list;
}
else
{
return null;
}
}
/// <summary>
/// 广播消息
/// </summary>
/// <param name="e"></param>
private void BroadcastMessage(ChatEventArgs e)
{
switch (e.msgType)
{
//广播用户进入消息
case MessageType.UserEnter:
Say("系统消息", "欢迎[" + e.name + "]加入聊天室!");
break;
//广播用户离开消息
case MessageType.UserLeave:
Say("系统消息", "[" + e.name + "]已经离开聊天室!");
break;
default: throw new Exception();
}
}
/// <summary>
/// 更新客户端列表
/// </summary>
/// <returns></returns>
public string[] GetOnlineUserList()
{
string[] list = new string[chatters.Count];
//以下代码返回当前进入聊天室成员的称列表
lock (syncObj)
{
chatters.Keys.CopyTo(list, 0);//将字典中记录的用户信息复制到数组中返回。
}
return list;
}
/// <summary>
/// 对所有人说
/// </summary>
/// <param name="msg"></param>
public void Say(string senderName, string msg)
{
ChatEventArgs e = new ChatEventArgs
{
msgType = MessageType.Receive,
name = senderName,
message = msg
};
try
{
ChatEventHandler chatterTo;//创建一个临时委托实例
lock (syncObj)
{
foreach (var chatter in chatters)
{
//不再发送给发送者本人
if (chatter.Key != senderName)
{
chatterTo = chatter.Value; //查找成员字典中,找到要接收者的委托调用
chatterTo.BeginInvoke(this, e, new AsyncCallback(EndAsync), null);//异步方式调用接收者的委托调用
}
}
}
}
catch (KeyNotFoundException) { }
}
/// <summary>
/// 私聊信息
/// </summary>
/// <param name="to"></param>
/// <param name="msg"></param>
public void Whisper(string to, string msg)
{
ChatEventArgs e = new ChatEventArgs
{
msgType = MessageType.ReceiveWhisper,
name = this.name,
message = msg
};
try
{
ChatEventHandler chatterTo;//创建一个临时委托实例
lock (syncObj)
{
chatterTo = chatters[to]; //查找成员字典中,找到要接收者的委托调用
}
chatterTo.BeginInvoke(this, e, new AsyncCallback(EndAsync), null);//异步方式调用接收者的委托调用
}
catch (KeyNotFoundException) { }
}
/// <summary>
/// 用户加入
/// </summary>
public void Leave()
{
if (this.name == null)
return;
lock (syncObj)
{
chatters.Remove(this.name);
}
ChatEvent -= myEventHandler;
ChatEventArgs e = new ChatEventArgs
{
msgType = MessageType.UserLeave,
name = this.name
};
this.name = null;
BroadcastMessage(e);
UpdateOnlineUserList();//更新其他用户的在线用户列表
}
/// <summary>
/// 更新在线用户列表
/// </summary>
private void UpdateOnlineUserList()
{
ChatEventArgs e = new ChatEventArgs
{
msgType = MessageType.UpdateOnlineUserList
};
try
{
ChatEventHandler chatterTo;//创建一个临时委托实例
lock (syncObj)
{
foreach (var chatter in chatters)
{
chatterTo = chatter.Value; //查找成员字典中,找到要接收者的委托调用
chatterTo.BeginInvoke(this, e, new AsyncCallback(EndAsync), null);//异步方式调用接收者的委托调用
}
}
}
catch (KeyNotFoundException) { }
}
//回调,根据客户端动作通知对应客户端执行对应的操作
private void MyEventHandler(object sender, ChatEventArgs e)
{
try
{
switch (e.msgType)
{
case MessageType.Receive:
callback.Receive(e.name, e.message);
break;
case MessageType.ReceiveWhisper:
callback.ReceiveWhisper(e.name, e.message);
break;
case MessageType.UserEnter:
callback.UserEnter(e.name);
break;
case MessageType.UserLeave:
callback.UserLeave(e.name);
break;
case MessageType.UpdateOnlineUserList:
callback.UpdateOnlineUserList(GetOnlineUserList());
break;
}
}
catch
{
Leave();
}
}
//广播中线程调用完成的回调方法功能:清除异常多路广播委托的调用列表中异常对象(空对象)
private void EndAsync(IAsyncResult ar)
{
ChatEventHandler d = null;
try
{
//封装异步委托上的异步操作结果
System.Runtime.Remoting.Messaging.AsyncResult asres = (System.Runtime.Remoting.Messaging.AsyncResult)ar;
d = ((ChatEventHandler)asres.AsyncDelegate);
d.EndInvoke(ar);
}
catch
{
ChatEvent -= d;
}
}
}
}
需要注意的是此处用到delegate的BeginInvoke和EndInvoke进行多线程异步处理委托,所以需注意在操作静态对象时需要加锁。 完成之后得对Web.config进行相应修改:
Web.config
<?xml version="1.0"?>
<!--
For more information on how to configure your ASP.NET application, please visit
http://go.microsoft.com/fwlink/?LinkId=169433
-->
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
</system.web>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<netTcpBinding>
<binding name="tcpBindingNoSecurity">
<security mode="None" />
</binding>
</netTcpBinding>
</bindings>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"
multipleSiteBindingsEnabled="true" />
<services>
<service name="MyWebChat.Web.ChatService">
<endpoint address="net.tcp://localhost:4502/MyWebChat.Web/DuplexService.svc"
binding="netTcpBinding" bindingConfiguration="tcpBindingNoSecurity"
contract="MyWebChat.Web.IChatService" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
</services>
</system.serviceModel>
</configuration>
生成一下解决方案服务端的工作就完成了,接下来进行客户端的创建。 在Silverlight项目中添加服务引用,点击Discover便可获得刚才创建的WCF服务,设置命名空间为ChatServiceReference并引入。 若此步能正确引入此服务则说明刚才创建、配置的WCF服务正确无误,否则会遇到各种错误,这时请参考上一篇Silverlight4配合WCF net.tcp实现在线聊天应用攻略1中提到设置要点进行检查、修正并重新生成服务项目,直到正确引入为止。
创建一个ChatServiceClientManager类以统一控制WCF服务代理的使用。 ChatServiceClientManager.cs
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.Generic;
using MyWebChat.ChatServiceReference;
namespace MyWebChat.ServiceClient
{
public class ChatServiceClientManager
{
public static ChatServiceClient client = new ChatServiceClient(); //实例化一个WCF服务客户端
public static bool hasSignedIn = false;//记录是否已经登录
public static string currentUserName = "";//记录当前用户昵称
public static List<string> onlineUserList = new List<string>();//记录在线用户列表
/// <summary>
/// 调用WCF服务给指定昵称的客户端发送消息
/// </summary>
/// <param name="receiverName">接收者昵称</param>
/// <param name="messageText">消息内容</param>
/// <returns>消息发送后更新的显示</returns>
public static string SendMessage(string receiverName, string messageText)
{
if (receiverName == "所有人")
{
//异步调用服务端定义的Say方法
client.SayAsync(ChatServiceClientManager.currentUserName, messageText);
return "你对所有人说:" + messageText + Environment.NewLine;
}
else
{
//异步调用服务端定义的Whisper方法
client.WhisperAsync(receiverName, messageText);
return "你对" + receiverName + "悄悄说:" + messageText + Environment.NewLine;
}
}
}
}
添加一个Silverlight Child Window用来做登录界面:
LoginWindow.xaml
<controls:ChildWindow x:Class="MyWebChat.LoginWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"
Width="400" Height="255"
Title="登录" HasCloseButton="False" FontSize="12">
<Canvas x:Name="LayoutRoot" Background="Beige">
<TextBlock Text="Silverlight在线聊天室" Canvas.Left="64" Canvas.Top="53" FontSize="25"></TextBlock>
<TextBlock Text="昵称:" Canvas.Left="77" Canvas.Top="135" FontSize="15" Height="30" VerticalAlignment="Center"></TextBlock>
<TextBox x:Name="txtUserName" Height="30" Width="116" FontSize="16" Canvas.Left="129" Canvas.Top="132" KeyDown="txtUserName_KeyDown" ></TextBox>
<Button x:Name="btnSignIn" Content="登录" FontSize="15" Height="30" Width="65" Canvas.Left="251" Canvas.Top="132" Click="btnSignIn_Click"></Button>
</Canvas>
</controls:ChildWindow>
LoginWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using MyWebChat.ChatServiceReference;
using MyWebChat.ServiceClient;
namespace MyWebChat
{
public partial class LoginWindow : ChildWindow
{
public string actualUserName = "";//最终用户名
public LoginWindow()
{
InitializeComponent();
}
/// <summary>
/// 登录按钮方法
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSignIn_Click(object sender, RoutedEventArgs e)
{
//监听Join方法以获得返回的在线用户列表
ChatServiceClientManager.client.JoinCompleted += new EventHandler<JoinCompletedEventArgs>(client_JoinCompleted);
actualUserName = txtUserName.Text == string.Empty ? "测试用户" + DateTime.Now.ToString() : txtUserName.Text;
ChatServiceClientManager.client.JoinAsync(actualUserName);//异步调用Join方法加入聊天室
}
/// <summary>
/// 处理Join方法的返回值
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void client_JoinCompleted(object sender, JoinCompletedEventArgs e)
{
if (e.Result != null)
{
foreach (var userName in e.Result)
{
ChatServiceClientManager.onlineUserList.Add(userName);
}
Close();//成功登录后关闭此窗口
}
else
{
MessageBox.Show("已存在重名用户,请重新输入昵称!", "提示", MessageBoxButton.OK);
txtUserName.Text = string.Empty;
}
}
/// <summary>
/// 昵称输入框点击Enter即触发登录
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void txtUserName_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
btnSignIn_Click(null, null);
}
}
}
}
接下来开始制作聊天主窗体,界面做的比较烂,以后再改进吧。
MainPage.xaml
<UserControl x:Class="MyWebChat.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="600" d:DesignWidth="800">
<StackPanel x:Name="LayoutRoot" Background="White">
<StackPanel Orientation="Horizontal" Background="AliceBlue">
<ScrollViewer x:Name="scrollChatContentContainer" Height="500" Width="600">
<TextBlock x:Name="tbChatContent" Margin="0" Width="575" FontSize="15" TextWrapping="Wrap" SizeChanged="tbChatContent_SizeChanged">
</TextBlock>
</ScrollViewer>
<ListBox x:Name="lstOnlineUser" Height="500" Width="200" Background="LightGray" ScrollViewer.VerticalScrollBarVisibility="Auto" SelectionChanged="lstOnlineUser_SelectionChanged"></ListBox>
</StackPanel>
<StackPanel Orientation="Horizontal">
<StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock x:Name="tbMyName" FontSize="13"></TextBlock>
<TextBlock Text="对" Width="19" FontSize="13"></TextBlock>
<ComboBox x:Name="cbbSpeakTo" Width="200"></ComboBox>
<TextBlock Text="说" Width="19" TextAlignment="Center" FontSize="13"></TextBlock>
</StackPanel>
<TextBox x:Name="txtChatContentInput" VerticalScrollBarVisibility="Auto" TextWrapping="Wrap" Height="80" Width="600" FontSize="14" KeyDown="txtChatContentInput_KeyDown"></TextBox>
</StackPanel>
<Button x:Name="btnSend" Content="发送" Height="100" Width="200" FontSize="20" Click="btnSend_Click"></Button>
</StackPanel>
<TextBlock x:Name="reply" Margin="0,0,0,0" FontSize="18"></TextBlock>
</StackPanel>
</UserControl>
MainPage.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.ServiceModel;
using System.ServiceModel.Channels;
using MyWebChat.ChatServiceReference;
using System.Windows.Browser;
using MyWebChat.ServiceClient;
namespace MyWebChat
{
public partial class MainPage : UserControl
{
LoginWindow loginWin = null;//登录子窗体
public MainPage()
{
InitializeComponent();
// HtmlPage.RegisterScriptableObject("MainPage", this);
this.Loaded += new RoutedEventHandler(MainPage_Loaded);
}
/// <summary>
/// 主界面加载完成事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void MainPage_Loaded(object sender, RoutedEventArgs e)
{
//为各个事件添加监听
ChatServiceClientManager.client.ReceiveReceived += new EventHandler<ReceiveReceivedEventArgs>(client_ReceiveReceived);
ChatServiceClientManager.client.ReceiveWhisperReceived += new EventHandler<ReceiveWhisperReceivedEventArgs>(client_ReceiveWhisperReceived);
ChatServiceClientManager.client.UpdateOnlineUserListReceived += new EventHandler<UpdateOnlineUserListReceivedEventArgs>(client_UpdateOnlineUserListReceived);
//打开注册子窗体
loginWin = new LoginWindow();
loginWin.Closed += new EventHandler(login_Closed);
loginWin.Show();
}
//[ScriptableMember()]
/// <summary>
/// 用户退出方法,通知服务端用户退出聊天室
/// </summary>
public void UserQuit()
{
ChatServiceClientManager.client.LeaveAsync();
}
//收到私聊消息处理方法
void client_ReceiveWhisperReceived(object sender, ReceiveWhisperReceivedEventArgs e)
{
//此处不太明白为什么Error不为空时才是有效的呢???
if (e.Error == null)
{
tbChatContent.Text += "[" + e.senderName + "]悄悄对你说:" + e.message + Environment.NewLine;
}
}
//收到群发消息处理方法
void client_ReceiveReceived(object sender, ReceiveReceivedEventArgs e)
{
//此处不太明白为什么Error不为空时才是有效的呢???
if (e.Error == null)
{
string msg = e.senderName == "系统消息" ? e.senderName + ":" + e.message :
"[" + e.senderName + "]对所有人说:" + e.message;
tbChatContent.Text += msg + Environment.NewLine;
}
}
//收到更新在线用户列表处理方法
void client_UpdateOnlineUserListReceived(object sender, UpdateOnlineUserListReceivedEventArgs e)
{
e.list.Insert(0, "所有人");
lstOnlineUser.ItemsSource = e.list;
List<string> userListForSpeakTo = new List<string>();
userListForSpeakTo.AddRange(e.list);
userListForSpeakTo.Remove(ChatServiceClientManager.currentUserName);
cbbSpeakTo.ItemsSource = userListForSpeakTo;
}
//登录窗口关闭处理方法
void login_Closed(object sender, EventArgs e)
{
ChatServiceClientManager.hasSignedIn = true;
tbMyName.Text = loginWin.actualUserName;
ChatServiceClientManager.currentUserName = loginWin.actualUserName;
lstOnlineUser.ItemsSource = ChatServiceClientManager.onlineUserList;
}
//发送消息
private void btnSend_Click(object sender, RoutedEventArgs e)
{
//若用户未选择聊天对象,默认设置为对所有人说
if (cbbSpeakTo.SelectedValue == null)
{
cbbSpeakTo.SelectedIndex = 0;
}
//不能发送空消息
if (txtChatContentInput.Text.Trim() == string.Empty)
{
MessageBox.Show("不能发送空消息!", "提示", MessageBoxButton.OK);
txtChatContentInput.Focus();
}
else
{
tbChatContent.Text += ChatServiceClientManager.SendMessage(cbbSpeakTo.SelectedValue.ToString(), txtChatContentInput.Text);
txtChatContentInput.Text = string.Empty;
}
}
/// <summary>
/// 列表点击事件,点击在线用户列表后自动设置说话对象为所点击对象
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void lstOnlineUser_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
cbbSpeakTo.SelectedValue = lstOnlineUser.SelectedValue;
}
/// <summary>
/// 聊天框按键事件,监听快捷键Ctrl+Enter发送消息
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void txtChatContentInput_KeyDown(object sender, KeyEventArgs e)
{
ModifierKeys keys = Keyboard.Modifiers;
if (e.Key == Key.Enter && keys == ModifierKeys.Control)
{
btnSend_Click(null, null);
}
}
/// <summary>
/// 聊天内容展示区域尺寸变化事件,用来实现自动滚动到最底
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void tbChatContent_SizeChanged(object sender, SizeChangedEventArgs e)
{
scrollChatContentContainer.ScrollToVerticalOffset(tbChatContent.ActualHeight);
}
}
}
最后在App.xaml.cs中的Application_Exit方法中添加:
private void Application_Exit(object sender, EventArgs e)
{
if (ChatServiceClientManager.hasSignedIn == true)
{
//用户关闭网页时通知服务端用户离开并关闭连接
ChatServiceClientManager.client.LeaveAsync();
ChatServiceClientManager.client.CloseAsync();
}
}
终于大功告成,调试一下,程序启动后弹出登录框,登录后即可在聊天框内进行聊天了,可以自己多开几个浏览器进行测试。
下一步准备改进一下界面,并把现在传递的string类型的消息改为富文本的以支持用户设定的字体、字号等风格,当然没有表情也是不行的,回头再尝试做个窗口抖动什么的。
现在看程序写的比较乱而且基本是只追求功能实现,不知大牛们能不能给点改进的建议,应用点什么模式好使代码更优雅些,多谢支持!
浙公网安备 33010602011771号