刷新 WPF 命令
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/783104/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Refresh WPF Command
提问by Josh G
Does anyone know how I can force CanExecute
to get called on a custom command (Josh Smith's RelayCommand
)?
有谁知道我如何强制CanExecute
调用自定义命令(Josh Smith 的RelayCommand
)?
Typically, CanExecute
is called whenever interaction occurs on the UI. If I click something, my commands are updated.
通常,CanExecute
在 UI 上发生交互时调用。如果我单击某些东西,我的命令就会更新。
I have a situation where the condition for CanExecute
is getting turned on/off by a timer behind the scenes. Because this is not driven by user interaction, CanExecute
is not called until the user interacts with the UI. The end result is that my Button
remains enabled/disabled until the user clicks on it. After the click, it is updated correctly. Sometimes the Button
appears enabled, but when the user clicks it changes to disabled instead of firing.
我有一种情况,其中的条件CanExecute
是由幕后的计时器打开/关闭。因为这不是由用户交互驱动的,CanExecute
所以在用户与 UI 交互之前不会调用。最终结果是我的Button
保持启用/禁用,直到用户点击它。点击后,就正确更新了。有时Button
显示已启用,但当用户单击它时,它会变为禁用而不是触发。
How can I force an update in code when the timer changes the property that affects CanExecute
? I tried firing PropertyChanged
(INotifyPropertyChanged
) on the property that affects CanExecute
, but that did not help.
当计时器更改影响 的属性时,如何强制更新代码CanExecute
?我尝试在影响 的属性上触发PropertyChanged
( INotifyPropertyChanged
) CanExecute
,但这没有帮助。
Example XAML:
示例 XAML:
<Button Content="Button" Command="{Binding Cmd}"/>
Example code behind:
后面的示例代码:
private ICommand m_cmd;
public ICommand Cmd
{
if (m_cmd == null)
m_cmd = new RelayCommand(
(param) => Process(),
(param) => EnableButton);
return m_cmd;
}
// Gets updated from a timer (not direct user interaction)
public bool EnableButton { get; set; }
回答by Kent Boogaart
回答by SilverSideDown
I was aware of CommandManager.InvalidateRequerySuggested() a long time ago, and used it, but it wasn't working for me sometimes. I finally figured out why this was the case! Even though it doesn't throw like some other actions, you HAVE to call it on the main thread.
我很久以前就知道 CommandManager.InvalidateRequerySuggested() 并使用它,但有时它对我不起作用。我终于知道为什么会这样了!即使它不像其他一些动作那样抛出,你也必须在主线程上调用它。
Calling it on a background thread will appear to work, but sometimes leave the UI disabled. I really hope this helps somebody, and saves them the hours I just wasted.
在后台线程上调用它似乎可以工作,但有时会禁用 UI。我真的希望这可以帮助某人,并为他们节省我浪费的时间。
回答by tridy.net
A workaround for that is binding IsEnabled
to a property:
一种解决方法是绑定IsEnabled
到属性:
<Button Content="Button" Command="{Binding Cmd}" IsEnabled="{Binding Path=IsCommandEnabled}"/>
and then implement this property in your ViewModel. This also makes it a bit easier for the UnitTesting to work with the properties rather than commands to see if the command can be executed at a certain point of time.
然后在您的 ViewModel 中实现此属性。这也使 UnitTesting 更容易使用属性而不是命令来查看命令是否可以在某个时间点执行。
I, personally, find it more convenient.
我个人觉得比较方便。
回答by R.Titov
Probably this variant will suit you:
可能这个变体适合你:
public interface IRelayCommand : ICommand
{
void UpdateCanExecuteState();
}
Implementation:
执行:
public class RelayCommand : IRelayCommand
{
public event EventHandler CanExecuteChanged;
readonly Predicate<Object> _canExecute = null;
readonly Action<Object> _executeAction = null;
public RelayCommand( Action<object> executeAction,Predicate<Object> canExecute = null)
{
_canExecute = canExecute;
_executeAction = executeAction;
}
public bool CanExecute(object parameter)
{
if (_canExecute != null)
return _canExecute(parameter);
return true;
}
public void UpdateCanExecuteState()
{
if (CanExecuteChanged != null)
CanExecuteChanged(this, new EventArgs());
}
public void Execute(object parameter)
{
if (_executeAction != null)
_executeAction(parameter);
UpdateCanExecuteState();
}
}
Using simple:
使用简单:
public IRelayCommand EditCommand { get; protected set; }
...
EditCommand = new RelayCommand(EditCommandExecuted, CanEditCommandExecuted);
protected override bool CanEditCommandExecuted(object obj)
{
return SelectedItem != null ;
}
protected override void EditCommandExecuted(object obj)
{
// Do something
}
...
public TEntity SelectedItem
{
get { return _selectedItem; }
set
{
_selectedItem = value;
//Refresh can execute
EditCommand.UpdateCanExecuteState();
RaisePropertyChanged(() => SelectedItem);
}
}
XAML:
XAML:
<Button Content="Edit" Command="{Binding EditCommand}"/>
回答by Michael Kennedy
Thanks guys for the tips. Here's a bit of code on how to marshal that call from a BG thread to the UI thread:
谢谢你们的提示。下面是一些关于如何将该调用从 BG 线程编组到 UI 线程的代码:
private SynchronizationContext syncCtx; // member variable
In the constructor:
在构造函数中:
syncCtx = SynchronizationContext.Current;
On the background thread, to trigger the requery:
在后台线程上,触发重新查询:
syncCtx.Post( delegate { CommandManager.InvalidateRequerySuggested(); }, null );
Hope that helps.
希望有帮助。
-- Michael
——迈克尔
回答by Apfelkuacha
To update only a single GalaSoft.MvvmLight.CommandWpf.RelayCommand you could use
要仅更新一个 GalaSoft.MvvmLight.CommandWpf.RelayCommand,您可以使用
mycommand.RaiseCanExecuteChanged();
and for me i've created an Extension method:
对我来说,我创建了一个扩展方法:
public static class ExtensionMethods
{
public static void RaiseCanExecuteChangedDispatched(this RelayCommand cmd)
{
System.Windows.Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() => { cmd.RaiseCanExecuteChanged(); }));
}
public static void RaiseCanExecuteChangedDispatched<T>(this RelayCommand<T> cmd)
{
System.Windows.Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() => { cmd.RaiseCanExecuteChanged(); }));
}
}