Page 1 of 1

Adding context menu using styles/resources

Posted: 14 Mar 2014, 13:13
by sckriel
Hi All,

I was wondering whether it is possible to give several elements the same ContextMenu by defining it as a static resource and setting it with a style.

Something like this:
<Grid
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
	<Grid.Resources>
		<ContextMenu x:Key="DefaultContextMenu" Background="White">
			<MenuItem Command="ApplicationCommands.Copy" />
			<MenuItem Command="ApplicationCommands.Cut" />
			<MenuItem Command="ApplicationCommands.Paste" />
		</ContextMenu>
		<Style TargetType="{x:Type TextBlock}">
			<Setter Property="ContextMenu" Value="{StaticResource DefaultContextMenu}" />
		</Style>
	</Grid.Resources>
	<StackPanel Orientation="Vertical">
		<TextBlock Text="This is a test"/>
		<TextBlock Text="This is a test"/>
		<TextBlock Text="This is a test"/>
		<TextBlock Text="This is a test"/>
		<TextBlock Text="This is a test"/>
		<TextBlock Text="This is a test"/>
	</StackPanel>
</Grid>

Re: Adding context menu using styles/resources

Posted: 14 Mar 2014, 20:53
by sfernandez
It should work, but there was a bug. We'll fix it for the next release.
Meanwhile you will have to define the ContextMenu for each element, sorry for the inconvenience.

Re: Adding context menu using styles/resources

Posted: 17 Mar 2014, 13:46
by sckriel
Thank you!

All the elements that need the context menu are being created programatically. One of a handful of templates are applied. I have tried to include the ContextMenu in the template, but that does not work. (Only a small, empty menu appears).

I then created a context menu for each element in code and then set the context menu's source as a collection of menu items defined as a resource. That failed since the collection can only be used as the source for a single context menu. Could I possibly clone the collection so that it can be applied to every element's context menu?

Or can you suggest some other workaround? I just want to avoid setting up all the menu items in code.

Re: Adding context menu using styles/resources

Posted: 18 Mar 2014, 11:34
by sfernandez
As it occurs when you define the ContextMenu in a Style, specifying it in a Template won't work because of a bug that will be solved for next release :oops:

If you want to share the same collection along several ItemsControl, you can use a CollectionViewSource:
Collection items = new Collection();
items.Add(new DataItem() { ItemText = "Item1" });
items.Add(new DataItem() { ItemText = "Item2" });
items.Add(new DataItem() { ItemText = "Item3" });
items.Add(new DataItem() { ItemText = "Item4" });
items.Add(new DataItem() { ItemText = "Item5" });
items.Add(new DataItem() { ItemText = "Item6" });
items.Add(new DataItem() { ItemText = "Item7" });
items.Add(new DataItem() { ItemText = "Item8" });
items.Add(new DataItem() { ItemText = "Item9" });

CollectionViewSource cvs1 = new CollectionViewSource();
cvs1.SetSource(items);
contextMenu1.SetItemsSource(cvs1);

CollectionViewSource cvs2 = new CollectionViewSource();
cvs2.SetSource(items);
contextMenu2.SetItemsSource(cvs1);

Re: Adding context menu using styles/resources

Posted: 24 Mar 2014, 13:41
by sckriel
Thanks. That's exactly what I need. I can't get it to work, though.

I created a custom class, inheriting from Window, with the following in the OnInit() function:
       ParentClass::OnInit();

        Rectangle *rec1;
        Rectangle *rec2;

        rec1 = FindName<Rectangle>("Rectangle1");
        rec2 = FindName<Rectangle>("Rectangle2");

        Ptr<MenuItem> menuItem;
        Ptr<TextBlock> header;
        Ptr<Collection> items = *new Collection();

        menuItem = *new MenuItem();
        header = *new TextBlock("Test1");
        menuItem->SetHeader(header.GetPtr());
        items->Add(menuItem.GetPtr());
        menuItem = *new MenuItem();
        header = *new TextBlock("Test2");
        menuItem->SetHeader(header.GetPtr());
        items->Add(menuItem.GetPtr());
        menuItem = *new MenuItem();
        header = *new TextBlock("Test3");
        menuItem->SetHeader(header.GetPtr());
        items->Add(menuItem.GetPtr());

        Ptr<ContextMenu> contextMenu1 = *new ContextMenu();
        Ptr<ContextMenu> contextMenu2 = *new ContextMenu();
        Ptr<CollectionViewSource> cvs1 = *new CollectionViewSource();
        cvs1->SetSource(items.GetPtr());
        contextMenu1->SetItemsSource(cvs1.GetPtr());
        rec1->SetContextMenu(contextMenu1.GetPtr());

        contextMenu2 = *new ContextMenu();
        Ptr<CollectionViewSource> cvs2 = *new CollectionViewSource();
        cvs2->SetSource(items.GetPtr());
        contextMenu2->SetItemsSource(cvs2.GetPtr());
        rec2->SetContextMenu(contextMenu2.GetPtr()); //Crashes when this line executes
...using the following XAML file...
<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="ContextMenuTest"
    Background="Black"
    Width="800" Height="600"
	x:Name="base">
	
	<StackPanel Orientation="Vertical"  Background="Black">
		<Rectangle x:Name="Rectangle1" Fill="Red" Width="200" Height="100" Margin="10"/>
		<Rectangle x:Name="Rectangle2" Fill="Blue" Width="200" Height="100" Margin="10"/>
		<Rectangle x:Name="Rectangle3" Fill="Green" Width="200" Height="100" Margin="10"/>
	</StackPanel>
</Window>
The moment the context menu is set for the second rectangle, an error occurs: "Child already has a logical parent". This is the same error I got when not using the CollectionViewSource.

Re: Adding context menu using styles/resources

Posted: 24 Mar 2014, 16:34
by DigitalKarnage
The best way around this, is not to create a 'ContextMenu' for each item, but only create 1 ContextMenu. Trap when an item opens it's context menu, handle it, and open the 'custom' context menu you created in it's place. This saves on resources, and allows you to do what your looking for.

If you need an example, let me know, i'll get one up as soon as i can.

Re: Adding context menu using styles/resources

Posted: 24 Mar 2014, 17:20
by sfernandez
FrameworkElements can only have 1 Logical Parent. If you assign the same MenuItem to different ContextMenu, you will get the error you mentioned.

In my example I was using data items (an ItemTemplate will also be needed), because that way new elements for each ContextMenu are created wrapping the data items, and the Logical Parent limitation is avoided.

Another alternative is, as DigitalKarnage suggested, using the same ContextMenu on different controls.

Re: Adding context menu using styles/resources

Posted: 25 Mar 2014, 09:18
by sckriel
Thanks for that suggestion DigitalKarnage, I will definitely investigate such an option as my design moves forward.

sfernandez, I finally got it working using ItemTemplates. Thank you!