SignalR A complete WPF client using MVVM

In this post, a WPF application using MVVM is created and used to send and recieve SignalR messages.

The post continues on from: SignalR Messaging, a more complete server with a Console Application

Code: https://github.com/damienbod/SignalRMessagingErrorHandling

The application uses 4 different projects:

  1. Damienbod.SignalR.IHubSync.Client: This project helps us use the SignalR Hub hosted in the server console application
  2. PortableSignalR: The ViewModel and Model for the WPF application is implemented here, can be reused for Windows PRT or RT
  3. SignalRClientWPF: Our WPF application, contains mainly views.
  4. SignalRDataProvider: Data Provider for the SignalR service.

wpfSignalR1

The SignalR DataProvider
The assembly is an implementation of the ISignalRHubSync interface with all the methods required by the ViewModel to send and recieve SignalR messages. The interface is defined in the Portable library so that it can be used by Win RT or Win PRT applications.

using System;
using PortableSignalR.Model;

namespace PortableSignalR.DataProvider
{
    public interface ISignalRHubSync
    {
        event Action<bool> ConnectionEvent;
        event Action<MyMessage> RecieveMessageEvent;
        void Connect();
        void Disconnect();
        void AddMessage(MyMessage message);
        void Heartbeat();
        void SendHelloObject(MyMessage message);
    }
}

The SignalRHubSync uses the WPF Model objects for recieving and sending messages. The _myHubClient instance provides access to the SignalR Hub Proxy. When a message is recieved, it is simply passed on in the same thread using the defined events.

using System;
using Damienbod.SignalR.IHubSync.Client.Dto;
using Microsoft.AspNet.SignalR.Client;
using PortableSignalR.DataProvider;
using PortableSignalR.Model;
using SignalRDataProvider.HubClients;
using SignalRDataProvider.Logging;

namespace SignalRDataProvider.DataProvider
{
    public class SignalRHubSync : ISignalRHubSync
    {
        private readonly MyHubClient _myHubClient;

        //public event Action<bool> ConnectionEvent;
        
        public SignalRHubSync()
        {
            _myHubClient = new MyHubClient();
            _myHubClient.ConnectionEvent += _myHubClient_ConnectionEvent;
            _myHubClient.RecievedMessageEvent += _myHubClient_RecievedMessageEvent;
        }

        void _myHubClient_RecievedMessageEvent(MyMessage obj)
        {
            if (RecieveMessageEvent != null) RecieveMessageEvent.Invoke(obj); 
        }

        void _myHubClient_ConnectionEvent(bool obj)
        {
            if (ConnectionEvent != null) ConnectionEvent.Invoke(obj);
        }

        public event Action<bool> ConnectionEvent;
        public event Action<MyMessage> RecieveMessageEvent;


        public void Connect()
        {
            _myHubClient.StartHub();
            HubClientEvents.Log.Informational("Started the Hub");
        }

        public void Disconnect()
        {
            _myHubClient.CloseHub();
            HubClientEvents.Log.Informational("Closed Hub");
        }

        public void AddMessage(MyMessage message)
        {
            if (_myHubClient.State == ConnectionState.Connected)
            {
                _myHubClient.AddMessage(message.Name, message.Message);
            }
            else
            {
                HubClientEvents.Log.Warning("Can't send message, connectionState= " + _myHubClient.State);
            }
        }

        public void Heartbeat()
        {
             _myHubClient.Heartbeat();
        }

        public void SendHelloObject(MyMessage message)
        {
            if (_myHubClient.State == ConnectionState.Connected)
            {
                _myHubClient.SendHelloObject(new HelloModel { Age = int.Parse(message.Name), Molly = message.Message });
            }
            else
            {
                HubClientEvents.Log.Warning("Can't send message, connectionState= " + _myHubClient.State);
            }
        }
    }
}

The MyHubClient class implements everything required for the Hub client proxy. The class also implements the helper interfaces provided by the Hub common assembly.

using System;
using Damienbod.SignalR.IHubSync.Client;
using Damienbod.SignalR.IHubSync.Client.Dto;
using Microsoft.AspNet.SignalR.Client;
using PortableSignalR.Model;
using SignalRDataProvider.Logging;

namespace SignalRDataProvider.HubClients
{
    public class MyHubClient : BaseHubClient, ISendHubSync, IRecieveHubSync
    {
        public event Action<MyMessage> RecievedMessageEvent;

        public MyHubClient()
        {
            Init();
        }

        public new void Init()
        {
            HubConnectionUrl = "http://localhost:8089/";
            HubProxyName = "Hubsync";
            HubTraceLevel = TraceLevels.None;
            HubTraceWriter = Console.Out;

            base.Init();

            _myHubProxy.On<string, string>("addMessage", Recieve_AddMessage);
            _myHubProxy.On("heartbeat", Recieve_Heartbeat);
            _myHubProxy.On<HelloModel>("sendHelloObject", Recieve_SendHelloObject);

            StartHubInternal();
        }

        public override void StartHub()
        {
            _hubConnection.Dispose();
            Init();
        }

        public void Recieve_AddMessage(string name, string message)
        {
            if (RecievedMessageEvent != null) RecievedMessageEvent.Invoke(new MyMessage { Name = name , Message = message});
            HubClientEvents.Log.Informational("Recieved addMessage: " + name + ": " + message);
        }

        public void Recieve_Heartbeat()
        {
            if (RecievedMessageEvent != null) RecievedMessageEvent.Invoke(new MyMessage { Name = "Heartbeat", Message = "recieved" });
            HubClientEvents.Log.Informational("Recieved heartbeat ");
        }

        public void Recieve_SendHelloObject(HelloModel hello)
        {
            if (RecievedMessageEvent != null) RecievedMessageEvent.Invoke(new MyMessage { Name = hello.Age.ToString(), Message = hello.Molly });
            HubClientEvents.Log.Informational("Recieved sendHelloObject " + hello.Molly + ", " + hello.Age);
        }

        public void AddMessage(string name, string message)
        {
            _myHubProxy.Invoke("addMessage", name, message).ContinueWith(task =>
            {
                if (task.IsFaulted)
                {
                    HubClientEvents.Log.Error("There was an error opening the connection:" + task.Exception.GetBaseException());
                }

            }).Wait();
            HubClientEvents.Log.Informational("Client Sending addMessage to server");
        }

        public void Heartbeat()
        {
            _myHubProxy.Invoke("Heartbeat").ContinueWith(task =>
            {
                if (task.IsFaulted)
                {
                    HubClientEvents.Log.Error("There was an error opening the connection:" + task.Exception.GetBaseException());
                }

            }).Wait();
            HubClientEvents.Log.Informational("Client heartbeat sent to server");
        }

        public void SendHelloObject(HelloModel hello)
        {
            _myHubProxy.Invoke<HelloModel>("SendHelloObject", hello).ContinueWith(task =>
            {
                if (task.IsFaulted)
                {
                    HubClientEvents.Log.Error("There was an error opening the connection:" + task.Exception.GetBaseException());
                }

            }).Wait();
            HubClientEvents.Log.Informational("Client sendHelloObject sent to server");
        }


    }
}

The MyHubClient class implements the abstract class BaseHubClient. All common methods which could be used for a second or third Hub are defined here.

using System;
using Microsoft.AspNet.SignalR.Client;
using SignalRDataProvider.Logging;

namespace SignalRDataProvider.HubClients
{
    public abstract class BaseHubClient
    {
        protected HubConnection _hubConnection;
        protected IHubProxy _myHubProxy;

        public string HubConnectionUrl { get; set; }
        public string HubProxyName { get; set; }
        public TraceLevels HubTraceLevel { get; set; }
        public System.IO.TextWriter HubTraceWriter { get; set; }

        public event Action<bool> ConnectionEvent;

        public ConnectionState State
        {
            get { return _hubConnection.State; }
        }

        protected void Init()
        {
            _hubConnection = new HubConnection(HubConnectionUrl)
            {
                TraceLevel = HubTraceLevel,
                TraceWriter = HubTraceWriter
            };

            _myHubProxy = _hubConnection.CreateHubProxy(HubProxyName);

            _hubConnection.Received += _hubConnection_Received;
            _hubConnection.Reconnected += _hubConnection_Reconnected;
            _hubConnection.Reconnecting += _hubConnection_Reconnecting;
            _hubConnection.StateChanged += _hubConnection_StateChanged;
            _hubConnection.Error += _hubConnection_Error;
            _hubConnection.ConnectionSlow += _hubConnection_ConnectionSlow;
            _hubConnection.Closed += _hubConnection_Closed;

        }

        public void CloseHub()
        {
            _hubConnection.Stop();
            _hubConnection.Dispose();
        }

        protected void StartHubInternal()
        {
            try
            {
                _hubConnection.Start().Wait();
            }
            catch (Exception ex)
            {
                HubClientEvents.Log.Warning(ex.Message + " " + ex.StackTrace);
            }

        }

        public abstract void StartHub();

        void _hubConnection_Closed()
        {
            HubClientEvents.Log.ClientEvents("_hubConnection_Closed New State:" + _hubConnection.State + " " + _hubConnection.ConnectionId);
        }

        void _hubConnection_ConnectionSlow()
        {
            HubClientEvents.Log.ClientEvents("_hubConnection_ConnectionSlow New State:" + _hubConnection.State + " " + _hubConnection.ConnectionId);
        }

        void _hubConnection_Error(Exception obj)
        {
            HubClientEvents.Log.ClientEvents("_hubConnection_Error New State:" + _hubConnection.State + " " + _hubConnection.ConnectionId);
        }

        void _hubConnection_StateChanged(StateChange obj)
        {
            if (this.State == ConnectionState.Connected)
            {
                if (ConnectionEvent != null) ConnectionEvent.Invoke(true);
            }
            else
            {
                if (ConnectionEvent != null) ConnectionEvent.Invoke(false);
            }
            HubClientEvents.Log.ClientEvents("_hubConnection_StateChanged New State:" + _hubConnection.State + " " + _hubConnection.ConnectionId);
        }

        void _hubConnection_Reconnecting()
        {
            HubClientEvents.Log.ClientEvents("_hubConnection_Reconnecting New State:" + _hubConnection.State + " " + _hubConnection.ConnectionId);
        }

        void _hubConnection_Reconnected()
        {
            HubClientEvents.Log.ClientEvents("_hubConnection_Reconnected New State:" + _hubConnection.State + " " + _hubConnection.ConnectionId);
        }

        void _hubConnection_Received(string obj)
        {
            HubClientEvents.Log.ClientEvents("_hubConnection_Received New State:" + _hubConnection.State + " " + _hubConnection.ConnectionId);
        }
    }
}

Portable SignalR Library
The ViewModel and the Model for the MVVM pattern are implemented in this library. This library can be reused for Win RT, Win PRT or .Net applications. All model classes implement the Observable class. The PropertyChanged events are defined in this class.

using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace PortableSignalR.Base
{
    public class Observable : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            var handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        protected virtual bool SetProperty<T>(ref T storage, T newValue, [CallerMemberName] string propertyName = null)
        {
            if (object.Equals(storage, newValue)) return false;
            storage = newValue;
            OnPropertyChanged(propertyName);
            return true;
        }
    }
}

using PortableSignalR.Base;

namespace PortableSignalR.Model
{
    public class MyMessage : Observable
    {
        private string _name;
        private string _message;

        public string Name
        {
            get { return _name; }
            set
            {
                SetProperty(ref _name, value);
            }
        }

        public string Message
        {
            get { return _message; }
            set
            {
                SetProperty(ref _message, value);
            }
        }
    }
}

The ICommand interface is also implemented in this library. This can then be used to send and bind events from the GUI.

using System;
using System.Windows.Input;

namespace PortableSignalR.Command
{
    public class DelegateCommand : ICommand
    {
        private readonly Action<object> _execute;
        private readonly Predicate<object> _canExecute;
        public DelegateCommand(Action<object> execute, Predicate<object> canExecute = null)
        {
            if (execute == null) throw new ArgumentNullException("execute");

            _execute = execute;
            _canExecute = canExecute;
        }

        public bool CanExecute(object parameter)
        {
            return _canExecute == null ? true : _canExecute(parameter);
        }

        public void RaiseCanExecuteChanged()
        {
            var handler = CanExecuteChanged;
            if (handler != null)
            {
                handler(this, EventArgs.Empty);
            }
        }

        public void Execute(object parameter)
        {
            _execute(parameter);
        }

        public event EventHandler CanExecuteChanged;
    }
}

All view model classes should also be implemented in the portable library. No references to .NET or platform specific code can be defined or used. In the demo application, only one ViewModel is implemented (for the MainWindow View). In this view model, the interfaces ISignalRHubSync , IContext are added to the constructor. The class is then easy to unit test. The view uses this as its DataContext. A nice seperation of concerns now exist between the View and the viewModel.

using System.Collections.ObjectModel;
using PortableSignalR.Base;
using PortableSignalR.Command;
using PortableSignalR.DataProvider;
using PortableSignalR.Model;

namespace PortableSignalR.ViewModel
{
    public class MainViewModel : Observable
    {
        private string _mollyText;
        private int _ageText;
        private string _messageText;
        private string _nameText;
        private bool _connectionActive;
        private readonly ISignalRHubSync _signalRHubSync;
        private readonly IContext _context;

        public DelegateCommand AddMessageCommand { get; private set; }
        public DelegateCommand SendHeartbeatCommand { get; private set; }
        public DelegateCommand SendObjectCommand { get; private set; }

        public DelegateCommand DisconnectSignalR { get; private set; }
        public DelegateCommand ConnectSignalR { get; private set; }

        public ObservableCollection<MyMessage> MyMessages { get; private set; }

        public MainViewModel(ISignalRHubSync signalRHubSync, IContext context)
        {
            _signalRHubSync = signalRHubSync;
            _context = context;
            MyMessages = new ObservableCollection<MyMessage>();
            AddMessageCommand = new DelegateCommand(OnAddMessageExecute, OnAddMessageCanExecute);
            SendHeartbeatCommand = new DelegateCommand(OnSendHeartbeatExecute, OnSendHeartbeatCanExecute);
            SendObjectCommand = new DelegateCommand(OnSendObjectExecute, OnSendObjectCanExecute);

            DisconnectSignalR = new DelegateCommand(OnDisconnectSignalRExecute, OnDisconnectSignalRCanExecute);
            ConnectSignalR = new DelegateCommand(OnConnectSignalRExecute, OnConnectSignalRCanExecute);

            _signalRHubSync.ConnectionEvent += _signalRHubSync_ConnectionEvent;
            _signalRHubSync.RecieveMessageEvent += _signalRHubSync_RecieveMessageEvent;
        }

        void _signalRHubSync_RecieveMessageEvent(MyMessage obj)
        {
            _context.RecieveMessageEvent(this, obj);
        }

        void _signalRHubSync_ConnectionEvent(bool obj)
        {
            _context.SendConnectionEvent(this, obj);
        }


        private bool OnDisconnectSignalRCanExecute(object obj)
        {
            return ConnectionActive;
        }

        private void OnDisconnectSignalRExecute(object obj)
        {
            _signalRHubSync.Disconnect();
        }

        private bool OnConnectSignalRCanExecute(object obj)
        {
            return !ConnectionActive;
        }

        private void OnConnectSignalRExecute(object obj)
        {
            _signalRHubSync.Connect();
        }

        public void Load()
        {
            // Nothing to do here unless we want to persist our messages 
        }

        private bool OnSendObjectCanExecute(object obj)
        {
            return !string.IsNullOrWhiteSpace(MollyText) && AgeText > 0 && ConnectionActive; 
        }

        private void OnSendObjectExecute(object obj)
        {
            //MyMessages.Add(new MyMessage { Name = AgeText.ToString(), Message = MollyText });
            _signalRHubSync.SendHelloObject(new MyMessage { Name = AgeText.ToString(), Message = MollyText });
            MollyText = "";
            AgeText = 0;
        }

        private bool OnSendHeartbeatCanExecute(object obj)
        {
            return ConnectionActive;
        }

        private void OnSendHeartbeatExecute(object obj)
        {
            _signalRHubSync.Heartbeat();
            //MyMessages.Add(new MyMessage { Name = "Heartbeat Test", Message = "hhhh" });
        }

        private bool OnAddMessageCanExecute(object obj)
        {
            return !string.IsNullOrWhiteSpace(MessageText) && !string.IsNullOrWhiteSpace(NameText) && ConnectionActive;
        }

        private void OnAddMessageExecute(object obj)
        {
            //MyMessages.Add(new MyMessage { Name = NameText, Message = MessageText });
            _signalRHubSync.AddMessage(new MyMessage { Name = NameText, Message = MessageText });
            MessageText = "";
            NameText = "";
        }

        public string MollyText
        {
            get { return _mollyText; }
            set
            {
                SetProperty(ref _mollyText, value);
                SendObjectCommand.RaiseCanExecuteChanged();
            }
        }

        public int AgeText
        {
            get { return _ageText; }
            set
            {
                SetProperty(ref _ageText, value);
                SendObjectCommand.RaiseCanExecuteChanged();
            }
        }

        public string MessageText
        {
            get { return _messageText; }
            set
            {
                SetProperty(ref _messageText, value);
                AddMessageCommand.RaiseCanExecuteChanged();
            }
        }

        public string NameText
        {
            get { return _nameText; }
            set
            {
                SetProperty(ref _nameText, value);
                AddMessageCommand.RaiseCanExecuteChanged();
            }
        }

        public bool ConnectionActive
        {
            get { return _connectionActive; }
            set
            {
                SetProperty(ref _connectionActive, value);
                DisconnectSignalR.RaiseCanExecuteChanged();
                ConnectSignalR.RaiseCanExecuteChanged();
                AddMessageCommand.RaiseCanExecuteChanged();
                SendObjectCommand.RaiseCanExecuteChanged();
                SendHeartbeatCommand.RaiseCanExecuteChanged();
            }
        }
    }
}

SignalRClientWPF
This is the WPF application. The views are implemented here. The MainWindow view contains all the logic in this demo application.

<Window x:Class="SignalRClientWPF.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:signalRClientWpf="clr-namespace:SignalRClientWPF"
        xmlns:viewModel="clr-namespace:PortableSignalR.ViewModel;assembly=PortableSignalR"
        Title="damienbod SignalR client" Height="350" Width="625" Icon="icon.ico" >
   <Window.Resources>
        <signalRClientWpf:ColorToSolidColorBrushValueConverter  x:Key="ColorToSolidColorBrush_ValueConverter"/>
   </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"  />
            <ColumnDefinition />
            <ColumnDefinition />
            <ColumnDefinition Width="Auto" MinWidth="149" />
        </Grid.ColumnDefinitions>

        <Ellipse Fill="{Binding Path=ConnectionActive,Converter={StaticResource ColorToSolidColorBrush_ValueConverter}}" Grid.Column="0" Grid.Row="0"  Height="20"  Stroke="Black"  Width="20" x:Name="activeLight"/>
        <Button Command="{Binding DisconnectSignalR}" Content="Disconnect" Grid.Column="1" Grid.Row="0" Margin="5,5,10,5"  />
        <Button Command="{Binding ConnectSignalR}" Content="Connect" Grid.Column="2" Grid.Row="0" Margin="5,5,10,5"  />
        <Button Command="{Binding SendHeartbeatCommand}" Content="Send Heartbeat" Grid.Column="3" Grid.Row="0" Margin="17,5" x:Name="HeartbeatButton"  />

        <Label Content="Message:" Grid.Column="0" Grid.Row="1" Margin="5" />
        <TextBox Text="{Binding NameText,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="Auto" Grid.Column="1" Grid.Row="1" Margin="5,5,10,5" x:Name="ClientNameTextBox"  />
        <TextBox Text="{Binding MessageText,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="Auto" Grid.Column="2" Grid.Row="1" Margin="5,5,10,5" x:Name="MessageTextBox" />
        <Button Command="{Binding AddMessageCommand}" Content="AddMessage" Grid.Column="3" Grid.Row="1" Margin="17,5" x:Name="SendButton"  />

        <Label Content="Age, Molly:" Grid.Column="0" Grid.Row="2" Margin="5" />
        <TextBox Text="{Binding AgeText,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="Auto" Grid.Column="1" Grid.Row="2" Margin="5,5,10,5" x:Name="HelloTextBox"  />
        <TextBox Text="{Binding MollyText,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="Auto" Grid.Column="2" Grid.Row="2" Margin="5,5,10,5" x:Name="HelloMollyTextBox"  />
        <Button Command="{Binding SendObjectCommand}" Content="Send Object" Grid.Column="3" Grid.Row="2" Margin="17,5" x:Name="HelloButton"  />

        <ListBox ItemsSource="{Binding MyMessages}" Grid.Column="0" Grid.Row="3" Grid.ColumnSpan="4" Margin="5" x:Name="MessagesListBox" >
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel>
                        <Grid>            
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="200"  />
                                <ColumnDefinition Width="10"  />
                                <ColumnDefinition />
                            </Grid.ColumnDefinitions>
                       
                            <TextBlock Grid.Column ="0" Text="{Binding Name}" FontSize="15"/>
                            <TextBlock Grid.Column ="2" Text="{Binding Message}" FontSize="15"/>                       
                        </Grid>
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
      </Grid>
</Window>

In the code behind, the DataContext is initialised. This class is very light.

using System.Windows;
using PortableSignalR.ViewModel;
using SignalRDataProvider.DataProvider;

namespace SignalRClientWPF
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private MainViewModel _vm;

        public MainWindow()
        {
            InitializeComponent();
            Execute.InitializeWithDispatcher();

            _vm = new MainViewModel( new SignalRHubSync(), new WpfContext() );
            this.DataContext = _vm;
            _vm.Load();

        }
    }
}

A special helper class is required for the SignalR incoming events. This class passes the SignalR events to the UiThread.

using System;
using System.Windows.Threading;

namespace SignalRClientWPF
{
    public static class Execute
    {
        private static Action<System.Action> executor = action => action();

        /// <summary>
        /// Initializes the framework using the current dispatcher.
        /// </summary>
        public static void InitializeWithDispatcher()
        {
#if SILVERLIGHT
        var dispatcher = Deployment.Current.Dispatcher;
#else
            var dispatcher = Dispatcher.CurrentDispatcher;
#endif
            executor = action =>
            {
                if (dispatcher.CheckAccess())
                    action();
                else dispatcher.BeginInvoke(action);
            };
        }

        /// <summary>
        /// Executes the action on the UI thread.
        /// </summary>
        /// <param name="action">The action to execute.</param>
        public static void OnUIThread(this System.Action action)
        {
            executor(action);
        }
    }
}

A ColorToSolidColorBrushValueConverter class is also used to convert the connection state so the Ellipse Fill property can be binded to this bool value.

using System;
using System.Windows.Data;
using System.Windows.Media;

namespace SignalRClientWPF
{
    public class ColorToSolidColorBrushValueConverter : IValueConverter
    {

        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if (null == value)
            {
                return null;
            }
            // For a more sophisticated converter, check also the targetType and react accordingly..
            if (value is bool)
            {
                if (bool.Parse(value.ToString()))
                {
                    return new SolidColorBrush(Colors.LawnGreen);
                }

                return new SolidColorBrush(Colors.Red);
            }
            // You can support here more source types if you wish
            // For the example I throw an exception

            Type type = value.GetType();
            throw new InvalidOperationException("Unsupported type [" + type.Name + "]");
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            // If necessary, here you can convert back. Check if which brush it is (if its one),
            // get its Color-value and return it.

            throw new NotImplementedException();
        }
    }
}

The application source code can be downloaded from gitHub.

Unity could easily be used as a IoC for this application. Semantic Logging is used as a logging framework. Now a WPF application using MVVM can push and pull messages from a SignalR Hub.

Links:

http://www.asp.net/signalr

http://www.asp.net/signalr/overview/signalr-20/getting-started-with-signalr-20/introduction-to-signalr

http://signalr.net

http://www.asp.net/signalr/overview/signalr-20/hubs-api/handling-connection-lifetime-events

http://deanchalk.com/2010/02/01/thread-safe-dispatcher-safe-observable-collection-for-wpf/

Why Shouldn’t I use PRISM?

http://msdn.microsoft.com/en-us/magazine/dd943055.aspx

http://csharperimage.jeremylikness.com/2010/04/ten-reasons-to-use-managed.html

16 comments

  1. Thanks for Posts on SignalR.
    I am not able to build this example facing issue
    The type or namespace name ‘CallerMemberNameAttribute’ could not be found (are you missing a using directive or an assembly reference?)

    Am i missing anything? I am using VS 2012 + .net 4.5. Even googling didnt help me so posting here.

  2. Hi

    Your need the latest SP1

    greetings Damien

  3. Hi, i cannot reach to find the source files for SignalRDataProvider project.
    I the github repository they are missing.
    What am i wrong ??
    Thanks

  4. hi Stefano
    it should work, it’s a public link
    https://github.com/damienbod/SignalRMessagingErrorHandling

    greetings Damien

  5. Hi Damien,
    thanks for the reply, but in your github repository there’s only 3 projects: ClientConsole, ClientWPF and ServerConsole.
    The SignalRDataProvider’s project is missing.
    Thanks again
    Stefano

      1. Thanks a lot.
        I wasn’t looking in the subfolder.
        Thanks again
        Stefano

  6. Hi Damien,
    i have tried to upgrade your project to the last version of SignalR for use the StrongTyped Hub
    after the upgrade of all nugget packages i have down this change to your file’s project

    On HubSync.cs i changed this line in this way:
    public class HubSync : Hub

    and on ISendSync.cs i have refactored the class split it in a base class and an inherited class in this way:

    public interface ISendHubSync : ISendHubSyncBase
    {
    void AddMessage(string name, string message);

    void SendHelloObject(HelloModel hello);

    }
    public interface ISendHubSyncBase
    {
    void Heartbeat();

    }

    If i compile and run all works fine until i try to invoke the AddMessage from that client.
    When u do that i get a TypLoadException.

    Could you help me ??
    Thanks in advance
    Stefano

  7. I’m sorry but in my previous message i missed some words.
    On HubSync.cs i changed this line in this way:
    public class HubSync : Hub

    Thanks

  8. As additional info, if i move the declaration of Heartbeat() from base interface to ISendHubSync all works fine.
    Thanks
    PS:
    Seems that wordpress hates my hub’s declaration 🙂
    public class HubSync : Hub < ISendHubSync >

  9. Hi Stefano
    Thanks
    I’ll update the demo project
    greetings Damien

  10. You’re welcome.
    Let me know if you cannot reach to replicate the error with typed hub.
    Thanks again

  11. Hi Stefano

    I have upgraded to the latest version of SignalR and everything is working. I havn’t refactored anything yet, don’t understand why splitting the interfaces should cause a problem.

    greetings Damien

  12. I’ve change the Hub to a strong Tpye Hub and it’s also working, I had the change the messages on the send and the clients addmessage => AddMessage, The a needs to be toUppercase.

    Sorry for the slow answer, been busy lately

    Greetings Damien

    1. HI,
      i have opened a issue on SignalR
      https://github.com/SignalR/SignalR/issues/3311#issuecomment-61045442
      because this seems to be a SignalR design issue.
      Thanks again

  13. Appreciate you sharing, great blog article. Cool.
    b.zero1 ring white gold fake http://www.bzero.cn/

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.