WPF UI Does Not Update After an Inbound WCF Message

advertisements

I have an MVVM app, where my ViewModel PingerViewModel processes incoming WCF Ping() messages. Processing such a message happens on a thread of Scheduler.Default's thread pool. Semantically, incoming WCF messages change a bound property CanPing and raise the PropertyChanged event for said property.

But my UI is not updating until it receives some UI event, e.g. focusing/clicking the window, etc.
How do I make it update as soon as the event is fired?

I have tried raising the PropertyChanged event...

  • on the Application's Dispatcher,
  • using a SynchronizationContext

without any luck.

I also verified that the bound property is indeed set to the proper value, and that there is indeed a listener consuming my PropertyChanged event.

Here's some code (full code on github):
part of my view's MainWindow.xaml:
It might be worth noting that the bound Command does not actually play a role in producing the incoming WCF message.

<Button Content="Ping" Height="23" HorizontalAlignment="Left" Margin="10,10,0,0" Name="PingBtn" VerticalAlignment="Top" Width="75" AutomationProperties.AutomationId="Ping"
            IsEnabled="{Binding CanPing}"
            Command="{Binding PingCommand}" />

part of my views MainWindow.xaml.cs

public MainWindow()
{
    DataContext = new PingerViewModel();
    InitializeComponent();
}

part of my ViewModel

public class PingerViewModel : INotifyPropertyChanged
    public PingerViewModel()
    {
        Pinger = new Pinger(true);
        PingCommand = new PingerPingCommand(this);
        //...
    }

    public bool CanPing
    {
        get
        {
            if (Pinger == null) return false;
            return Pinger.CanPing;
        }
    }

    public void Ping()
    {
        _pingClient.Channel.Ping();
        Pinger.CanPing = false;
        OnPropertyChanged("CanPing");
    }

    protected virtual void OnPong(PongEventArgs e)
    {
        Pinger.CanPing = true;
        OnPropertyChanged("CanPing");
    }

    public Pinger Pinger { get; private set; }

    public ICommand PingCommand { get; private set; }
    //...
}


I think you need to remove IsEnabled="{Binding CanPing}" from your button.

Binding to the command is enough as the ICommand object contains CanExecute and the CanExecuteChanged event handler.

I would create a CanExecute boolean inside your Command class, and implement INotifyPropertyChanged also on this class. Something like this:

public class PingCommand : ICommand, INotifyPropertyChanged
{
    private bool _canExecute;

    public bool CanExecute1
    {
        get { return _canExecute; }
        set
        {
            if (value.Equals(_canExecute)) return;
            _canExecute = value;
            CanExecuteChanged.Invoke(null, null);
            OnPropertyChanged("CanExecute1");
        }
    }

    public void Execute(object parameter)
    {
        //whatever
    }

    public bool CanExecute(object parameter)
    {
        return _canExecute;
    }

    public event EventHandler CanExecuteChanged;
    public event PropertyChangedEventHandler PropertyChanged;

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

Then, on the Ping/Pong methods inside your ViewModel, update this property inside your Command:

public void Ping()
{
    _pingClient.Channel.Ping();
    Pinger.CanPing = false;
    PingCommand.CanExecute1 = false;
    OnPropertyChanged("CanPing");
}

protected virtual void OnPong(PongEventArgs e)
{
    Pinger.CanPing = true;
    PingCommand.CanExecute1 = true;
    OnPropertyChanged("CanPing");
}