动态创建上下文菜单
Dynamically create ContextMenu
在我的应用程序中,我想动态地建立一个上下文菜单。第一个 MenuItem
是静态的,第二个应该是 Separator
。 Separator
之后的所有项目都是在运行时动态创建的。
我不想使用代码隐藏,因为我正在使用 MVVM 模式。
我现在的想法是使用以下三个实现创建一个名为 IAppMenuItem
的 interface
- ModifyMenuItem(静态
MenuItem
)
- SeparatorMenuItem
- ExecuteMenuItem(动态
MenuItem
在我的应用程序的视图模型中,我创建了一个 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>
这是 MenuItem
s
的屏幕截图
似乎当您在 ContextMenu
上设置 ItemSource
属性 时,默认使用的 ItemContainer
是 MenuItem
。这就是 Separator
呈现为 MenuItem
的原因。
您可以通过实现自己的 ContextMenu
继承自 ContextMenu
的控件来解决此问题。
您需要覆盖 IsItemItsOwnContainerOverride
和 GetContainerForItemOverride
方法。
请看我下面的例子:
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>
在我的应用程序中,我想动态地建立一个上下文菜单。第一个 MenuItem
是静态的,第二个应该是 Separator
。 Separator
之后的所有项目都是在运行时动态创建的。
我不想使用代码隐藏,因为我正在使用 MVVM 模式。
我现在的想法是使用以下三个实现创建一个名为 IAppMenuItem
的 interface
- ModifyMenuItem(静态
MenuItem
) - SeparatorMenuItem
- ExecuteMenuItem(动态
MenuItem
在我的应用程序的视图模型中,我创建了一个 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>
这是 MenuItem
s
似乎当您在 ContextMenu
上设置 ItemSource
属性 时,默认使用的 ItemContainer
是 MenuItem
。这就是 Separator
呈现为 MenuItem
的原因。
您可以通过实现自己的 ContextMenu
继承自 ContextMenu
的控件来解决此问题。
您需要覆盖 IsItemItsOwnContainerOverride
和 GetContainerForItemOverride
方法。
请看我下面的例子:
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>