我有一个菜单,它是在运行时从集合构建的。这一切都如图所示。
但如果菜单包含子项(Child1、Child2 等),则 ReactiveCommand 菜单命令 永远不会被调用。
如果我从菜单中删除所有子项,以便菜单仅包含父项,则调用 MenuCommand。我对 WPF 相当陌生。我在示例应用程序中重新创建了问题(下面的代码)。 VS 中没有可见的绑定(bind)错误。
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel();
}
}
public class Service
{
public Service(string menuHeading, string menuSubHeading)
{
MenuHeading = menuHeading;
MenuSubHeading = menuSubHeading;
}
public string MenuHeading { get; set; }
public string MenuSubHeading { get; set; }
}
public static class MenuBuilder
{
public static ReactiveList<MenuItem> Build(ReactiveList<Service> services)
{
ReactiveList<MenuItem> menuItems = new ReactiveList<MenuItem>();
foreach (var service in services)
{
AddOrUpdate(menuItems, service);
}
return menuItems;
}
private static void AddOrUpdate(ReactiveList<MenuItem> menu, Service service)
{
if (menu.Any((_ => _.Header.ToString() == service.MenuHeading)))
{
var item = menu.FirstOrDefault(x => x.Header.ToString() == service.MenuHeading);
item.Items.Add(new MenuItem() { Header = service.MenuSubHeading });
//if above line removed MenuCommand works
}
else
{
menu.Add(new MenuItem() { Header = service.MenuHeading });
var item = menu.FirstOrDefault(x => x.Header.ToString() == service.MenuHeading);
item.Items.Add(new MenuItem() { Header = service.MenuSubHeading });
//if above line removed MenuCommand works
}
}
}
public class MainWindowViewModel : ReactiveObject
{
public MainWindowViewModel()
{
MenuCommand = ReactiveCommand.Create<Object>(selectedItem => OnMenuItemSelected(selectedItem));
MenuCommand.Execute().Subscribe();
}
public ReactiveCommand<Object, Unit> MenuCommand { get; }
private ReactiveList<MenuItem> servicesMenu;
private ReactiveList<Service> Services = new ReactiveList<Service>()
{
new Service("Parent1", "Child1"),
new Service("Parent2", "Child1"),
new Service("Parent2", "Child2"),
};
public ReactiveList<MenuItem> ServicesMenu
{
get
{
if (servicesMenu == null)
{
servicesMenu = MenuBuilder.Build(Services);
return servicesMenu;
}
else
{
return servicesMenu;
}
}
}
private void OnMenuItemSelected(Object selectedItem)
{
//This method is only called when the menu does not contain any child items
}
}
<Grid>
<StackPanel Orientation="Vertical">
<Button Name="Button" Content="Button" Padding="5" HorizontalAlignment="Left"
Tag="{Binding RelativeSource={RelativeSource Self}, Path=DataContext}">
<Button.ContextMenu>
<ContextMenu x:Name="MainMenu" ItemsSource="{Binding ServicesMenu}"
DataContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
<ContextMenu.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Command"
Value="{Binding DataContext.MenuCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Button}}}" />
<Setter Property="CommandParameter"
Value="{Binding RelativeSource={RelativeSource Self}}" />
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
</Button.ContextMenu>
</Button>
</StackPanel>
</Grid>
根据 Glenn 的建议更新了 XAML
<Grid>
<StackPanel Orientation="Vertical">
<Button Name="Button" Content="Button" Padding="5" HorizontalAlignment="Left"
Tag="{Binding RelativeSource={RelativeSource Self}, Path=DataContext}">
<Button.ContextMenu>
<ContextMenu x:Name="MainMenu" ItemsSource="{Binding ServicesMenu}"
DataContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
<ContextMenu.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Header" Value="{Binding Header}" />
<Setter Property="Command" Value="{Binding Command}" />
<!--<Setter Property="Command" Value="{Binding MenuCommand}" /> was also tried-->
<Setter Property="CommandParameter" Value="{Binding}" />
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
</Button.ContextMenu>
</Button>
</StackPanel>
</Grid>
最佳答案
我怀疑这是因为子项目放置目标不会像您期望的那样是 Button,而是父 MenuItem。
我过去解决这个问题的一种方法是对这些类型的菜单项使用 MVVM 方法。
为您的项目创建一个菜单项 VM(您在上面将其称为服务)(类似于您已经在做的事情)。在 VM 中有一个 Command 属性,并将您的命令作为其构造函数的一部分传入。然后您可以从您的项目容器样式中执行 {Binding MenuCommand}。
也不要直接在 ViewModel 中创建 MenuItem,而是直接绑定(bind)到服务。我还建议直接在您的服务内将您的子服务创建为 ObservableCollection,然后在您的项目容器中设置 ItemsSource 属性以绑定(bind)到您的服务的子子项。
关于c# - WPF 命令不适用于 MVVM 应用程序中的子菜单项,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48114412/