WPF:在异步操作中使用CommandManager手动更新Command执行状态(转载)

WPF判断命令(Command)是否能够执行是通过ICommand.CanExecute事件,在实际程序中路由命令一般是通过CommandBinding来使命令得到实际操作代码,但是这个CanExecute事件的调用是由WPF控制的,有些时候,比如命令执行后进行一些异步耗时操作,操作完成后会影响CanExecute事件结果,但是WPF不会立即做出反应,那么这个时侯就需要手动调用CommandManager.InvalidateRequerySuggested对命令系统进行一次刷新。

比如下面这个小程序

<Window.CommandBindings>

<CommandBinding Command="New" CanExecute="CommandBinding_CanExecute" Executed="CommandBinding_Executed" />

</Window.CommandBindings>

<StackPanel>

<Button Command="New">执行工作</Button>

<TextBlock Name="tbl" Text="等待执行"></TextBlock>

</StackPanel>

 

 

//

// 事件执行代码

//

 

privatevoid CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)

{

e.CanExecute =!finished;

}

 

privatevoid CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)

{

System.Threading.ThreadPool.QueueUserWorkItem(dowork);

 

}

 

bool finished =false;

void dowork(object obj)

{

updateUI("开始工作");

System.Threading.Thread.Sleep(1000);

updateUI("工作结束");

finished =true;

}

 

void updateUI(string msg)

{

Dispatcher.BeginInvoke((Action)(() => tbl.Text = msg));

}

 

程序按钮点击后下面文字显示“工作结束”,这时按钮理应是禁用的(因为此时CanExecute结果是false),但实际上按钮没有被禁用,只有界面发生改变后(如焦点,按键变化,或者按钮再次被点击),按钮才会被禁用,因为此时WPF才调用相应的CanExecute事件。

 

手动调用CommandManager.InvalidateRequerySuggested就可以解决问题,注意这个函数只有在UI主线程下调用才会起作用。

void dowork(object obj)

{

updateUI("开始工作");

System.Threading.Thread.Sleep(1000);

updateUI("工作结束");

finished =true;

//手动更新

updateCommand();

}

 

void updateCommand()

{

Dispatcher.BeginInvoke((Action)(() =>CommandManager.InvalidateRequerySuggested()));

}

 

另外,如果你对WPF命令体系不太清楚的话,可以参考MSDN:

http://msdn.microsoft.com/zh-cn/library/ms752308.aspx