首先创建 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
