WPF ViewModel命令无法执行问题
我在视图模型上使用关联菜单命令时遇到一些困难 我正在为视图模型中的每个命令实现ICommand接口,然后在视图(MainWindow)的资源中创建ContextMenu,并使用MVVMToolkit中的CommandReference访问当前的DataContext(ViewModel)命令 当我调试应用程序时,除了在创建窗口时,命令上的CanExecute方法似乎没有被调用,因此我的上下文菜单项没有像我预期的那样被启用或禁用 我制作了一个简单的示例(),它表明了我的实际应用,并总结如下。任何帮助都将不胜感激 这是ViewModelWPF ViewModel命令无法执行问题,wpf,binding,command,contextmenu,icommand,Wpf,Binding,Command,Contextmenu,Icommand,我在视图模型上使用关联菜单命令时遇到一些困难 我正在为视图模型中的每个命令实现ICommand接口,然后在视图(MainWindow)的资源中创建ContextMenu,并使用MVVMToolkit中的CommandReference访问当前的DataContext(ViewModel)命令 当我调试应用程序时,除了在创建窗口时,命令上的CanExecute方法似乎没有被调用,因此我的上下文菜单项没有像我预期的那样被启用或禁用 我制作了一个简单的示例(),它表明了我的实际应用,并总结如下。任何帮
namespace WpfCommandTest
{
public class MainWindowViewModel
{
private List<string> data = new List<string>{ "One", "Two", "Three" };
// This is to simplify this example - normally we would link to
// Domain Model properties
public List<string> TestData
{
get { return data; }
set { data = value; }
}
// Bound Property for listview
public string SelectedItem { get; set; }
// Command to execute
public ICommand DisplayValue { get; private set; }
public MainWindowViewModel()
{
DisplayValue = new DisplayValueCommand(this);
}
}
}
最后,视图在Xaml中定义:
<Window x:Class="WpfCommandTest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfCommandTest"
xmlns:mvvmtk="clr-namespace:MVVMToolkit"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<mvvmtk:CommandReference x:Key="showMessageCommandReference" Command="{Binding DisplayValue}" />
<ContextMenu x:Key="listContextMenu">
<MenuItem Header="Show MessageBox" Command="{StaticResource showMessageCommandReference}"/>
</ContextMenu>
</Window.Resources>
<Window.DataContext>
<local:MainWindowViewModel />
</Window.DataContext>
<Grid>
<ListBox ItemsSource="{Binding TestData}" ContextMenu="{StaticResource listContextMenu}"
SelectedItem="{Binding SelectedItem}" />
</Grid>
</Window>
您必须跟踪CanExecute的状态何时更改,并触发ICommand.CanExecuteChanged事件 此外,您可能会发现它并不总是有效的,在这种情况下,需要调用
CommandManager.invalidateRequestSuggested()
,才能将命令管理器踢出屁股
如果您发现这需要很长时间才能完成Will的回答,下面是
CanExecuteChanged
事件的“标准”实现:
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
(摘自Josh Smith的<代码>RelayCommand课程)
顺便说一下,你应该考虑使用<代码> RelayCube 或
private EventHandler _commandCanExecuteChangedHandler;
public event EventHandler CanExecuteChanged;
...
if (oldCommand != null)
{
oldCommand.CanExecuteChanged -= commandReference._commandCanExecuteChangedHandler;
}
if (newCommand != null)
{
commandReference._commandCanExecuteChangedHandler = commandReference.Command_CanExecuteChanged;
newCommand.CanExecuteChanged += commandReference._commandCanExecuteChangedHandler;
}
...
private void Command_CanExecuteChanged(object sender, EventArgs e)
{
if (CanExecuteChanged != null)
CanExecuteChanged(this, e);
}
为了实现相同的行为,我还必须包含EventHandler
来自Josh Smith的RelayCommand,在CommandReference中,但在doing中
因此,我必须注释掉OnCommandChanged中的一些代码
方法。我不完全确定它为什么会在那里,也许是
防止事件内存泄漏(猜测!)
请注意,将订阅转发到CommandManager.RequerySuggested的方法也消除了该错误(不再有未引用的处理程序),但它会妨碍CommandReference功能。与CommandReference关联的命令可以直接引发CanExecuteChanged(而不是依赖CommandManager发出重新查询请求),但此事件将被吞没,并且永远不会到达绑定到CommandReference的命令源。这也应该回答您的问题,即为什么CommandReference是通过订阅newCommand.CanExecuteChanged来实现的
更新:提交对我来说,一个更简单的解决方案是在菜单项上设置CommandTarget
<MenuItem Header="Cut" Command="Cut" CommandTarget="
{Binding Path=PlacementTarget,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ContextMenu}}}"/>
更多信息:好东西。。。我一直在想今天该怎么做+1.
private EventHandler _commandCanExecuteChangedHandler;
public event EventHandler CanExecuteChanged;
...
if (oldCommand != null)
{
oldCommand.CanExecuteChanged -= commandReference._commandCanExecuteChangedHandler;
}
if (newCommand != null)
{
commandReference._commandCanExecuteChangedHandler = commandReference.Command_CanExecuteChanged;
newCommand.CanExecuteChanged += commandReference._commandCanExecuteChangedHandler;
}
...
private void Command_CanExecuteChanged(object sender, EventArgs e)
{
if (CanExecuteChanged != null)
CanExecuteChanged(this, e);
}
<MenuItem Header="Cut" Command="Cut" CommandTarget="
{Binding Path=PlacementTarget,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ContextMenu}}}"/>