Question

I'm trying to make a reusable ContextMenu for each element of an array like this :

<ContextMenu>
    <ContextMenu.Resources>
        <Style TargetType="{x:Type MenuItem}">
            <Setter Property="Command" Value="{Binding UpdateFooInfoCommand}"/>
            <Setter Property="CommandParameter" Value="{Binding  MyViewModel.FooInfo[0]}" />
        </Style>
    </ContextMenu.Resources>

    <MenuItem 
        Header="Blocked" 
        IsEnabled="{Binding MyViewModel.FooInfo[0].CurrentlyEnabled, Converter={StaticResource ContainsValueConverter}, ConverterParameter={x:Static entities:FooStatus.Blocked}}" 
        IsChecked="{Binding MyViewModel.FooInfo[0].CurrentlySelected, Mode=TwoWay, Converter={StaticResource ContainsValueConverter}, ConverterParameter={x:Static entities:FooStatus.Blocked}}" 
    />
    <MenuItem 
        Header="Working" 
        IsEnabled="{Binding MyViewModel.FooInfo[0].CurrentlyEnabled, Converter={StaticResource ContainsValueConverter}, ConverterParameter={x:Static entities:FooStatus.Working}}" 
        IsChecked="{Binding MyViewModel.FooInfo[0].CurrentlySelected, Mode=TwoWay, Converter={StaticResource ContainsValueConverter}, ConverterParameter={x:Static entities:FooStatus.Working}}" 
    />
    <MenuItem 
        Header="Sleeping" 
        IsEnabled="{Binding MyViewModel.FooInfo[0].CurrentlyEnabled, Converter={StaticResource ContainsValueConverter}, ConverterParameter={x:Static entities:FooStatus.Sleeping}}" 
        IsChecked="{Binding MyViewModel.FooInfo[0].CurrentlySelected, Mode=TwoWay, Converter={StaticResource ContainsValueConverter}, ConverterParameter={x:Static entities:FooStatus.Sleeping}}" 
    />
</ContextMenu>

Using :

[Flags]
public enum FooStatus : byte
{
    None = 0,
    Sleeping = 1 << 0,
    Working = 1 << 1,
    Blocked = 1 << 4
}

But as you can see I'm using the array everywhere so there is no way to reuse this contextMenu but using copy & paste and changing the index, To avoid having a lot of array references I tried this:

<ContextMenu DataContext="{Binding PlacementTarget.DataContext.MyViewModel.FooInfo[0], RelativeSource={RelativeSource Self}}">
    <ContextMenu.Resources>
        <Style TargetType="{x:Type MenuItem}">
            <Setter Property="Command" Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type Control}, Mode=FindAncestor, AncestoLevel=2}, Path=DataContext.UpdateFooInfoCommand }" />
            <Setter Property="CommandParameter" Value="{Binding}" />
        </Style>
    </ContextMenu.Resources>
    .....
</ContextMenu> 

That gets me the datacontext so I no longer need to reference the array everywhere, but I'm still lost on how to make the command work as before and store this resource for multiple usages and just be changing the datacontext.

EDIT

<ContextMenu DataContext="{Binding PlacementTarget.DataContext.MyViewModel.FooInfo[0], RelativeSource={RelativeSource Self}}"
              Tag="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
    <ContextMenu.Resources>
        <Style TargetType="{x:Type MenuItem}">
            <Setter Property="Command" Value="{Binding  RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ContextMenu}}, Path=Tag.UpdateFooInfoCommand}"/>
            <Setter Property="CommandParameter" Value="{Binding}" />
        </Style>
    </ContextMenu.Resources>
    .....
</ContextMenu>

I found the way reading about PlacementTarget, no Idea that the contextmenu was not part of the tree But I still have no clue on how to template this context menu

No correct solution

OTHER TIPS

I use a converter to deal with a similar situation; that approach may work for you. Perhaps something like this:

 public class FooInfoContextMenueConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        var foo = value as FooInfo;
        if (foo!=null)
        {
            var blocked = new MenueItem(){ Header = "Blocked" };
            //Set up bindings ...

            var working = new MenueItem(){ Header = "Working" };
            //Set up bindings ...

            var sleeping = new MenueItem(){ Header = "Sleeping" };
            //Set up bindings...

            return new[] { blocked, working, sleeping };
        }
        return null;
    }
}

You can use it in (for instance) a DataTemplate like this:

<DataTemplate.Resources>
    <foo:FooInfoContextMenueConverter x:Key="fooInfoConv">
</DataTemplate.Resources>
...
<ContextMenu ItemsSource="{Binding Converter={StaticResource fooInfoConv}}" />
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top