简体   繁体   中英

WPF: Contextmenu MenuItem with static and dynamic entries and Command binding

I have a Contextmenu which contains MenuItems. So far, so good. In a submenu ("Colors"), I want to have static entries ("Reset color" and "Custom color") as well a dynamic values. It currently looks like this: Contextmenu .

So, my question is:

What do I need to do to get the same styling for both: static and dynamic (via my CustomColorCollection) entries and still have the possibility to separately set the Command and CommandParameter?

The dynamic values are bound to a public static ObservableCollection<ContextMenuColor> ColorList { get; set; } = new ObservableCollection<ContextMenuColor>() public static ObservableCollection<ContextMenuColor> ColorList { get; set; } = new ObservableCollection<ContextMenuColor>() public static ObservableCollection<ContextMenuColor> ColorList { get; set; } = new ObservableCollection<ContextMenuColor>() with ContextMenuColor being a small class like this:

    public class ContextMenuColor
    {
        public string Title { get; set; }
        public string Color { get; set; }
        public ContextMenuColor(string title, string color)
        {
            this.Title = title;
            this.Color = color;
        }
    }

When I click on a menuitem, I want it to run my ICommand SetColorCommand with ContextMenuColor.Color as a CommandParameter.

This is what I have so far in my XAML:

<MenuItem Header="Colors">
    <MenuItem.Resources>
        <CollectionViewSource x:Key="CustomColorCollection" Source="{Binding ColorList}"/>
    </MenuItem.Resources>
    <MenuItem.ItemsSource>
        <CompositeCollection>
            <MenuItem Header="Reset color" Command="{Binding SetColorCommand}" CommandParameter="#DCDCDC" UsesItemContainerTemplate="False">
                <MenuItem.Icon>
                    <Rectangle Width="20" Height="20" Fill="#DCDCDC"/>
                </MenuItem.Icon>
            </MenuItem>
            <MenuItem Header="Custom color" Command="{Binding SetColorCommand}" CommandParameter="custom" UsesItemContainerTemplate="False">
                <MenuItem.Icon>
                    <Rectangle Width="20" Height="20">
                        <Rectangle.Fill>
                            <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                                <GradientStop Color="#FF5DFF00" Offset="0"/>
                                <GradientStop Color="#FFC500FF" Offset="1"/>
                                <GradientStop Color="#FF0A7D8F" Offset="0.5"/>
                            </LinearGradientBrush>
                        </Rectangle.Fill>
                    </Rectangle>
                </MenuItem.Icon>
            </MenuItem>
            <Separator/>
            <CollectionContainer Collection="{Binding Source={StaticResource CustomColorCollection}}" />
        </CompositeCollection>
    </MenuItem.ItemsSource>
    <MenuItem.ItemTemplate>
        <DataTemplate> <!-- this should only be used for the CustomColorCollection -->
            <MenuItem x:Name="MyMenuItem" Header="{Binding Title}">
                <MenuItem.Icon>
                    <Rectangle Width="20" Height="20" Fill="{Binding Color}"/>
                </MenuItem.Icon>
            </MenuItem>
        </DataTemplate>
    </MenuItem.ItemTemplate>
    <MenuItem.ItemContainerStyle>  <!-- this should only be used for the CustomColorCollection -->
        <Style TargetType="MenuItem">
            <Setter Property="Background" Value="{Binding Path=Background, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}}"/>
            <Setter Property="Command" Value="{Binding Path=DataContext.SetColorCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}}"/>
            <Setter Property="CommandParameter" Value="{Binding Color}"/>
        </Style>
    </MenuItem.ItemContainerStyle>
</MenuItem>

PS: This one helped but is still a little different from my problem as I was unable to set Command and CommandParameter for the proposed TextBlock in the DataTemplate.

I now have a working solution!

First thing: I do not have to use a DataTemplate at all. Second was to add a rectangle in the Resources: <Rectangle x:Key="ColorRectangle" x:Shared="False" Width="20" Height="20" Fill="{Binding Color}" />

Third thing was to change the ItemContainerStyle to use the defined <Rectangle> and also use the general style for MenuItems (via BasedOn ):

<MenuItem.ItemContainerStyle>
    <Style TargetType="MenuItem" BasedOn="{StaticResource {x:Type MenuItem}}">
        <Setter Property="Header" Value="{Binding Title}"/>
        <Setter Property="Icon" Value="{StaticResource ColorRectangle}" />
        <Setter Property="Command" Value="{Binding Path=DataContext.SetColorCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}}"/>
        <Setter Property="CommandParameter" Value="{Binding Color}"/>
    </Style>
</MenuItem.ItemContainerStyle>

For completeness, this is the full code:

<MenuItem Header="Colors">
    <MenuItem.Resources>
        <CollectionViewSource x:Key="CustomColorCollection" Source="{Binding ColorList}"/>
        <Rectangle x:Key="ColorRectangle" x:Shared="False" Width="20" Height="20" Fill="{Binding Color}" />
    </MenuItem.Resources>
    <MenuItem.ItemsSource>
        <CompositeCollection>
            <MenuItem Header="Reset color" Command="{Binding SetColorCommand}" CommandParameter="#DCDCDC" UsesItemContainerTemplate="False">
                <!--this is TextColorBrush-->
                <MenuItem.Icon>
                    <Rectangle Width="20" Height="20" Fill="#DCDCDC"/>
                </MenuItem.Icon>
            </MenuItem>
            <MenuItem Header="Custom color" Command="{Binding SetColorCommand}" CommandParameter="custom" UsesItemContainerTemplate="False">
                <MenuItem.Icon>
                    <Rectangle Width="20" Height="20">
                        <Rectangle.Fill>
                            <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                                <GradientStop Color="#FF5DFF00" Offset="0"/>
                                <GradientStop Color="#FFC500FF" Offset="1"/>
                                <GradientStop Color="#FF0A7D8F" Offset="0.5"/>
                            </LinearGradientBrush>
                        </Rectangle.Fill>
                    </Rectangle>
                </MenuItem.Icon>
            </MenuItem>
            <Separator/>
            <CollectionContainer Collection="{Binding Source={StaticResource CustomColorCollection}}" />
        </CompositeCollection>
    </MenuItem.ItemsSource>
    <MenuItem.ItemContainerStyle>
        <Style TargetType="MenuItem" BasedOn="{StaticResource {x:Type MenuItem}}">
            <Setter Property="Header" Value="{Binding Title}"/>
            <Setter Property="Icon" Value="{StaticResource ColorRectangle}" />
            <Setter Property="Command" Value="{Binding Path=DataContext.SetColorCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}}"/>
            <Setter Property="CommandParameter" Value="{Binding Color}"/>
        </Style>
    </MenuItem.ItemContainerStyle>
</MenuItem>

Only one question remains: for the static entries (ie "Reset color" and "Custom color") I get these warnings in the Output window of Visual Studio 2017:

System.Windows.Data Error: 40 : BindingExpression path error: 'Title' property not found on 'object' ''SoundtrackRow' (HashCode=9307971)'. BindingExpression:Path=Title; DataItem='SoundtrackRow' (HashCode=9307971); target element is 'MenuItem' (Name=''); target property is 'Header' (type 'Object')
System.Windows.Data Error: 40 : BindingExpression path error: 'Color' property not found on 'object' ''SoundtrackRow' (HashCode=9307971)'. BindingExpression:Path=Color; DataItem='SoundtrackRow' (HashCode=9307971); target element is 'MenuItem' (Name=''); target property is 'CommandParameter' (type 'Object')

They are, of course, since the static entries can not bind to Title and Color as they simply do not exist for them.

How can I get rid of these warnings?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM