gpt4 book ai didi

c# - WPF 命令不适用于 MVVM 应用程序中的子菜单项

转载 作者:行者123 更新时间:2023-12-03 10:33:00 25 4
gpt4 key购买 nike

我有一个菜单,它是在运行时从集合构建的。这一切都如图所示。

Working Menu
但如果菜单包含子项(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/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com