动态创建上下文菜单

Dynamically create ContextMenu

在我的应用程序中,我想动态地建立一个上下文菜单。第一个 MenuItem 是静态的,第二个应该是 SeparatorSeparator 之后的所有项目都是在运行时动态创建的。

我不想使用代码隐藏,因为我正在使用 MVVM 模式。 我现在的想法是使用以下三个实现创建一个名为 IAppMenuIteminterface

在我的应用程序的视图模型中,我创建了一个 ObservableCollection<IAppMenuItem>,其中包含 ContextMenu-Items。

到这里一切正常。我的问题是 ContextMenu-Items 在 UI.

中的显示

我尝试在视图的资源中使用以下 DataTemplate 设置正确的控件。

<DataTemplate DataType="{x:Type model:SeparatorMenuItem}">
    <Separator/>
</DataTemplate>

<DataTemplate DataType="{x:Type model:ModifyMenuItem}">
    <MenuItem Header="Edit items"/>
</DataTemplate>

<DataTemplate DataType="{x:Type model:ExecuteMenuItem}">
    <MenuItem Header="{Binding DisplayText}"/>
</DataTemplate>

我的ContextMenu的定义是:

<ContextMenu ItemsSource="{Binding MenuItemsCollection}"/>

DataTemplate 工作正常,但控件绘制在 MenuItem 内。例如,对于 Separator,我在 UI 中看到 Separator-Control 位于 MenuItem-Control 中。但我需要 Separator 作为控件。

有人知道如何将 DataTemplate 中的控件直接设置为上下文菜单吗?


更新:

完整的 ContextMenu 看起来像:

<ToggleButton Margin="0,0,10,0"
              AutomationProperties.Name="Update"
              AutomationProperties.AutomationId="Update_List"
              Content="Update"
              AttachedProperties:ButtonExtensions.IsDropDownButton="True"
              Style="{StaticResource GenericToggleButtonStyle}">
    <ToggleButton.ContextMenu>
        <controls:CustomContextMenu ItemsSource="{Binding MenuItemsCollection}">
            <ContextMenu.Resources>
                <ResourceDictionary>
                    <DataTemplate DataType="{x:Type model:ExecuteMenuItem}">
                        <MenuItem Header="{Binding DisplayText}"/>
                    </DataTemplate>
                    <DataTemplate DataType="{x:Type model:ModifyMenuItem}">
                        <MenuItem Header="Edit items"/>
                    </DataTemplate>
                </ResourceDictionary>
            </ContextMenu.Resources>
        </controls:CustomContextMenu>       
    </ToggleButton.ContextMenu>
</ToggleButton>

GenericToggleButtonStyle 只是:

<Style x:Key="GenericToggleButtonStyle" TargetType="ToggleButton"
       BasedOn="{StaticResource {x:Type ToggleButton}}">
    <Setter Property="MinWidth" Value="80" />
    <Setter Property="Height" Value="22" />
    <Setter Property="Padding" Value="3,1" />
</Style>

这是 MenuItems

的屏幕截图

似乎当您在 ContextMenu 上设置 ItemSource 属性 时,默认使用的 ItemContainerMenuItem。这就是 Separator 呈现为 MenuItem 的原因。

您可以通过实现自己的 ContextMenu 继承自 ContextMenu 的控件来解决此问题。

您需要覆盖 IsItemItsOwnContainerOverrideGetContainerForItemOverride 方法。

请看我下面的例子:

public class CustomContextMenu 
    : ContextMenu
{
    private bool _mustGenerateAsSeparator = false;

    protected override bool IsItemItsOwnContainerOverride(object item)
    {
         _mustGenerateAsSeparator = (item is SeparatorMenuItem);

         return base.IsItemItsOwnContainerOverride(item);            
    }

    protected override System.Windows.DependencyObject GetContainerForItemOverride()
    {
        if (_mustGenerateAsSeparator)
        {
            return new Separator { Style = this.FindResource(MenuItem.SeparatorStyleKey) as System.Windows.Style };
        }
        else 
        {
            return base.GetContainerForItemOverride();                      
        }            
    }
}

Style = this.FindResource(MenuItem.SeparatorStyleKey) as System.Windows.Style 是必需的,因为您必须应用默认的 MenuItem.SeparatorStyleKey 样式才能获得默认的分隔符样式。

这个link给我指明了正确的方向http://drwpf.com/blog/category/item-containers/

如何在XAML中使用自定义控件:

window 声明:xmlns:cnt="your namespace"

<Label Content="Dynamic Menu">
        <Label.ContextMenu>                
            <cnt:CustomContextMenu x:Name="contextMenu" ItemsSource="{Binding MenuItemsCollection}">                    
                <ContextMenu.Resources>
                    <ResourceDictionary>                            
                        <DataTemplate DataType="{x:Type model:ExecuteMenuItem}">
                            <MenuItem Header="{Binding DisplayText}"></MenuItem>
                        </DataTemplate>
                        <DataTemplate DataType="{x:Type model:ModifyMenuItem}">
                            <MenuItem Header="{Binding DisplayText}"></MenuItem>
                        </DataTemplate>                            
                    </ResourceDictionary>
                </ContextMenu.Resources>
            </model:CustomContextMenu>                            
        </Label.ContextMenu>
    </Label> 

问题的第二部分:

更新您的数据模板以使用 TextBlock,因为它们将在 MenuItem 内呈现,请参见下文:

<DataTemplate DataType="{x:Type model:ModifyMenuItem}">
    <TextBlock Header="Edit items"/>
</DataTemplate>

<DataTemplate DataType="{x:Type model:ExecuteMenuItem}">
    <TextBlock Header="{Binding DisplayText}"/>
</DataTemplate>