- 浏览: 2084478 次
- 性别:
- 来自: 深圳
文章分类
最新评论
-
wahahachuang5:
web实时推送技术使用越来越广泛,但是自己开发又太麻烦了,我觉 ...
细说websocket - php篇 -
wahahachuang8:
挺好的,学习了
细说websocket - php篇 -
jacking124:
学习了!支持你,继续
初窥Linux 之 我最常用的20条命令 -
aliahhqcheng:
应该是可以实现的,没有看过源码。你可以参考下:http://w ...
Jackson 框架,轻易转换JSON
WPF+WCF一步一步打造音频聊天室(二):文字聊天和白板共享
这篇文章将讲述实现WPF的UI和WCF中的双工通信。实现文字部分的聊天功能和实现共享白板的功能。
画WPF的界面其实是一件麻烦的事情。虽然WPF和WindowsForm一样,能将控件拖到哪,它就在哪。我们在开发asp.net项目的时候用从原始的table布局,到现在流行的div+css布局。这些都需要设计人员的仔细设计。这个程序的布局我采用Grid和StackPanel两种方式。Gird类似html的表格布局,StackPanel就就像它的字面意思“堆栈面板”。
WPF的UI实现
首先新建一个wpf的应用程序,改名为ZqlChart。添加一个UserControl,用来实现登陆窗体,这个是用了StackPanel进行布局。XAML代码如下:
<UserControl x:Class="ZqlChart.LoginControl" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Height="210" Width="350" Loaded="UserControl_Loaded"> <StackPanel> <Border Height="220" BorderBrush="#FFFFFFFF" BorderThickness="2,2,2,0" CornerRadius="5,5,0,0"> <Border.Background> <LinearGradientBrush EndPoint="0.713,0.698" StartPoint="0.713,-0.139"> <GradientStop Color="#FFFFFFFF" Offset="0.933"/> <GradientStop Color="LightBlue" Offset="0.337"/> </LinearGradientBrush> </Border.Background> <StackPanel Name="infoPanel" Orientation="Vertical" Margin="10,10,10,10"> <StackPanel Name="typePanel" Orientation="Horizontal"> <RadioButton Name="chatTypeServer" FontSize="24" Margin="80,0,20,0" Checked="chatTypeServer_Checked" VerticalContentAlignment="Center">服务端</RadioButton> <RadioButton Name="chatTypeClient" FontSize="24" Checked="chatTypeClient_Checked" VerticalContentAlignment="Center">客户端</RadioButton> </StackPanel> <StackPanel Name="serverPanel" Orientation="Horizontal" Margin="0,10,0,0"> <Label Name="lblServer" FontSize="20" Width="120" HorizontalContentAlignment="Right" VerticalContentAlignment="Center">服务端:</Label> <TextBox Height="30" Name="txtServer" Width="160" FontSize="20" VerticalContentAlignment="Center" /> </StackPanel> <StackPanel Name="usernamePanel" Orientation="Horizontal" Margin="0,10,0,10"> <Label Name="lblUserName" FontSize="20" Width="120" HorizontalContentAlignment="Right">用户名:</Label> <TextBox Height="30" Name="txtUserName" Width="160" FontSize="20" VerticalContentAlignment="Center" /> </StackPanel> <StackPanel Name="buttonPanel" Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center"> <Button Name="btnLogin" Width="120" FontSize="20" Margin="10,10,10,10" Click="btnLogin_Click">连接</Button> <Button Name="btnCancel" Width="120" FontSize="20" Margin="10,10,10,10" Click="btnCancel_Click">取消</Button> </StackPanel> </StackPanel> </Border> </StackPanel> </UserControl>界面效果如下:
聊天的主界面,如下图:
大框架是3行3列。XAML代码如下:
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Name="ZqlChartMainWindow" x:Class="ZqlChart.ZqlChartWindow" Title="麒麟语音聊天室” Height="600" Width="800" Background="#FF3B3737" Loaded="Window_Loaded" MinWidth="800" MinHeight="500"> <Grid x:Name="LayoutRoot" > <Grid.RowDefinitions> <RowDefinition Height="50" /> <RowDefinition Height="261" /> <RowDefinition Height="250" /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="150" /> <ColumnDefinition Width="580*" /> <ColumnDefinition Width="48" /> </Grid.ColumnDefinitions> <Border Name="BorderUsersList" Grid.Column="0" Grid.Row="1" Grid.RowSpan="2" CornerRadius="8,8,8,8" Background="LightBlue" BorderThickness="0,0,4,4" > <ListView Name="lvUsers" Margin="10" FontSize="20"> <ListView.BitmapEffect> <DropShadowBitmapEffect /> </ListView.BitmapEffect> </ListView> </Border> <Border Name="BorderEditingType" Grid.ColumnSpan="3" CornerRadius="8,8,8,8" Background="LightBlue" BorderThickness="0,4,4,4"> <StackPanel Orientation="Horizontal" VerticalAlignment="Center"> <Button Margin="0,0,0,0" Height="28" Width="121" Click="Button_Click" Background="White"> 与其语音聊天
</Button> <RadioButton Name="rbInk" Content="墨水" Margin="15,0,0,0" VerticalAlignment="Center" FontSize="20" IsChecked="True" Tag="{x:Static InkCanvasEditingMode.Ink}" Click="rbInkType_Checked"> </RadioButton> <RadioButton Name="rbEraserByStroke" Content="一笔一笔清除" Margin="15,0,0,0" VerticalAlignment="Center" FontSize="20" Tag="{x:Static InkCanvasEditingMode.EraseByStroke}" Click="rbInkType_Checked"> </RadioButton> <RadioButton Name="rbEraserByPoint" Content="一点一点清除" Margin="15,0,0,0" VerticalAlignment="Center" FontSize="20" Tag="{x:Static InkCanvasEditingMode.EraseByPoint}" Click="rbInkType_Checked"> </RadioButton> <TextBlock Margin="25,0,10,0" VerticalAlignment="Center" FontSize="20" >选择颜色:</TextBlock> <Button Margin="0,0,0,0" Background="White" Height="28" Width="64" Click="OnSetFill"> <Rectangle Width="54" Height="20" Stroke="Black" StrokeThickness="2"> <Rectangle.Fill> <SolidColorBrush Color="{Binding ElementName=ZqlChartMainWindow, Path=FillColor}" /> </Rectangle.Fill> </Rectangle> </Button> </StackPanel> </Border> <Border Name="BorderInkCanvas" Grid.Column="1" Grid.Row="1" Background="LightBlue" BorderThickness="4,4,4,4" CornerRadius="8,8,8,8" Grid.ColumnSpan="2"> <InkCanvas x:Name="inkCanv" Margin="10" Background="White" StrokeCollected="inkCanv_StrokeCollected" StrokeErasing="inkCanv_StrokeErasing" StrokeErased="inkCanv_StrokeErased" VerticalAlignment="Top" > </InkCanvas> </Border> <Border Name="BorderInkMessage" Grid.Column="1" Grid.Row="2" Background="LightBlue" BorderThickness="0,0,4,4" CornerRadius="8,8,8,8" Grid.ColumnSpan="2"> <Grid > <Grid.RowDefinitions> <RowDefinition /> <RowDefinition Height="30" /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="500*" /> <ColumnDefinition Width="62*" /> <ColumnDefinition Width="62*" /> </Grid.ColumnDefinitions> <Border Grid.Column="1" Grid.Row="1" Background="LightBlue" BorderThickness="4,4,4,4" CornerRadius="8,8,8,8" > <Button Content="发送" Height="23" Name="btnSend" Click="btnSend_Click" /> </Border> <Border Grid.ColumnSpan="3" Background="LightBlue" BorderThickness="4,4,4,4" CornerRadius="8,8,8,8" > <TextBox Name="txtAllMessage" > <TextBox.BitmapEffect> <DropShadowBitmapEffect /> </TextBox.BitmapEffect> </TextBox> </Border> <Border Grid.Row="1" Background="LightBlue" BorderThickness="4,4,4,4" CornerRadius="8,8,8,8" > <TextBox Grid.Row="1" Name="txtMessage" /> </Border> <Border Grid.Column="2" Grid.Row="1" Background="LightBlue" BorderThickness="4,4,4,4" CornerRadius="8,8,8,8" > <Button Content="关闭" Name="btnLeave" Height="23" FontSize="10" Click="btnLeave_Click"> </Button> </Border> </Grid> </Border> <Canvas Name="loginCanvas" Grid.Column="1" Grid.Row="1" Width="500" Height="300" VerticalAlignment="Top" HorizontalAlignment="Center" Margin="39,78,41,0" Grid.RowSpan="2" /> </Grid> </Window> 窗体就设计好了。
WCF双工通信:
双工通信能允许服务通知用户当前的进度情况。我们可以通过使用指定CallbackContract的ServiceContract属性的服务使用双工,如服务器端的代码如下:
[ServiceContract(CallbackContract = typeof(IService1Callback))] public interface IService1 { [OperationContract] string GetData(int value); } [ServiceContract] public interface IService1Callback { [OperationContract] void Reply(string message); }
客户端代码:
class Program { static void Main(string[] args) { var callback = new Service1Callback(); var proxy = new Service1Client(new InstanceContext(callback)); Console.WriteLine(proxy.GetData(42)); Console.ReadLine(); } } class Service1Callback : IService1Callback { public void Reply(string message) { Console.WriteLine(message); } }
这篇文章中我将利用WCF的双工通信实现文字聊天的功能和共享白板的功能。
定义协议:
public interface IZqlChartService { [OperationContract()] bool Join(ChatUser chatUser); [OperationContract()] void Leave(ChatUser chatUser); [OperationContract] void SendBroadcastMessage(string strUserName, string message); [OperationContract()] bool IsUserNameTaken(string strUserName); [OperationContract()] void SendInkStrokes(MemoryStream memoryStream); }
定义回调:
public interface IZqlChartServiceCallback { [OperationContract(IsOneWay = true)] void NotifyMessage(string message); [OperationContract(IsOneWay = true)] void UpdateUsersList(List<ChatUser> listChatUsers); [OperationContract(IsOneWay = true)] void OnInkStrokesUpdate(ChatUser chatUser, byte[] bytesStroke); [OperationContract(IsOneWay = true)] void ServerDisconnected(); } 实现服务:
public class ZqlChartService : IZqlChartService { public static Dictionary<IZqlChartServiceCallback, ChatUser> s_dictCallbackToUser = new Dictionary<IZqlChartServiceCallback, ChatUser>(); public ZqlChartService() { } public bool Join(ChatUser chatUser) { IZqlChartServiceCallback client = OperationContext.Current.GetCallbackChannel<IZqlChartServiceCallback>(); if (s_dictCallbackToUser.ContainsValue(chatUser) == false) { s_dictCallbackToUser.Add(client, chatUser); } foreach (IZqlChartServiceCallback callbackClient in s_dictCallbackToUser.Keys) { callbackClient.UpdateUsersList(s_dictCallbackToUser.Values.ToList()); } return true; } public void Leave(ChatUser chatUser) { IZqlChartServiceCallback client = OperationContext.Current.GetCallbackChannel<IZqlChartServiceCallback>(); if (s_dictCallbackToUser.ContainsKey(client)) { s_dictCallbackToUser.Remove(client); } foreach (IZqlChartServiceCallback callbackClient in s_dictCallbackToUser.Keys) { if (chatUser.IsServer) { if (callbackClient != client) { //server user logout, disconnect clients callbackClient.ServerDisconnected(); } } else { //normal user logout callbackClient.UpdateUsersList(s_dictCallbackToUser.Values.ToList()); } } if (chatUser.IsServer) { s_dictCallbackToUser.Clear(); } } public bool IsUserNameTaken(string strNickName) { foreach (ChatUser chatUser in s_dictCallbackToUser.Values) { if (chatUser.NickName.ToUpper().CompareTo(strNickName) == 0) { return true; } } return false; } public void SendInkStrokes(MemoryStream memoryStream) { IZqlChartServiceCallback client = OperationContext.Current.GetCallbackChannel<IZqlChartServiceCallback>(); foreach (IZqlChartServiceCallback callbackClient in s_dictCallbackToUser.Keys) { if (callbackClient != OperationContext.Current.GetCallbackChannel<IZqlChartServiceCallback>()) { callbackClient.OnInkStrokesUpdate(s_dictCallbackToUser[client], memoryStream.GetBuffer()); } } } public void SendBroadcastMessage(string clientName, string message) { IZqlChartServiceCallback client = OperationContext.Current.GetCallbackChannel<IZqlChartServiceCallback>(); if (client != null) { foreach (IZqlChartServiceCallback callbackClient in s_dictCallbackToUser.Keys) { if (callbackClient != OperationContext.Current.GetCallbackChannel<IZqlChartServiceCallback>()) { callbackClient.NotifyMessage(clientName + ": " + message); } } } } }
客户端:
[System.Diagnostics.DebuggerStepThroughAttribute()] [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")] public partial class ZqlChartServiceClient : System.ServiceModel.DuplexClientBase<IZqlChartService>, IZqlChartService { public ZqlChartServiceClient(System.ServiceModel.InstanceContext callbackInstance) : base(callbackInstance) { } public ZqlChartServiceClient(System.ServiceModel.InstanceContext callbackInstance, string endpointConfigurationName) : base(callbackInstance, endpointConfigurationName) { } public ZqlChartServiceClient(System.ServiceModel.InstanceContext callbackInstance, string endpointConfigurationName, string remoteAddress) : base(callbackInstance, endpointConfigurationName, remoteAddress) { } public ZqlChartServiceClient(System.ServiceModel.InstanceContext callbackInstance, string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress) : base(callbackInstance, endpointConfigurationName, remoteAddress) { } public ZqlChartServiceClient(System.ServiceModel.InstanceContext callbackInstance, System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) : base(callbackInstance, binding, remoteAddress) { } public bool Join(ZqlChartObjects.ChatUser chatUser) { return base.Channel.Join(chatUser); } public void Leave(ZqlChartObjects.ChatUser chatUser) { base.Channel.Leave(chatUser); } public bool IsUserNameTaken(string strUserName) { return base.Channel.IsUserNameTaken(strUserName); } public void SendInkStrokes(System.IO.MemoryStream memoryStream) { base.Channel.SendInkStrokes(memoryStream); } public void SendBroadcastMessage(string strUserName, string message) { base.Channel.SendBroadcastMessage(strUserName, message); } }
客户端回调类:
public class ClientCallBack : IZqlChartServiceCallback { public static ClientCallBack Instance; private SynchronizationContext m_uiSyncContext = null; private ZqlChartWindow m_mainWindow; //ActiveCallWindow _activeCallForm; //CallManager _callManager; public ClientCallBack(SynchronizationContext uiSyncContext, ZqlChartWindow mainWindow) { m_uiSyncContext = uiSyncContext; m_mainWindow = mainWindow; } public void OnInkStrokesUpdate(ZqlChartObjects.ChatUser chatUser, byte[] bytesStroke) { SendOrPostCallback callback = delegate(object state) { m_mainWindow.OnInkStrokesUpdate(state as byte[] ); }; m_uiSyncContext.Post(callback, bytesStroke); SendOrPostCallback callback2 = delegate(object objchatUser) { m_mainWindow.LastUserDraw(objchatUser as ZqlChartObjects.ChatUser); }; m_uiSyncContext.Post(callback2, chatUser); } public void UpdateUsersList(List<ZqlChartObjects.ChatUser> listChatUsers) { SendOrPostCallback callback = delegate(object objListChatUsers) { m_mainWindow.UpdateUsersList(objListChatUsers as List<ZqlChartObjects.ChatUser>); }; m_uiSyncContext.Post(callback, listChatUsers); } public void ServerDisconnected() { SendOrPostCallback callback = delegate(object dummy) { m_mainWindow.ServerDisconnected(); }; m_uiSyncContext.Post(callback, null); } public void NotifyMessage(string message) { SendOrPostCallback callback = delegate(object dummy) { m_mainWindow.NotifyMessage(message); }; m_uiSyncContext.Post(callback, message); } public bool AcceptCall(string username) { //调獭?用?线?程ì必?须?为a STA,?因皑?为a许í多à UI 组哩?件t都?需è要癮。£ return MessageBox.Show(String.Format("Accep call from \"{0}\" ", username), "Incomming Call", MessageBoxButton.YesNo, MessageBoxImage.Information) == MessageBoxResult.Yes; } } 效果:
1、服务端登陆:
2、客户端登录:
3、文字聊天
4、共享白板:
总结:这篇文章实现了WPF的UI界面以及文字聊天和共享白板的功能。下一篇文章中将在此基础上实现语音通话的功能。
参考文章:
WXWinter的Silverlight例子_多人手写讨论板
Tim Callaghan, Alvin Lim的DrawMeWCF
附代码下载:http://files.cnblogs.com/zhuqil/ZqlChart.rar
相关推荐
WPF+WCF一步一步打造音频聊天室,里面还带有一个说明文档。
wpf, wcf 的简单程序示例,适合首次学习wpf和wcf技术的菜鸟们。
wpf
WPF + WCF画板聊天程序,即时通讯
wpf+wcf+ef搭建的框架,初学者适用
ASP+WPF+WCF,包含ASP资料、WPF示例、WCF示例,具体内容自己研究好了
此示例程序中集成了WPF、WCF、WWF和LINQ,是学习.net 30.和.net 3.5的较好的范例
WPF+WCF做的大文件上传控件,在WPF客户端把大文件分割成二进制数组,每次往服务器上写100K,可断点续传。页面做的比较丑,没认真设计,只为实现功能。另外还有一个Silverlight+WCF版的。
通讯用的WCF,客户端用WPF写的一个聊天Demo
EF+WCF+WPF(MVVM)框架简单示例
一个学习WCF+LINQ+WPF很好的一个范例
一个WPF+WCF的例子,在WPF很少的情况下,学习学习。本不想收分的,但我也想下其它的东东,所以。。。
WCF & WPF 聊天程序源码 For those that have read some of my other CodeProject articles you will probably know, that I am not shy about trying out new technologies. But one good thing about that is ...
运用wpf wcf 实现的聊天室的功能,非常适合学习wcf的
WPF、WCF、WF打造Hello World程序
最简单的WPF+MVVM项目最简单的WPF+MVVM项目最简单的WPF+MVVM项目最简单的WPF+MVVM项目
wpf+wcf写的聊天室。大家可以下载看看。,值得学习。
使用WPF-WCF创建的网络绘图板聊天程序。 供大家参考。
C# WPF+MVVM+EF+多语言实例源码
WPF+EF+SQLite从建项目到写入数据开发环境配置 运行环境:VS2019 数据库工具:sqlitestudio 用于验证数据是否插入,项目中的数据库就是这个工具创建的 操作流程: 新建项目=> 安装System.Data.SQLite=> 配置App....