首先创建 WPF Server 端,新建一个 WPF 项目
安装 Nuget 包
替换 MainWindows 的Xaml代码
<Window x:Class="WPFServer.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="WPF SignalR Server" Height="319" Width="343 "> <Grid> <Button x:Name="ButtonStart" Content="Start" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="100" Click="ButtonStart_Click"/> <Button x:Name="ButtonStop" Content="Stop" HorizontalAlignment="Left" Margin="225,10,0,0" VerticalAlignment="Top" Width="100" Click="ButtonStop_Click" IsEnabled="False"/> <RichTextBox x:Name="RichTextBoxConsole" HorizontalAlignment="Left" Height="243" Margin="10,35,0,0" VerticalAlignment="Top" Width="315"> <FlowDocument> <Paragraph> </Paragraph> </FlowDocument> </RichTextBox> </Grid> </Window>
替换 MainWindows 后台代码
using Microsoft.AspNet.SignalR; using Microsoft.Owin.Cors; using Microsoft.Owin.Hosting; using Owin; using System; using System.Reflection; using System.Threading.Tasks; using System.Windows; namespace WPFServer { /// <summary> /// WPF host for a SignalR server. The host can stop and start the SignalR /// server, report errors when trying to start the server on a URI where a /// server is already being hosted, and monitor when clients connect and disconnect. /// The hub used in this server is a simple echo service, and has the same /// functionality as the other hubs in the SignalR Getting Started tutorials. /// For simplicity, MVVM will not be used for this sample. /// </summary> public partial class MainWindow : Window { public IDisposable SignalR { get; set; } const string ServerURI = "http://localhost:8080"; public MainWindow() { InitializeComponent(); } /// <summary> /// Calls the StartServer method with Task.Run to not /// block the UI thread. /// </summary> private void ButtonStart_Click(object sender, RoutedEventArgs e) { WriteToConsole("Starting server..."); ButtonStart.IsEnabled = false; Task.Run(() => StartServer()); } /// <summary> /// Stops the server and closes the form. Restart functionality omitted /// for clarity. /// </summary> private void ButtonStop_Click(object sender, RoutedEventArgs e) { SignalR.Dispose(); Close(); } /// <summary> /// Starts the server and checks for error thrown when another server is already /// running. This method is called asynchronously from Button_Start. /// </summary> private void StartServer() { try { SignalR = WebApp.Start(ServerURI); } catch (TargetInvocationException) { WriteToConsole("A server is already running at " + ServerURI); this.Dispatcher.Invoke(() => ButtonStart.IsEnabled = true); return; } this.Dispatcher.Invoke(() => ButtonStop.IsEnabled = true); WriteToConsole("Server started at " + ServerURI); } ///This method adds a line to the RichTextBoxConsole control, using Dispatcher.Invoke if used /// from a SignalR hub thread rather than the UI thread. public void WriteToConsole(String message) { if (!(RichTextBoxConsole.CheckAccess())) { this.Dispatcher.Invoke(() => WriteToConsole(message) ); return; } RichTextBoxConsole.AppendText(message + " "); } } /// <summary> /// Used by OWIN's startup process. /// </summary> class Startup { public void Configuration(IAppBuilder app) { app.UseCors(CorsOptions.AllowAll); app.MapSignalR(); } } /// <summary> /// Echoes messages sent using the Send message by calling the /// addMessage method on the client. Also reports to the console /// when clients connect and disconnect. /// </summary> public class MyHub : Hub { public void Send(string name, string message) { Clients.All.addMessage(name, message); //Groups.Add } public override Task OnConnected() { //Use Application.Current.Dispatcher to access UI thread from outside the MainWindow class Application.Current.Dispatcher.Invoke(() => ((MainWindow)Application.Current.MainWindow).WriteToConsole("Client connected: " + Context.ConnectionId)); return base.OnConnected(); } public override Task OnDisconnected(bool ss) { //Use Application.Current.Dispatcher to access UI thread from outside the MainWindow class Application.Current.Dispatcher.Invoke(() => ((MainWindow)Application.Current.MainWindow).WriteToConsole("Client disconnected: " + Context.ConnectionId)); return base.OnDisconnected(ss); } } }
创建 WPF Client 端,新建一个 WPF 项目
安装 Nuget 包
替换 MainWindow 的前台 xmal 文件
<Window x:Name="WPFClient" x:Class="WPFClient.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="WPF SignalR Client" Height="552" Width="517" MinWidth="517" MinHeight="552" ResizeMode="CanMinimize" Closing="WPFClient_Closing"> <Grid> <StackPanel x:Name="SignInPanel" Margin="10,0" MaxWidth="550"> <Label Content="Enter user name:"/> <Grid> <TextBox x:Name="UserNameTextBox" Height="20" Margin="0,0,80,0"/> <Button x:Name="SignInButton" Content="Sign In" Width="75" Click="SignInButton_Click" HorizontalAlignment="Right"/> </Grid> <Label x:Name="StatusText" Visibility="Collapsed" HorizontalAlignment="Center" Margin="0,10"/> </StackPanel> <StackPanel x:Name="ChatPanel" Margin="10" MaxWidth="550" Visibility="Collapsed"> <Grid> <TextBox x:Name="TextBoxMessage" Height="20" TextWrapping="Wrap" Margin="0,0,80,0"/> <Button x:Name="ButtonSend" Content="Send" Width="75" Height="20" Click="ButtonSend_Click" IsDefault="True" IsEnabled="False" HorizontalAlignment="Right"/> </Grid> <RichTextBox x:Name="RichTextBoxConsole" HorizontalAlignment="Left" Height="461" ScrollViewer.VerticalScrollBarVisibility="Auto" Margin="0,10" IsReadOnly="True"/> </StackPanel> </Grid> </Window>
替换后台代码
using System; using System.Net.Http; using System.Windows; using Microsoft.AspNet.SignalR.Client; namespace WPFClient { /// <summary> /// SignalR client hosted in a WPF application. The client /// lets the user pick a user name, connect to the server asynchronously /// to not block the UI thread, and send chat messages to all connected /// clients whether they are hosted in WinForms, WPF, or a web application. /// For simplicity, MVVM will not be used for this sample. /// </summary> public partial class MainWindow : Window { /// <summary> /// This name is simply added to sent messages to identify the user; this /// sample does not include authentication. /// </summary> public String UserName { get; set; } public IHubProxy HubProxy { get; set; } const string ServerURI = "http://localhost:8080/signalr"; public HubConnection Connection { get; set; } public MainWindow() { InitializeComponent(); } private void ButtonSend_Click(object sender, RoutedEventArgs e) { HubProxy.Invoke("Send", UserName, TextBoxMessage.Text); TextBoxMessage.Text = String.Empty; TextBoxMessage.Focus(); } /// <summary> /// Creates and connects the hub connection and hub proxy. This method /// is called asynchronously from SignInButton_Click. /// </summary> private async void ConnectAsync() { Connection = new HubConnection(ServerURI); Connection.Closed += Connection_Closed; HubProxy = Connection.CreateHubProxy("MyHub"); //Handle incoming event from server: use Invoke to write to console from SignalR's thread HubProxy.On<string, string>("AddMessage", (name, message) => this.Dispatcher.Invoke(() => RichTextBoxConsole.AppendText(String.Format("{0}: {1} ", name, message)) ) ); try { await Connection.Start(); } catch (HttpRequestException) { StatusText.Content = "Unable to connect to server: Start server before connecting clients."; //No connection: Don't enable Send button or show chat UI return; } //Show chat UI; hide login UI SignInPanel.Visibility = Visibility.Collapsed; ChatPanel.Visibility = Visibility.Visible; ButtonSend.IsEnabled = true; TextBoxMessage.Focus(); RichTextBoxConsole.AppendText("Connected to server at " + ServerURI + " "); } /// <summary> /// If the server is stopped, the connection will time out after 30 seconds (default), and the /// Closed event will fire. /// </summary> void Connection_Closed() { //Hide chat UI; show login UI var dispatcher = Application.Current.Dispatcher; dispatcher.Invoke(() => ChatPanel.Visibility = Visibility.Collapsed); dispatcher.Invoke(() => ButtonSend.IsEnabled = false); dispatcher.Invoke(() => StatusText.Content = "You have been disconnected."); dispatcher.Invoke(() => SignInPanel.Visibility = Visibility.Visible); } private void SignInButton_Click(object sender, RoutedEventArgs e) { UserName = UserNameTextBox.Text; //Connect to server (use async method to avoid blocking UI thread) if (!String.IsNullOrEmpty(UserName)) { StatusText.Visibility = Visibility.Visible; StatusText.Content = "Connecting to server..."; ConnectAsync(); } } private void WPFClient_Closing(object sender, System.ComponentModel.CancelEventArgs e) { if (Connection != null) { Connection.Stop(); Connection.Dispose(); } } } }
在解决方案的属性里面,设置 Server 和 Client 端一起启动
运行查看效果
源代码链接:
链接: http://pan.baidu.com/s/1eRC2qVw 密码: twh3
分类: SignalR