Caliburn Micro 的动态菜单和子菜单,如何绑定命令?

Dynamic menus and submenus with Caliburn Micro, how can I bind the commands?

我在 WPF 项目中使用 Caliburn Micro,我希望插件组件能够填充工具栏。每个插件都有一个顶级菜单项,如果他们愿意,可以用子菜单填充它。

<ToolBar>
    <Menu x:Name="ToolBarMenuItems">
        <Menu.ItemContainerStyle>
            <Style TargetType="{x:Type MenuItem}">
                <Setter Property="Header" Value="{Binding Path=Text}" />
                <Setter Property="ItemsSource" Value="{Binding Path=Children}" />
            </Style>
        </Menu.ItemContainerStyle>
    </Menu>
</ToolBar>

ToolBarMenuItems 是接口的 BindableCollection:

public BindableCollection<IMenuItemViewModel> ToolBarMenuItems { get; set; }

public interface IMenuItemViewModel
{
    string Text { get; set; }

    ObservableCollection<IMenuItemViewModel> Children { get; set; }

    bool CanRunCommand();

    void RunCommand();
}

就创建菜单而言,这工作正常,问题是将菜单项连接到 RunCommand/CanRunCommand。 Dynamic menus with Caliburn micro 解释了如何在 MenuItem 上执行此操作:

<Menu>
    <MenuItem x:Name="ToolBarMenuItems" DisplayMemberPath="Text" Header="MyMenu" cal:Message.Attach="RunToolbarCommand($originalsourcecontext)"/>
</Menu>

其中RunToolBarCommand是view model中的一个方法public void RunToolbarCommand(IMenuItemViewModel menuItem)而$originalsourcecontext是指在bootstrapper中设置如下

MessageBinder.SpecialValues.Add("$originalsourcecontext", context =>
{
    var args = context.EventArgs as RoutedEventArgs;
    var fe = args?.OriginalSource as FrameworkElement;
    return fe?.DataContext;
});

但正如我所说,我需要对整个菜单执行此操作,而不仅仅是在 MenuItem 上执行此操作,但我不知道如何使用 Caliburn Micro 来绑定方法。

将 setter 添加到您的 Style 以设置 cal:Message.Attach 附加 属性 并传递 $executionContext:

<Menu x:Name="ToolBarMenuItems">
    <Menu.ItemContainerStyle>
        <Style TargetType="{x:Type MenuItem}">
            <Setter Property="Header" Value="{Binding Path=Text}" />
            <Setter Property="ItemsSource" Value="{Binding Path=Children}" />
            <Setter Property="cal:Message.Attach" Value="RunCommand($executionContext)" />
        </Style>
    </Menu.ItemContainerStyle>
</Menu>

您需要接口和实现中的上下文才能阻止事件冒泡:

public void RunCommand(ActionExecutionContext context)
{
    if (context?.EventArgs is RoutedEventArgs routedEventArgs)
        routedEventArgs.Handled = true;

    MessageBox.Show("Run!");
}

请参阅 问题了解更多信息。