代码之家  ›  专栏  ›  技术社区  ›  pedrito

不同来源的Contextmenu:为不同的menuitems设置不同的数据绑定

  •  1
  • pedrito  · 技术社区  · 7 年前

    我想实现一种上下文菜单行为,例如visual studio为工具栏提供了一个可检查项列表和一个命令列表。 contextmenu项应该来自视图模型中的一些observablecollection。

    VS ContextMenu for Toolboxes

    因为它们来自不同的来源。我想过使用一个复合集合来实现这一点。一个集合的绑定应该是到Command,另一个绑定到IsChecked/IsChecked。我还想使用分隔符。

    https://stackoverflow.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>
    
    2 回复  |  直到 7 年前
        1
  •  1
  •   pedrito    7 年前

    经过多次尝试,我终于找到了一个适合我的解决方案。不幸的是,它包含了许多针对不同内容的变通方法,并不是一个非常直接的解决方案。我不敢相信创建一个简单的contextmenu这么难。

    如前所述,我无法使用datatemplate,因为这将导致由分隔符引起的异常,分隔符未实现某些属性,例如 IsCheckable ContextMenu.ItemContainerStyle ContextMenu.Resources 仅适用于真实菜单项 (见H.B.的答案 https://stackoverflow.com/a/18948356/5381620 )

    <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>
    

    https://social.msdn.microsoft.com/Forums/vstudio/en-US/42cd1554-de7a-473b-b977-ddbd6298b3d0/binding-error-when-using-compositecollection-for-menuitems?forum=wpf

    我仍然不明白的是,如果我只在应用程序中设置ContentAlignment,为什么仍然会出现绑定错误。资源或上下文。资源。出于某种原因,有必要同时设置这两个参数。如果有人能给我解释一下,我会很高兴的。

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

    对于绑定,我使用了一些MenuItemVM类,或多或少类似于此,我可以根据menuitem是可检查的还是命令来设置属性。

    class ContextMenuItemVM
    {
    
        public string Name { get; }
        public bool IsCheckable { get; }
        public bool IsChecked { get; set; }
        public ICommand Cmd { get; }
    }
    
        2
  •  0
  •   mm8    7 年前

    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>
    

    数据模板 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;
        }
    }