来自不同来源的上下文菜单:为不同的菜单项设置不同的数据绑定

问题描述:

我想实现上下文菜单行为,例如, visual studio具有工具栏,可选项目清单和命令列表。 上下文菜单项应该来自视图模型中的一些observable集合。来自不同来源的上下文菜单:为不同的菜单项设置不同的数据绑定

VS ContextMenu for Toolboxes

由于这些来自不同的来源。我想使用复合集合来实现这一点。一个集合的绑定应该是Command,其他是IsChecked/IsChecked。我也想用分隔符。

我遇到的问题是关于绑定。我不能使用完整的menuitem的datatemplate,因为它不包含IsChecked属性。因此,我正在使用ItemContainerStyle(请参见https://*.com/a/29130774/5381620)。 只要我只使用1个收集容器,并有1个来源一切都很好。 但是,插入来自其他来源(或分隔符)的项目会将“样式”绑定应用于所有菜单项,而“分隔符”将导致异常。

<ContextMenu> 
    <ContextMenu.Resources> 
     <CollectionViewSource x:Key="ContextMenuColCollection" Source="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path= DataContext.HeaderContextMenu}"/> 
    </ContextMenu.Resources> 
    <ContextMenu.ItemTemplate> 
     <DataTemplate DataType="{x:Type vm:Collection1VM}" > 
      <TextBlock Text="{Binding Name}"/> 
     </DataTemplate> 
    </ContextMenu.ItemTemplate> 
    <ContextMenu.ItemsSource> 
     <CompositeCollection> 
      <MenuItem Header="Settings"/> 
      <Separator /> 
      <CollectionContainer Collection="{Binding Source={StaticResource ContextMenuColCollection}}"/> 
     </CompositeCollection> 
    </ContextMenu.ItemsSource> 
    <ContextMenu.ItemContainerStyle> 
     <Style TargetType="{x:Type MenuItem}"> 
      <Setter Property="IsCheckable" Value="True"/> 
      <Setter Property="IsChecked" Value="{Binding IsSelected}"/> 
     </Style> 
    </ContextMenu.ItemContainerStyle> 
</ContextMenu> 
+0

如果我的做法是愚蠢的莫名其妙或者有什么更好的方法来实现这种类型的上下文菜单的,这也将帮助我很多。 – pedrito

想了很多,我终于找到了一个解决方案,它是适合我后。不幸的是,它包含了很多针对不同内容的解决方法,并不是一个简单的解决方案。我不能相信这是创建一个简单的上下文菜单这很难。

如上所述,我无法使用数据模板,因为这会导致由分隔符引起的异常,该分隔符不会实现一些属性,例如, IsCheckable。 移动从ContextMenu.ItemContainerStyleContextMenu.Resources样式只适用于这真正的MenuItems (看到这里https://*.com/a/18948356/5381620 HB的答案)

<ContextMenu> 
    <ContextMenu.Resources> 
     <Style TargetType="MenuItem"> 
      <Setter Property="Header" Value="{Binding Name}"/> 
      <Setter Property="IsCheckable" Value="{Binding IsCheckable}"/> 
      <Setter Property="IsChecked" Value="{Binding IsChecked}"/> 
      <Setter Property="Command" Value="{Binding Cmd}"/> 
      <!-- this is necessary to avoid binding error, see explanation below--> 
      <Setter Property="HorizontalContentAlignment" Value="Left"/> 
      <Setter Property="VerticalContentAlignment" Value="Center"/> 
     </Style> 
     <!-- collectionViewSource necessary for behavior described here 
     https://social.msdn.microsoft.com/Forums/vstudio/en-US/b15cbd9d-95aa-47c6-8068-7ae9f7dca88a/collectioncontainer-does-not-support-relativesource?forum=wpf 
     --> 
     <CollectionViewSource x:Key="MenuCmds" Source="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path= DataContext.CmdObsColl}"/> 
     <CollectionViewSource x:Key="MenuCheckable" Source="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path= DataContext.CheckableObsCol}"/> 
    </ContextMenu.Resources> 
    <ContextMenu.ItemsSource> 
     <CompositeCollection> 
      <CollectionContainer Collection="{Binding Source={StaticResource MenuCmds}}"/> 
      <Separator /> 
      <CollectionContainer Collection="{Binding Source={StaticResource MenuCheckable}}"/> 
     </CompositeCollection> 
    </ContextMenu.ItemsSource> 
</ContextMenu> 

如果使用一个以上的收集容器,还有一些奇怪的绑定错误,这可以通过将以下内容添加到Application.Resources来处理。有关更多信息,请参见msdn论坛中的以下链接。 https://social.msdn.microsoft.com/Forums/vstudio/en-US/42cd1554-de7a-473b-b977-ddbd6298b3d0/binding-error-when-using-compositecollection-for-menuitems?forum=wpf

我还是不明白为什么我仍然得到绑定错误,如果我只在Application.Resources或Context.Resources中设置ContentAlignment。出于某种原因,有必要设置两者。如果有人能向我解释这一点,我会很高兴。

<Application.Resources> 
    <Style TargetType="MenuItem"> 
     <Setter Property="HorizontalContentAlignment" Value="Left" /> 
     <Setter Property="VerticalContentAlignment" Value="Center" /> 
    </Style> 
</Application.Resources> 

对于结合我使用一些MenuItemVM类,这是或多或少像这样与在哪里可以取决于如果菜单项应该是一个可检验的一个或一个命令上设置属性。

class ContextMenuItemVM 
{ 

    public string Name { get; } 
    public bool IsCheckable { get; } 
    public bool IsChecked { get; set; } 
    public ICommand Cmd { get; } 
} 

移动DataTemplate<ContextMenu.Resources>并删除ItemTemplate

<ContextMenu> 
    <ContextMenu.Resources> 
     <CollectionViewSource x:Key="ContextMenuColCollection" Source="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path= DataContext.HeaderContextMenu}"/> 
     <DataTemplate DataType="{x:Type vm:Collection1VM}" > 
      <TextBlock Text="{Binding Name}"/> 
     </DataTemplate> 
     <local:Converter x:Key="conv" /> 
    </ContextMenu.Resources> 
    <ContextMenu.ItemsSource> 
     <CompositeCollection> 
      <MenuItem Header="Settings"/> 
      <Separator /> 
      <CollectionContainer Collection="{Binding Source={StaticResource ContextMenuColCollection}}"/> 
     </CompositeCollection> 
    </ContextMenu.ItemsSource> 
    <ContextMenu.ItemContainerStyle> 
     <Style TargetType="{x:Type MenuItem}"> 
      <Setter Property="IsCheckable" Value="True"/> 
      <Setter Property="IsChecked" Value="{Binding Path=., Converter={StaticResource conv}}"/> 
     </Style> 
    </ContextMenu.ItemContainerStyle> 
</ContextMenu> 

然后DataTemplate应适用于仅Collection1VM对象。

当涉及到IsChecked财产,你既可以忽略任何约束力的警告或实现一个转换器,例如:

public class Converter : IValueConverter 
{ 
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 
    { 
     Collection1VM vm = value as Collection1VM; 
     return vm != null && vm.IsChecked; 
    } 

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 
    { 
     return value; 
    } 
} 
+0

我没有想到ValueConverter。使用ValueConverter,我应该能够对来自不同来源的不同元素做出反应。所以这部分我会解决。但是,仍然会抛出异常,因为它没有任何属性“IsChecked”或“IsCheckable”。 – pedrito