.net 从WPF服务器向WPF客户端发送消息的信号器

.net 从WPF服务器向WPF客户端发送消息的信号器,.net,signalr,.net,Signalr,我一直在研究一个示例,其中在自托管WPF服务器应用程序中使用Signalr2与WPF客户端通信。我已经下载了这个项目——我将在下面展示它——它运行得很好: WPF服务器: public partial class MainWindow : Window { public IDisposable SignalR { get; set; } const string ServerURI = "http://localhost:8080"; public MainWi

我一直在研究一个示例,其中在自托管WPF服务器应用程序中使用Signalr2与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();
    }

}
公共部分类主窗口:窗口
{
公共IDisposable信号器{get;set;}
常量字符串ServerURI=”http://localhost:8080";
公共主窗口()
{
初始化组件();
}
/// 
///使用Task.Run调用StartServer方法以不运行
///阻止UI线程。
/// 
私有无效按钮开始单击(对象发送者,路由目标)
{
WriteToConsole(“启动服务器…”);
ButtonStart.IsEnabled=false;
Task.Run(()=>StartServer());
}
/// 
///停止服务器并关闭表单。忽略重新启动功能
///为了清楚起见。
/// 
私有无效按钮停止单击(对象发送者,路由目标)
{
Dispose();
Close();
}
/// 
///启动服务器并检查在另一台服务器已启动时引发的错误
///正在运行。此方法从按钮开始异步调用。
/// 
私有void StartServer()
{
尝试
{
SignalR=WebApp.Start(ServerURI);
}
捕获(目标异常)
{
WriteToConsole(“服务器已在“+ServerURI”上运行);
this.Dispatcher.Invoke(()=>ButtonStart.IsEnabled=true);
回来
}
this.Dispatcher.Invoke(()=>ButtonStop.IsEnabled=true);
WriteToConsole(“服务器启动于”+ServerURI);
}
///此方法使用Dispatcher.Invoke(如果使用)向RichTextBoxConsole控件添加一行
///来自信号集线器线程而不是UI线程。
公共无效WriteToConsole(字符串消息)
{
如果(!(RichTextBoxConsole.CheckAccess())
{
this.Dispatcher.Invoke(()=>
WriteToConsole(消息)
);
回来
}
RichTextBoxConsole.AppendText(消息+“\r”);
}
}
/// 
///用于OWIN的启动过程。
/// 
类启动
{
公共无效配置(IAppBuilder应用程序)
{
应用程序UseCors(CorsOptions.AllowAll);
app.mapsigner();
}
}
/// 
///通过调用
///客户端上的addMessage方法。还向控制台报告
///当客户端连接和断开连接时。
/// 
公共类MyHub:Hub
{
公共无效发送(字符串名称、字符串消息)
{
Clients.All.addMessage(名称、消息);
}
已连接的公用覆盖任务()
{
//使用Application.Current.Dispatcher从MainWindow类外部访问UI线程
Application.Current.Dispatcher.Invoke(()=>
((MainWindow)Application.Current.MainWindow).WriteConsole(“连接的客户端:“+Context.ConnectionId”);
返回base.OnConnected();
}
公共覆盖任务OnDisconnected()
{
//使用Application.Current.Dispatcher从MainWindow类外部访问UI线程
Application.Current.Dispatcher.Invoke(()=>
((MainWindow)Application.Current.MainWindow).WriteConsole(“客户端断开连接:“+Context.ConnectionId”);
返回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();
        }
    }  
}
公共部分类主窗口:窗口
{
/// 
///此名称仅添加到已发送的消息中以标识用户;此
///示例不包括身份验证。
/// 
公共字符串用户名{get;set;}
公共IHubProxy HubProxy{get;set;}
常量字符串ServerURI=”http://localhost:8080/signalr";
公共HUB连接{get;set;}
公共主窗口()
{
初始化组件();
}
私有无效按钮发送\单击(对象发送者,路由目标)
{
调用(“发送”,用户名,TextBoxMessage.Text);
TextBoxMessage.Text=String.Empty;
TextBoxMessage.Focus();
}
/// 
///创建并连接集线器连接和集线器代理。此方法
///从SignInButton\u Click异步调用。
/// 
私有异步void ConnectAsync()
{
连接=新的HUB连接(ServerURI);
连接关闭+=连接关闭;
HubProxy=Connection.CreateHubProxy(“MyHub”);
//处理来自服务器的传入事件:使用Invoke从信号器的线程写入控制台
HubProxy.On(“AddMessage”,(名称、消息)=>
this.Dispatcher.Invoke(()=>
RichTextBoxConsole.AppendText(String.Format(“{0}:{1}\r”,名称,消息))
)
);
尝试
{
等待连接。开始();
}
捕获(HttpRequestException)
{
StatusText.Content=“无法连接到服务器:在连接客户端之前启动服务器。”;
//无连接:不启用发送按钮或显示聊天界面
回来
}
//显示聊天界面;隐藏登录界面
SignInPanel.Visibility=可见性。已折叠;
ChatPanel.Visibility=Visibility.Visible;
ButtonSend.IsEnabled=true;
TextBoxMessage.Focus();
AppendText(“连接到服务器的“+ServerURI+”\r”);
}
/// 
///如果服务器停止,连接将在30秒后超时(默认),并且
///关闭的事件将被触发。
/// 
无效连接\u已关闭()
{
//隐藏聊天界面;显示登录界面
var dispatcher=Application.Current.dispatcher;
dispatcher.Invoke(()=>ChatPanel.Visibility=Visibility.Collapsed);
dispatcher.Invoke(()=>ButtonSend.IsEnabled=false);
dispatcher.Invoke(()=>StatusText.Content=“您已断开连接。”);
dispatcher.Invoke(()=>SignInPanel.Visibility=Visibility.Visi
HubProxy.On<string, string>("AddMessage", (name, message) =>
        this.Dispatcher.Invoke(() =>
            RichTextBoxConsole.AppendText(String.Format("{0}: {1}\r", name, message))
        )
    );