zoukankan      html  css  js  c++  java
  • Silverlight4配合WCF net.tcp实现在线聊天应用攻略2

           接上篇,完成了开发文档中的例子后我学习了一下小笨蛋写的例子,但是发现他并没有把应用写完,我就狗尾续貂吧再来说道说道。

           上代码,在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类型的消息改为富文本的以支持用户设定的字体、字号等风格,当然没有表情也是不行的,回头再尝试做个窗口抖动什么的。

          现在看程序写的比较乱而且基本是只追求功能实现,不知大牛们能不能给点改进的建议,应用点什么模式好使代码更优雅些,多谢支持!

    源代码下载

  • 相关阅读:
    matlab : Nelder mead simplex 单纯形直接搜索算法;
    python: 基本知识(二)
    vim: 基本知识;
    C++: 带参数回调函数和不带参数的回调函数;
    android studio DrawerLayout(官方侧滑菜单)的简单使用1
    Android studio DrawerLayout示例
    android studio ViewPager
    团队开发之任务认领
    android studio ViewPager的简单使用
    Android studio 菜单(Menu)
  • 原文地址:https://www.cnblogs.com/chocstarfish/p/1768782.html
Copyright © 2011-2022 走看看