使用ItemContainerStyle时如何在ContextMenu的MenuItem中设置图标

9
所以我有一个“上下文菜单”,其中包含一个“菜单项”,它会分解成一系列名称列表:
<ContextMenu>
    <MenuItem Header="Set As Default For" ItemsSource="{Binding Source={StaticResource Names}}">
       <MenuItem.ItemContainerStyle>
           <Style TargetType="MenuItem">
                <Setter Property="Header" Value={Binding Name}/>
                <Setter Property="Command" Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=MenuItem}, Path=DataContext.DoSomething}" />
                <Setter Property="CommandParameter" Value="{Binding }" />
            </Style>
         </MenuItem.ItemContainerStyle>
     </MenuItem>
</ContextMenu>

现在以上代码可以正常工作并显示我的名称列表。现在我想使用Pack URI在每个名称旁边添加一个图标。所以从this question中我可以看到最好的方法是对Header进行模板化,所以我首先尝试了这个问题。
<ContextMenu>
    <MenuItem Header="Set As Default For" ItemsSource="{Binding Source={StaticResource Names}}">
       <MenuItem.ItemContainerStyle>
           <Style TargetType="MenuItem">
                <Setter Property="Header">
                    <Setter.Value>
                         <StackPanel>
                               <Image Width="20" Height="20" Source="/MyProj;component/Resources/MyImg.png" />
                              <ContentPresenter Content="{Binding Name}" />
                          </StackPanel>
                     </Setter.Value>
                 </Setter>
                 <Setter Property="Command" Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=MenuItem}, Path=DataContext.DoSomething}" />
                 <Setter Property="CommandParameter" Value="{Binding }" />
            </Style>
         </MenuItem.ItemContainerStyle>
     </MenuItem>
</ContextMenu>

但是这给了我一个错误:

指定的元素已经是另一个元素的逻辑子元素。请先断开连接。

所以在一些研究之后,我尝试了:

<ContextMenu>
    <MenuItem Header="Set As Default For" ItemsSource="{Binding Source={StaticResource Names}}">
       <MenuItem.ItemContainerStyle>
           <Style TargetType="MenuItem">
                <Setter Property="Header">
                    <Setter.Value>
                         <ControlTemplate>
                             <StackPanel>
                                   <Image Width="20" Height="20" Source="/MyProj;component/Resources/MyImg.png" />
                                    <ContentPresenter Content="{Binding Name}" />
                              </StackPanel>
                           </ControlTemplate>
                     </Setter.Value>
                 </Setter>
                 <Setter Property="Command" Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=MenuItem}, Path=DataContext.DoSomething}" />
                 <Setter Property="CommandParameter" Value="{Binding }" />
            </Style>
         </MenuItem.ItemContainerStyle>
     </MenuItem>
</ContextMenu>

现在我的所有名称都是ControlTemplate,没有显示图标...如何通过ItemContainerStyle向上下文菜单的菜单项添加图标?编辑:我已经尝试过:
   <Setter Property="Header" Value="{Binding Name}"/>
   <Setter Property="Icon">
       <Setter.Value>
           <Image Width="20" Height="20" Source="/MyProj;component/Resources/MyImg.png" />
       </Setter.Value>
    </Setter>

我可以帮您进行翻译。该文段的含义是“当我在菜单中添加多个选项时,只有最后一个选项显示图标?”

可以使用 HeaderTemplate 代替 Header,并使用 DataTemplate 代替 ControlTemplate。但个人建议将 Icon 属性设置为 Style 中的另一个 Setter。你的代码将把图标放置在 MenuItem 的内容部分,而不是 Icon 部分。 - dkozl
@dkozl,你说得对,使用HeaderTemplateDataTemplate是可行的。而且你也说得对,我的代码将图标放在了标题中,这不是我计划中的。你知道如何在样式中使用Pack URI来设置图标吗? - JKennedy
尝试将x:Shared设置为false,对于你的Style,像这样:<Style TargetType="MenuItem" x:Shared="False"> - dkozl
@dkozl,我似乎找不到我的样式中的x:shared属性? - JKennedy
抱歉,它只能应用于“ResourceDictionary”中的项目。您需要将“Style”移动到“ContextMenu.Resources”中,并设置一些“x:Key”,然后将“ItemContainerStyle”设置为“StaticResource”。还要确保您设置了“x:Shared”,而不是“x:shared”。 - dkozl
1个回答

13
问题在于不能在多个地方使用同一视觉元素。你可以通过使用HeaderTemplate代替Header,并使用DataTemplate代替ControlTemplate来解决这个问题。
<Style TargetType="MenuItem">
   <Setter Property="HeaderTemplate">
      <Setter.Value>
         <DataTemplate>
            <StackPanel>
               <Image Width="20" Height="20" Source="/MyProj;component/Resources/MyImg.png" />
               <ContentPresenter Content="{Binding Name}" />
            </StackPanel>
         </DataTemplate>
      </Setter.Value>
   </Setter>
   <!-- other setters -->
</Style>
请注意,此解决方案将把图标放在MenuItem的内容部分中,而不是图标本身。另一个解决方案是将MenuItemIcon属性设置为另一个Setter,但在这种情况下,Image需要作为单独的Resource,并将x:Shared设置为false,否则您最终会遇到相同的问题,即只有最后一个项目具有图标。
<ContextMenu>
   <ContextMenu.Resources>
      <Image Width="20" Height="20" Source="/MyProj;component/Resources/MyImg.png" x:Key="myMenuIcon" x:Shared="False" />
   </ContextMenu.Resources>
   <MenuItem Header="Set As Default For" ItemsSource="{Binding Source={StaticResource Names}}">
      <MenuItem.ItemContainerStyle>
         <Style TargetType="{x:Type MenuItem}">
            <Setter Property="Header" Value="{Binding Name}"/>
            <Setter Property="Icon" Value="{StaticResource myMenuIcon}"/>
         </Style>
      </MenuItem.ItemContainerStyle>
   </MenuItem>
</ContextMenu>

我已经使用x:Shared属性复制了第二个示例,但是在运行时仍然会出现错误Shared attribute in namespace 'http://schemas.microsoft.com/winfx/2006/xaml' can be used only in compiled resource dictionaries.。不过还是感谢您的帮助。 - JKennedy
我已经测试过了。你要把 x:Shared 设置在哪个元素上?它应该只设置在 ContextMenu.Resources 中的 myMenuIcon 元素上。 - dkozl
它是针对myMenuIcon设置的,我的项目是一个插件,是一个.dll文件,并在运行时加载到我的应用程序中...这可能会影响它吗?我的错误被抛回到我的主应用程序。 - JKennedy
@user1 你需要将它放入<Window.Resources>中。然后你就可以在Source上应用绑定,然后就可以在正确的位置上获得动态图标了 :) - bytecode77

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接