我在互联网上寻找解决方案,但无法在我的示例中找到它。我需要在从代码隐藏生成的上下文菜单项之间添加一个分隔符。我尝试使用如下代码行添加它,但没有成功。
this.Commands.Add(new ToolStripSeparator());
我想知道是否有人可以提供帮助。提前谢谢你。
上下文菜单 XAML:
<Style x:Key="DataGridCellStyle" TargetType="{x:Type DataGridCell}">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu ItemsSource="{Binding Commands}">
<ContextMenu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command" Value="{Binding}" />
<Setter Property="Header" Value="{Binding Path=Text}" />
<Setter Property="CommandParameter" Value="{Binding Path=Parameter}" />
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
</Setter.Value>
</Setter>
C#在方法中添加:
this.Commands = new ObservableCollection<ICommand>();
this.Commands.Add(MainWindow.AddRole1);
this.Commands.Add(MainWindow.AddRole2);
this.Commands.Add(MainWindow.AddRole3);
this.Commands.Add(MainWindow.AddRole4);
//this.Add(new ToolStripSeparator());
this.Commands.Add(MainWindow.AddRole5);
this.Commands.Add(MainWindow.AddRole6);
this.Commands.Add(MainWindow.AddRole7);
我这样做过一次并使用
null
作为我的分隔符。在 XAML 中,如果数据上下文为 null,我将模板设置为使用分隔符
背后的代码:
this.Commands.Add(MainWindow.AddRole4);
this.Add(null);
this.Commands.Add(MainWindow.AddRole5);
XAML 是这样的:
<ContextMenu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command" Value="{Binding}" />
<Setter Property="Header" Value="{Binding Path=Text}" />
<Setter Property="CommandParameter" Value="{Binding Path=Parameter}" />
<Style.Triggers>
<DataTrigger Binding="{Binding }" Value="{x:Null}">
<Setter Property="Template" Value="{StaticResource MenuSeparatorTemplate}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContextMenu.ItemContainerStyle>
希望我的语法正确——我这台机器上没有 IDE 来验证代码
编辑
这是上下文菜单分隔符的示例模板。我把它放在
ContextMenu.Resources
中,尽管你可以把它放在应用程序中任何你想要的地方,只要 ContextMenu 可以访问它。
<ContextMenu.Resources>
<ControlTemplate x:Key="MenuSeparatorTemplate">
<Separator />
</ControlTemplate>
</ContextMenu.Resources>
编辑:
我对这个问题的第一个回答,虽然确实有效,但没有遵循MVVM设计原则。我现在提供一种 MVVM 方法,并在下面留下原始答案以供参考。
你可以创建一个行为来解决这个问题。
XAML:
<Menu>
<MenuItem Header="_File" menu:MenuBehavior.MenuItems="{Binding Path=MenuItemViewModels, Mode=OneWay}">
</MenuItem>
</Menu>
视图模型:
public IEnumerable<MenuItemViewModelBase> MenuItemViewModels => new List<MenuItemViewModelBase>
{
new MenuItemViewModel { Header = "Hello" },
new MenuItemSeparatorViewModel(),
new MenuItemViewModel { Header = "World" }
};
行为:
public class MenuBehavior
{
public static readonly DependencyProperty MenuItemsProperty =
DependencyProperty.RegisterAttached("MenuItems",
typeof(IEnumerable<MenuItemViewModelBase>), typeof(MenuBehavior),
new FrameworkPropertyMetadata(MenuItemsChanged));
public static IEnumerable<MenuItemViewModelBase> GetMenuItems(DependencyObject element)
{
if (element == null)
{
throw (new ArgumentNullException("element"));
}
return (IEnumerable<MenuItemViewModelBase>)element.GetValue(MenuItemsProperty);
}
public static void SetMenuItems(DependencyObject element, IEnumerable<MenuItemViewModelBase> value)
{
if (element == null)
{
throw (new ArgumentNullException("element"));
}
element.SetValue(MenuItemsProperty, value);
}
private static void MenuItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var menu = (MenuItem)d;
if (e.OldValue != e.NewValue)
{
menu.ItemsSource = ConvertViewModelsToFrameworkElements((IEnumerable<MenuItemViewModelBase>)e.NewValue);
}
}
private static IEnumerable<FrameworkElement> ConvertViewModelsToFrameworkElements(IEnumerable<MenuItemViewModelBase> viewModels)
{
var frameworkElementList = new List<FrameworkElement>();
foreach (var viewModel in viewModels)
{
switch (viewModel)
{
case MenuItemViewModel mi:
frameworkElementList.Add(new MenuItem
{
Header = mi.Header,
Command = mi.Command,
Icon = mi.Icon
});
break;
case MenuItemSeparatorViewModel s:
frameworkElementList.Add(new Separator());
break;
}
}
return frameworkElementList;
}
}
课程:
public class MenuItemViewModelBase
{
}
public class MenuItemViewModel : MenuItemViewModelBase
{
public object Header { get; set; }
public ICommand Command { get; set; }
public object Icon { get; set; }
}
public class MenuItemSeparatorViewModel : MenuItemViewModelBase
{
}
原答案:
或者,不是将 ContextMenu 绑定到命令集合,而是将其绑定到 FrameworkElements 集合,然后您可以将 MenuItems 或 Separators 直接添加到集合中,并让 Menu 控件完成所有模板制作....
<Style x:Key="DataGridCellStyle" TargetType="{x:Type DataGridCell}">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu ItemsSource="{Binding Commands}" />
</Setter.Value>
</Setter>
</Style>
C#:
this.Commands = new ObservableCollection<FrameworkElement>();
this.Commands.Add(new MenuItem {Header = "Menuitem 2", Command = MainWindow.AddRole1});
this.Commands.Add(new MenuItem {Header = "Menuitem 2", Command = MainWindow.AddRole2});
this.Commands.Add(new MenuItem {Header = "Menuitem 3", Command = MainWindow.AddRole3});
this.Commands.Add(new MenuItem {Header = "Menuitem 4", Command = MainWindow.AddRole4});
this.Commands.Add(new Separator);
this.Commands.Add(new MenuItem {Header = "Menuitem 5", Command = MainWindow.AddRole5});
this.Commands.Add(new MenuItem {Header = "Menuitem 6", Command = MainWindow.AddRole6});
this.Commands.Add(new MenuItem {Header = "Menuitem 7", Command = MainWindow.AddRole7});
刚刚在我的应用程序中使用了这种方法 - 这样分隔符看起来也更好。
我修改了上面Rachel提供的解决方案,修正了Separator的样式。我意识到这篇文章很旧,但仍然是 Google 上的顶级结果之一。在我的情况下,我将它用于菜单与上下文菜单,但同样应该有效。
XAML
<Menu ItemsSource="{Binding MenuItems}">
<Menu.Resources>
<ControlTemplate x:Key="MenuSeparatorTemplate">
<Separator>
<Separator.Style>
<Style TargetType="{x:Type Separator}" BasedOn="{StaticResource ResourceKey={x:Static MenuItem.SeparatorStyleKey}}"/>
</Separator.Style>
</Separator>
</ControlTemplate>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Header" Value="{Binding MenuItemHeader}" />
<Setter Property="Command" Value="{Binding MenuItemCommand}" />
<Setter Property="CommandParameter" Value="{Binding MenuItemCommandParameter}" />
<Setter Property="ItemsSource" Value="{Binding MenuItemCollection}" />
<Style.Triggers>
<DataTrigger Binding="{Binding }" Value="{x:Null}">
<Setter Property="Template" Value="{StaticResource MenuSeparatorTemplate}" />
</DataTrigger>
</Style.Triggers>
</Style>
</Menu.Resources>
</Menu>
使用 ItemTemplateSelector:
public class MenuItemTemplateSelector : DataTemplateSelector
{
public DataTemplate SeparatorTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
var menuItem = container.GetVisualParent<MenuItem>();
if (menuItem == null)
{
throw new Exception("Unknown MenuItem type");
}
if (menuItem.DataContext == null)
{
return SeparatorTemplate;
}
return menuItem.ItemTemplate;
}
}
Xaml:
<ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}"
ItemsSource="{Binding Path=ViewContentMenuItems}" >
<ContextMenu.ItemTemplateSelector>
<templateSelectors:MenuItemTemplateSelector>
<templateSelectors:MenuItemTemplateSelector.SeparatorTemplate>
<DataTemplate>
<Separator />
</DataTemplate>
</templateSelectors:MenuItemTemplateSelector.SeparatorTemplate>
</templateSelectors:MenuItemTemplateSelector>
</ContextMenu.ItemTemplateSelector>
</ContextMenu>
在模型中:
public ObservableCollection<MenuItem> ViewContentMenuItems
{
get
{
var temp = new ObservableCollection<MenuItem>();
temp.Add(null);
temp.Add(CreateFolderMenuItem);
return temp;
}
}
private MenuItem CreateFolderMenuItem
{
get
{
var createFolderMenuItem = new MenuItem()
{
Header = "New Folder",
Icon = new Image
{
Source = new BitmapImage(new Uri("/icons/folderWinCreate.png", UriKind.Relative)),
Height = 16,
Width = 16
}
};
Message.SetAttach(createFolderMenuItem, "CreateDocumentsFolder");//Caliburn example
return createFolderMenuItem;
}
}
WPF 提供了您正在寻找的东西——它被称为“分隔符”:
this.Commands.Add(new Separator());
要为MVVM正确执行此操作,您必须定义自己的项目接口(例如IMenuItem),为Menu/ContextMenu和MenuItem创建派生类,在这些类中覆盖以下虚拟保护方法:
ItemsControl.PrepareContainerForItemOverride
ItemsControl.ClearContainerForItemOverride
ItemsControl.GetContainerForItemOverride
ItemsControl.IsItemItsOwnContainerOverride
确保此方法为 IMenuItem 类型的项目创建您的新派生自 MenuItem 类型的项目并绑定所有需要的属性,在这里您可以区分不同类型的 IMenuItem 以显示正常项目、分隔符或一些薄别的。对于未知类型调用基础实现。
现在,如果您将从 Menu/ContextMenu 控件的新派生的 ItemsSource 属性与 IMenuItem 的集合绑定,它将显示您预期的结果,而无需现在查看 ViewModel 上的内容边。