Catel - CommandManager 未传递 CommandParameter
Catel - CommandManager not passing CommandParameter
我有一个带有 "MainView" 和一些嵌套视图的 Catel 应用程序。
嵌套视图有一个 ListView
,一些项目有一个 ContextMenu
,一些项目有 MenuItems
。
在 MainView 的 ViewModel 中,我创建了一个 TaskCommand<object>
,它将对传递的参数执行某些操作。这个传递的参数应该是ListView
当前的SelectedItem
。此命令正在全局注册到 ICommandManager
.
如果我使用来自 ICommandManager
的绑定命令单击 MenuItem
,则传递的参数将始终为 null
。
这里是相关代码:
NestedView.xaml:
<catel:UserControl
x:Name="UserControl"
x:Class="My.NameSpace.Views.View"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:catel="http://catel.codeplex.com">
...
<ListView ItemsSource="{Binding Items}"
BorderThickness="0"
SelectedItem="{Binding SelectedItem}"
HorizontalContentAlignment="Stretch">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock VerticalAlignment="Center"
Margin="2.5"
Text="{Binding Description}">
<TextBlock.ContextMenu>
<ContextMenu>
<MenuItem Header="Do Stuff"
Command="{catel:CommandManagerBinding DoStuffCommand}"
CommandParameter="{Binding DataContext.SelectedItem, ElementName=UserControl}" />
...
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ListView>
...
</catel:UserControl>
MainViewModel.cs:
public class MainViewModel : ViewModelBase {
...
public ICommand DoStuffCommand { get; }
public MainViewModel(ICommandManager commandManager, IUIVisualizerService uiVisualizerService, IMessageService messageService)
{
...
DoStuffCommand = new TaskCommand<object>(OnDoStuffCommandExecute);
commandManager.CreateCommand(nameof(DoStuffCommand));
commandManager.RegisterCommand(nameof(DoStuffCommand), DoStuffCommand, this);
}
private async Task OnDoStuffCommandExecute(object parameter)
{
// command needs to be in the MainViewModel because there will be some modification on the MainView based on parameter (adding tabs, etc)
Debugger.Break();
}
...
}
如果您需要更多代码,我也可以 post 但这应该足够了。
我还查看了 Catel 的 CommandManager
实现并发现:
/// <summary>
/// Executes the command.
/// </summary>
/// <param name="commandName">Name of the command.</param>
/// <exception cref="ArgumentException">The <paramref name="commandName"/> is <c>null</c> or whitespace.</exception>
/// <exception cref="InvalidOperationException">The specified command is not created using the <see cref="CreateCommand"/> method.</exception>
public void ExecuteCommand(string commandName)
{
Argument.IsNotNullOrWhitespace("commandName", commandName);
lock (_lockObject)
{
Log.Debug("Executing command '{0}'", commandName);
if (!_commands.ContainsKey(commandName))
{
throw Log.ErrorAndCreateException<InvalidOperationException>("Command '{0}' is not yet created using the CreateCommand method", commandName);
}
_commands[commandName].Execute(null);
}
}
我假设如果我单击 MenuItem
将调用此方法并解释该行为。
是否有适当的 solution/workaround 将(绑定)参数传递给我的 OnExecute
方法?
提前致谢
在 xaml
中的命令绑定 之前设置 CommandParameter
绑定
菜单项Header="Do Stuff"
CommandParameter="{Binding DataContext.SelectedItem, ElementName=UserControl}"
Command="{catel:CommandManagerBinding DoStuffCommand}"
请记住,菜单项中的绑定上下文是 项,而不是 视图模型。如果你想绑定到视图模型,你需要为根网格提供一个名称并像这样绑定:
CommandParameter="{Binding DataContext.SelectedItem, ElementName=rootGrid}"
请注意,使用 UserControl
无效,因为 VM 设置在内部网格中(请参阅文档了解原因的解释)。所以要么使用 UserControl.ViewModel
要么 SomeInnerControl.DataContext
好的,我找到了解决方案,但实际上有点奇怪。
首先调换Command={...}
和CommandParamter={...}
的顺序(如Geert所说)。
然后在经历了很多失败之后我让它开始工作:只需传递到 CommandParameter
-Binding the Markup 而不是其他任何东西。所以我的 XAML MenuItem
代码如下所示:
<MenuItem Header="Do Stuff"
CommandParameter="{Binding}"
Command="{catel:CommandManagerBinding DoStuffCommand}"/>
编辑:
好的,现在我找到了解决方案(我想)。上面的方法大部分时间都有效,但可能 运行 正如@mm8 指出的那样出现问题。
所以我在谷歌上搜索得更多,发现了这个:(见更新 2)
这个答案中给出的解决方案现在对我来说很好用,因为我可以直接绑定到我的 ViewModel
并通过绑定获得所需的值。
我有一个带有 "MainView" 和一些嵌套视图的 Catel 应用程序。
嵌套视图有一个 ListView
,一些项目有一个 ContextMenu
,一些项目有 MenuItems
。
在 MainView 的 ViewModel 中,我创建了一个 TaskCommand<object>
,它将对传递的参数执行某些操作。这个传递的参数应该是ListView
当前的SelectedItem
。此命令正在全局注册到 ICommandManager
.
如果我使用来自 ICommandManager
的绑定命令单击 MenuItem
,则传递的参数将始终为 null
。
这里是相关代码:
NestedView.xaml:
<catel:UserControl
x:Name="UserControl"
x:Class="My.NameSpace.Views.View"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:catel="http://catel.codeplex.com">
...
<ListView ItemsSource="{Binding Items}"
BorderThickness="0"
SelectedItem="{Binding SelectedItem}"
HorizontalContentAlignment="Stretch">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock VerticalAlignment="Center"
Margin="2.5"
Text="{Binding Description}">
<TextBlock.ContextMenu>
<ContextMenu>
<MenuItem Header="Do Stuff"
Command="{catel:CommandManagerBinding DoStuffCommand}"
CommandParameter="{Binding DataContext.SelectedItem, ElementName=UserControl}" />
...
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ListView>
...
</catel:UserControl>
MainViewModel.cs:
public class MainViewModel : ViewModelBase {
...
public ICommand DoStuffCommand { get; }
public MainViewModel(ICommandManager commandManager, IUIVisualizerService uiVisualizerService, IMessageService messageService)
{
...
DoStuffCommand = new TaskCommand<object>(OnDoStuffCommandExecute);
commandManager.CreateCommand(nameof(DoStuffCommand));
commandManager.RegisterCommand(nameof(DoStuffCommand), DoStuffCommand, this);
}
private async Task OnDoStuffCommandExecute(object parameter)
{
// command needs to be in the MainViewModel because there will be some modification on the MainView based on parameter (adding tabs, etc)
Debugger.Break();
}
...
}
如果您需要更多代码,我也可以 post 但这应该足够了。
我还查看了 Catel 的 CommandManager
实现并发现:
/// <summary>
/// Executes the command.
/// </summary>
/// <param name="commandName">Name of the command.</param>
/// <exception cref="ArgumentException">The <paramref name="commandName"/> is <c>null</c> or whitespace.</exception>
/// <exception cref="InvalidOperationException">The specified command is not created using the <see cref="CreateCommand"/> method.</exception>
public void ExecuteCommand(string commandName)
{
Argument.IsNotNullOrWhitespace("commandName", commandName);
lock (_lockObject)
{
Log.Debug("Executing command '{0}'", commandName);
if (!_commands.ContainsKey(commandName))
{
throw Log.ErrorAndCreateException<InvalidOperationException>("Command '{0}' is not yet created using the CreateCommand method", commandName);
}
_commands[commandName].Execute(null);
}
}
我假设如果我单击 MenuItem
将调用此方法并解释该行为。
是否有适当的 solution/workaround 将(绑定)参数传递给我的 OnExecute
方法?
提前致谢
在 xaml
中的命令绑定 之前设置CommandParameter
绑定菜单项Header="Do Stuff" CommandParameter="{Binding DataContext.SelectedItem, ElementName=UserControl}" Command="{catel:CommandManagerBinding DoStuffCommand}"
请记住,菜单项中的绑定上下文是 项,而不是 视图模型。如果你想绑定到视图模型,你需要为根网格提供一个名称并像这样绑定:
CommandParameter="{Binding DataContext.SelectedItem, ElementName=rootGrid}"
请注意,使用 UserControl
无效,因为 VM 设置在内部网格中(请参阅文档了解原因的解释)。所以要么使用 UserControl.ViewModel
要么 SomeInnerControl.DataContext
好的,我找到了解决方案,但实际上有点奇怪。
首先调换Command={...}
和CommandParamter={...}
的顺序(如Geert所说)。
然后在经历了很多失败之后我让它开始工作:只需传递到 CommandParameter
-Binding the Markup 而不是其他任何东西。所以我的 XAML MenuItem
代码如下所示:
<MenuItem Header="Do Stuff"
CommandParameter="{Binding}"
Command="{catel:CommandManagerBinding DoStuffCommand}"/>
编辑:
好的,现在我找到了解决方案(我想)。上面的方法大部分时间都有效,但可能 运行 正如@mm8 指出的那样出现问题。
所以我在谷歌上搜索得更多,发现了这个:(见更新 2)
这个答案中给出的解决方案现在对我来说很好用,因为我可以直接绑定到我的 ViewModel
并通过绑定获得所需的值。