SignalR 从 WPF 服务器向 WPF 客户端发送消息
Posted
技术标签:
【中文标题】SignalR 从 WPF 服务器向 WPF 客户端发送消息【英文标题】:SignalR sending a message from WPF server to WPF client 【发布时间】:2018-12-06 01:07:45 【问题描述】:我一直在研究一个在自托管 WPF 服务器应用程序中使用 SignalR 2 与 WPF 客户端通信的示例。我已经下载了这个项目——我在下面展示——它工作得很好:
WPF 服务器:
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 + "\r");
/// <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);
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()
//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();
这是 WPF 客户端:
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\r", 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 + "\r");
/// <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();
让我感到困惑的是服务器 MyHub 类中的“Send”方法,它将文本推送到所有连接的客户端。在该方法中,它似乎在所有客户端上调用了一个名为“addMessage(String, String)”的方法,但该方法并未在客户端代码的任何地方声明。这是怎么回事?
【问题讨论】:
【参考方案1】:addMessage(String, String) 方法在客户端代码中声明:
HubProxy.On<string, string>("AddMessage", (name, message) =>
this.Dispatcher.Invoke(() =>
RichTextBoxConsole.AppendText(String.Format("0: 1\r", name, message))
)
);
根据https://docs.microsoft.com/en-us/previous-versions/aspnet/jj917974%28v%3dvs.100%29
该方法是一个动作委托:
https://docs.microsoft.com/en-us/dotnet/api/system.action-2?&view=netframework-4.8
【讨论】:
以上是关于SignalR 从 WPF 服务器向 WPF 客户端发送消息的主要内容,如果未能解决你的问题,请参考以下文章