tags:

views:

26

answers:

1

or conversly can you import a parents menu items into a child window?

So I have an UI that is composed of controls that all have their own context menu, but we also have a datagrid that get's nested inside our generic contaier.

So when you right click the datagrid elements I want to show both the context items I've created for the datagrid and the items from the generic container.

A: 

Here is a solution using a few Attached Properties to manually combine multiple ContextMenus together.

Note: Since it is using XAMLReader to clone the MenuItems, this solution won't preserve bindings in the inherited MenuItems.

using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup;
using System.Xml;

namespace ContextMenuSample
{
    public static class InheritMenu
    {
        public static readonly DependencyProperty ContextMenuProperty = DependencyProperty.RegisterAttached(
            "ContextMenu", typeof(ContextMenu), typeof(InheritMenu), new FrameworkPropertyMetadata(null, OnContextMenuChanged));
        private static void OnContextMenuChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            SetContextMenu(d as FrameworkElement);
        }
        public static void SetContextMenu(FrameworkElement element, ContextMenu value)
        {
            element.SetValue(ContextMenuProperty, value);
        }
        public static ContextMenu GetContextMenu(FrameworkElement element)
        {
            return (ContextMenu)element.GetValue(ContextMenuProperty);
        }

        public static readonly DependencyProperty ParentMenuProperty = DependencyProperty.RegisterAttached(
            "ParentMenu", typeof(ContextMenu), typeof(InheritMenu), new FrameworkPropertyMetadata(null, OnParentMenuChanged));
        private static void OnParentMenuChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            SetContextMenu(d as FrameworkElement);
        }
        public static void SetParentMenu(FrameworkElement element, ContextMenu value)
        {
            element.SetValue(ParentMenuProperty, value);
        }
        public static ContextMenu GetParentMenu(FrameworkElement element)
        {
            return (ContextMenu)element.GetValue(ParentMenuProperty);
        }

        private static void SetContextMenu(FrameworkElement element)
        {
            var context = GetContextMenu(element);
            var parent = GetParentMenu(element);
            if (context == null || parent == null) return;
            var menu = new ContextMenu();
            foreach (var item in parent.Items)
                menu.Items.Add(SimpleXamlClone(item));
            menu.Items.Add(new Separator());
            foreach (var item in context.Items)
                menu.Items.Add(SimpleXamlClone(item));
            element.ContextMenu = menu;
        }

        public static object SimpleXamlClone(object original)
        {
            var xaml = XamlWriter.Save(original);
            var reader = new StringReader(xaml);
            var xml = XmlReader.Create(reader);
            return XamlReader.Load(xml);
        }
    }
}

<Window x:Class="ContextMenuSample.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:l="clr-namespace:ContextMenuSample"
    Title="Window1" Height="250" Width="500">
    <Window.Resources>
        <l:MyCommand x:Key="MainCommand" Message="Main Command!" />
        <l:MyCommand x:Key="ScopeACommand" Message="Scope A Command!" />
        <l:MyCommand x:Key="ScopeBCommand" Message="Scope B Command!" />
        <l:MyCommand x:Key="ScopeCCommand" Message="Scope C Command!" />
        <l:MyCommand x:Key="ScopeDCommand" Message="Scope D Command!" />
        <Style TargetType="Label">
            <Setter Property="HorizontalAlignment" Value="Center" />
        </Style>
        <Style TargetType="TextBox">
            <Setter Property="HorizontalAlignment" Value="Center" />
        </Style>
    </Window.Resources>
    <!-- Main Scope -->
    <Grid Margin="8" Background="LightGray">
        <Grid.ContextMenu>
            <ContextMenu>
                <MenuItem Header="Main Scope" Command="{StaticResource MainCommand}" />
            </ContextMenu>
        </Grid.ContextMenu>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Label Grid.ColumnSpan="2" Grid.Row="0">Main Scope</Label>
        <Label Grid.ColumnSpan="2" Grid.Row="1">Check ContextMenu Here!</Label>

        <!-- Scope A -->
        <StackPanel Grid.Column="0" Grid.Row="2" Margin="8" Background="LightBlue">
            <l:InheritMenu.ParentMenu>
                <Binding Path="ContextMenu" RelativeSource="{RelativeSource AncestorType={x:Type Grid}}" />
            </l:InheritMenu.ParentMenu>
            <l:InheritMenu.ContextMenu>
                <ContextMenu>
                    <MenuItem Header="Scope A" Command="{StaticResource ScopeACommand}" />
                </ContextMenu>
            </l:InheritMenu.ContextMenu>
            <Label>Scope A</Label>
            <Label>Check ContextMenu Here!</Label>

            <!-- Scope C -->
            <StackPanel Margin="8" Background="LightGreen">
                <l:InheritMenu.ParentMenu>
                    <Binding Path="ContextMenu" RelativeSource="{RelativeSource AncestorType={x:Type StackPanel}}" />
                </l:InheritMenu.ParentMenu>
                <l:InheritMenu.ContextMenu>
                    <ContextMenu>
                        <MenuItem Header="Scope C" Command="{StaticResource ScopeCCommand}" />
                    </ContextMenu>
                </l:InheritMenu.ContextMenu>
                <Label>Scope C</Label>
                <Label>Check ContextMenu Here!</Label>
            </StackPanel>
        </StackPanel>

        <!-- Scope B -->
        <StackPanel Grid.Column="1" Grid.Row="2" Margin="8" Background="LightCoral">
            <l:InheritMenu.ParentMenu>
                <Binding Path="ContextMenu" RelativeSource="{RelativeSource AncestorType={x:Type Grid}}" />
            </l:InheritMenu.ParentMenu>
            <l:InheritMenu.ContextMenu>
                <ContextMenu>
                    <MenuItem Header="Scope B" Command="{StaticResource ScopeBCommand}" />
                </ContextMenu>
            </l:InheritMenu.ContextMenu>
            <Label>Scope B</Label>
            <Label>Check ContextMenu Here!</Label>

            <!-- Scope D -->
            <StackPanel Margin="8" Background="LightSalmon">
                <l:InheritMenu.ParentMenu>
                    <Binding Path="ContextMenu" RelativeSource="{RelativeSource AncestorType={x:Type StackPanel}}" />
                </l:InheritMenu.ParentMenu>
                <l:InheritMenu.ContextMenu>
                    <ContextMenu>
                        <MenuItem Header="Scope D" Command="{StaticResource ScopeDCommand}" />
                    </ContextMenu>
                </l:InheritMenu.ContextMenu>
                <Label>Scope D</Label>
                <Label>Check ContextMenu Here!</Label>
            </StackPanel>
        </StackPanel>
    </Grid>
</Window>

public class MyCommand : ICommand
{
    public string Message { get; set; }
    public void Execute(object parameter) { MessageBox.Show(Message); }
    public bool CanExecute(object parameter) { return true; }
    public event EventHandler CanExecuteChanged;
}
Joseph Sturtevant