WPF MVVM Light:Command.RaiseCanExecuteChanged()不起作用

时间:2013-08-22 15:46:49

标签: c# wpf mvvm mvvm-light

我的ViewModel中有类似的内容:

public enum WorkState
{
    Idle, Working, Stopping
};

public class MainViewModel : ViewModelBase
{
    public const string StatePropertyName = "State";
    private WorkState _state = WorkState.Idle;
    public WorkState State
    {
        get
        { return _state; }
        set
        {
            if (_state == value)
            {
                return;
            }

            RaisePropertyChanging(StatePropertyName);
            _state = value;
            RaisePropertyChanged(StatePropertyName);

            StartStopCommand.RaiseCanExecuteChanged(); // <—————————————
        }
    }


    private RelayCommand _startStopCommand;
    public RelayCommand StartStopCommand
    {
        get
        {
            return _startStopCommand
                   ?? (_startStopCommand = new RelayCommand(
                                  () =>
                                  {
                                      if (State == WorkState.Idle)
                                      {
                                          State = WorkState.Working;
                                      }
                                      else if (State == WorkState.Working)
                                      {
                                          State = WorkState.Stopping;

                                          new Thread(() =>
                                               {
                                                   Thread.Sleep(5000);
                                                   State = WorkState.Idle;
                                                }).Start();
                                      }
                                  },
                                  () => (State == WorkState.Idle ||
                                           (State == WorkState.Working)));
        }
    }
}

我视图中的按钮:

        <Button Command="{Binding StartStopCommand}">
        <Button.Style>
            <Style>
                <Style.Triggers>
                    <DataTrigger Binding="{Binding State}" Value="{x:Static vm:WorkState.Idle}">
                        <Setter Property="Button.Content" Value="Start"/>
                    </DataTrigger>
                    <DataTrigger Binding="{Binding State}" Value="{x:Static vm:WorkState.Working}">
                        <Setter Property="Button.Content" Value="Stop"/>
                    </DataTrigger>
                    <DataTrigger Binding="{Binding State}" Value="{x:Static vm:WorkState.Stopping}">
                        <Setter Property="Button.Content" Value="Stop"/>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </Button.Style>
    </Button>

但是当我按下“停止”按钮并且“状态”属性更改为“空闲”时,按钮仅将其“文本”更改为“开始”,但在我单击窗口中的某个位置之前一直处于禁用状态。

我将RaiseCanExecuteChanged添加到State属性的setter中,但它没有帮助。

我做错了什么?

此外,我不确定在同一命令中使用Start / Stop和使用设置按钮文本的DataTriggers这种方法是最好的,所以如果有人分享更好的方法,我将不胜感激。

3 个答案:

答案 0 :(得分:3)

此处的问题不是RaiseCanExecuteChanged()无效,而是在您的用户界面依赖它时从后台线程修改State属性。

所以说如果我相应地修改你的代码:(这是.Net 4.5)

public RelayCommand StartStopCommand { get; set; }

...

public MainViewModel() {
  StartStopCommand = new RelayCommand(async () => {
    switch (State) {
      case WorkState.Idle:
        State = WorkState.Working;
        break;
      case WorkState.Working:
        State = WorkState.Stopping;
        await Task.Delay(5000);
        State = WorkState.Idle;
        break;
    }
  }, () => State == WorkState.Idle || State == WorkState.Working);
}

现在,您可以看到Button从禁用状态切换回正确启用。

您可以从Here

获取样本

如果您想坚持使用Thread,请将其切换为:

new Thread(
  () => {
    Thread.Sleep(5000);
    Application.Current.Dispatcher.BeginInvoke(
      new Action(() => State = WorkState.Idle));
  }).Start();

^^现在再次正常工作,因为你没有从另一个线程修改属性。

答案 1 :(得分:1)

答案 2 :(得分:1)

我认为您必须通过State change设置Dispatcher

请参阅此处了解如何执行此操作:

http://msdn.microsoft.com/en-US/magazine/cc163328.aspx#S3 - &gt; (图4更新UI)