1

我有一个 MVVM 应用程序,我的 ViewModelPingerViewModel处理传入的 WCFPing()消息。处理此类消息发生在Scheduler.Default's 线程池的线程上。从语义上讲,传入的 WCF 消息会更改绑定的属性CanPing并引发该属性的 PropertyChanged 事件。

但是我的 UI 在收到一些 UI 事件之前不会更新,例如聚焦/单击窗口等。
如何在事件触发后立即更新?

我试过引发 PropertyChanged 事件......

  • 在应用程序的调度程序上,
  • 使用 SynchronizationContext

没有任何运气。

我还验证了绑定属性确实设置为正确的值,并且确实有一个侦听器正在使用我的 PropertyChanged 事件。

下面是一些代码(github 上的完整代码):
我的视图 MainWindow.xaml 的一部分:
可能值得注意的是,绑定Command实际上在生成传入的 WCF 消息时并没有发挥作用。

<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}" />

我的部分观点 MainWindow.xaml.cs

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

我的视图模型的一部分

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; }
    //...
}
4

3 回答 3

3

我认为您需要从按钮中删除IsEnabled="{Binding CanPing}"

绑定到命令就足够了,因为 ICommand 对象包含CanExecuteCanExecuteChanged事件处理程序。

我会在你的 Command 类中创建一个 CanExecute 布尔值,并在这个类上实现 INotifyPropertyChanged。像这样的东西:

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));
    }
}

然后,在 ViewModel 中的 Ping/Pong 方法上,更新 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");
}
于 2013-08-08T12:38:20.750 回答
0

如果您的 CanPing 属性和 PingCommand 的 CanExecute 方法都返回 TRUE,则它应该可以工作。

有时,Delegate/RelayCommand 实现提供了调用 RaiseCanExecuteChanged() 的可能性 - 如果上面的语句对两者都适用且不起作用,请尝试此操作

顺便说一句,这是在 RaiseCanExecuteChanged() 中调用的

 CommandManagerHelper.CallWeakReferenceHandlers(_canExecuteChangedHandlers);
于 2013-08-08T12:18:17.077 回答
0

您可以使用RaiseCanExecuteChanged()该属性的方法进行更新。
例如:

this.PingCommand.RaiseCanExecuteChanged();

试试这个,我希望它能解决你的问题。

于 2013-08-08T12:39:01.427 回答