之前写了个用Socket来更新多个Winfrom的试例,这两天看了下SignalR,也用这个来试一下
SignalR 地址:https://www.asp.net/signalr
我这个也是基于 https://code.msdn.microsoft.com/Using-SignalR-in-WinForms-f1ec847b 改的
有很多地方基本上不明白,现在也只处在稍微会用的一些阶段
话不多讲,先看代码
1.创建服务端
样子,基本上就这样,先贴代码
里面有两个类文件要注意 MyHub和Startup
using System.Threading.Tasks; using Microsoft.AspNet.SignalR; namespace ServerFrm { public class MyHub:Hub { /// <summary> /// 在连接上时 /// </summary> /// <returns></returns> public override Task OnConnected () { ///向服务端写入一些数据 Program.serverFrm.WriteToInfo ("客户端连接ID:" + Context.ConnectionId); return base.OnConnected (); } public override Task OnDisconnected (bool stopCalled) { ///向服务端写入一些数据 Program.serverFrm.WriteToInfo ("客户端退出ID:" + Context.ConnectionId); return base.OnReconnected (); } /// <summary> /// 这是给客户来调用的 /// 当客户端的添加按钮点击后 /// 就调用此方法 /// 当在客户端绑定了下面的Update方法后 /// 会自动去调用Update方法 /// </summary> /// <param name="actionId">操作标识符</param> public void Send(string actionId) { /// 这是给客户端来调用的 /// 在连接服务器之前就给连接代理绑定这个方法 /// 在客户端连接上此服务后 /// 客户端绑定此方法,并且传入一个标识符,本例为 "1"(代表要更新界面上的datagridview Clients.All.Update (actionId); } } }
在MyHub里就只有一个方法是给客户端调用的Send
等会就会在客户端里看到这个方法的调用场景
using Microsoft.Owin; using Owin; [assembly: OwinStartup (typeof (ServerFrm.Startup))] namespace ServerFrm { public class Startup { public void Configuration (IAppBuilder app) { // 有关如何配置应用程序的详细信息,请访问 http://go.microsoft.com/fwlink/?LinkID=316888 //设置可以跨域访问 app.UseCors (Microsoft.Owin.Cors.CorsOptions.AllowAll); //映射到默认的管理 app.MapSignalR (); } } }
Startup类,基本上我也弄不太明白,反正就这么写也不出错,但不写肯定是错
接着是服务端主要的代码
using System; using System.Windows.Forms; namespace ServerFrm { static class Program { /// <summary> /// 创建一个静态的对象,方便给服务端调用 /// </summary> internal static ServerFrm serverFrm { get; set; } /// <summary> /// 应用程序的主入口点。 /// </summary> [STAThread] static void Main () { Application.EnableVisualStyles (); Application.SetCompatibleTextRenderingDefault (false); serverFrm = new ServerFrm (); Application.Run (serverFrm); } } }
using System; using System.Threading.Tasks; using System.Windows.Forms; using Microsoft.Owin.Hosting; namespace ServerFrm { public partial class ServerFrm : Form { public ServerFrm () { InitializeComponent (); } private IDisposable signalR { get; set; } private string serverUrl { get; set; } = System.Configuration.ConfigurationManager.AppSettings["url"]; private void btnStart_Click (object sender , EventArgs e) { WriteToInfo ("正在连接中...."); btnStart.Enabled = false; Task.Run (() => { ServerStart (); }); } /// <summary> /// 开启服务 /// </summary> private void ServerStart () { try { //开启服务 signalR = WebApp.Start (serverUrl); } catch ( Exception ex ) { //服务失败时的处理 WriteToInfo ("服务开启失败,原因:" + ex.Message); this.Invoke (new Action (() => { btnStart.Enabled = true; })); return; } //服务成功,继续下一步 this.Invoke (new Action (() => { //启用停止按钮 btnStop.Enabled = true; })); WriteToInfo ("服务开启成功 : " + serverUrl); } /// <summary> /// 向服务容器写入信息 /// </summary> /// <param name="msg">信息</param> internal void WriteToInfo(string msg) { if ( richTextBox.InvokeRequired ) { this.Invoke (new Action (() => { WriteToInfo (msg); })); return; } richTextBox.AppendText (msg+Environment.NewLine); } private void btnStop_Click (object sender , EventArgs e) { if ( signalR!=null ) { signalR.Dispose (); } } private void ServerFrm_FormClosing (object sender , FormClosingEventArgs e) { btnStop_Click (this , new EventArgs ()); Close (); } } }
服务端的代码很少,主要的就是开启服务而已
接着是客户端
using System; using System.Linq; using System.Windows.Forms; using Microsoft.AspNet.SignalR.Client; using Model; namespace ClinetFrm { public partial class Clinet : Form { /// <summary> /// 连接代理对象 /// </summary> private IHubProxy hubProxy { get; set; } /// <summary> /// 绑定的服务器url /// </summary> private string ServerURI = System.Configuration.ConfigurationManager.AppSettings["url"]; /// <summary> /// 连接对象 /// </summary> private HubConnection hubConnection { get; set; } public Clinet () { InitializeComponent (); } private void Clinet_Load (object sender , EventArgs e) { InitData (); InitHub (); } private async void InitHub () { //创建连接对象 hubConnection = new HubConnection (ServerURI); //绑定一个集线器 hubProxy = hubConnection.CreateHubProxy ("MyHub"); //注册服务端的方法,此方法请转至服务端MyHub.cs中查看 hubProxy.On ("Update" , (a) => { //如果接收到的是"1" if ( a == "1" ) { //则更新界面 InitData (); } }); try { //开始连接 await hubConnection.Start (); } catch ( Exception ex ) { this.Text = "服务器未连接上"; return; } this.Text = "服务器已连接上"; } /// <summary> /// 加载或更新datagridview /// </summary> private void InitData () { //获取数据 DemoEntities demo = new DemoEntities (); var list = demo.DemoTable.ToList (); this.Invoke (new Action (() => { //绑定数据 dataGridView1.DataSource = list; })); } /// <summary> /// 添加按钮 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnAdd_Click (object sender , EventArgs e) { DemoEntities demo = new DemoEntities (); demo.DemoTable.Add (new DemoTable () { name = txtName.Text , value = txtVal.Text }); demo.SaveChanges (); ///使用代理启动方法,启动的是服务端中的Send方法 ///而在服务端中Send会调用Update方法 ///因为我们在程序启动时连接上了服务端 ///而且绑定了Update方法,所以服务端在接收到Send方法被调用的通知时 ///会自动去广播所有已经连上服务端的客户端使其调用Update方法 hubProxy.Invoke ("Send" , "1"); } private void Clinet_FormClosing (object sender , FormClosingEventArgs e) { if ( hubConnection != null ) { hubConnection.Stop (); hubConnection.Dispose (); } } } }
客户端界面
再来张动图演示一下
https://github.com/raozhihao/SignallR4WinformUpdateDemo
git地址
里面用了EF数据库,可以照着Model - > DemoTable.cs 中的字段在数据库中建一个
另外在git中加入了服务端向客户端广播的一些代码