Treeview ContextMenu with no Click Event Handler in WPF - wpf

I have a TreeView, and I want to be able to add children to it and to the Linq to SQL database that it's bound to.
The best way that I can think of (off the top of my head) would be to have the user right click on a parent node and have the option to add new item from a context menu.
I added a context menu, but when I try to program it in the back end, it says that there is no event handler associated with it.
<TreeView Name="TreeView1" Margin="3" ItemsSource="{Binding ElementName=ManufacturerWarranty, Path=ManufacturerQuery, UpdateSourceTrigger=PropertyChanged}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=WarrantyList}">
<TextBlock Name="txtManufacturerName" Text="{Binding Path=ManufacturerName}">
<TextBlock.ContextMenu>
<ContextMenu>
<MenuItem Name="mnuAddRecord" Header="Add Year Record"></MenuItem>
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Years}">
<TextBlock.ContextMenu>
<ContextMenu>
<MenuItem Name="mnuDelRecord" Header="Remove Year Record"></MenuItem>
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
Whats the correct way to do this?

You forgot to add handler which will execute on click of menuItem
<ContextMenu>
<MenuItem Name="mnuAddRecord" Header="Add Year Record" Click="HandlerInClass"/>
</ContextMenu>

Related

How can I track which element of ItemsControl caused ContexMenu to open?

In my book editor app I need to display themes and subthemes in an hierarchical order, so I made such an markup:
<StackPanel>
<ScrollViewer>
<Grid ColumnDefinitions="auto, *" RowDefinitions="auto">
<ItemsControl Grid.Column="0" Grid.Row="0" Items="{Binding Book.Themes}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<Button Content="{Binding Name}" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.UpdateSubthemesVisibilityCommand}" CommandParameter="{Binding Name}"/>
<ItemsControl Items="{Binding Subthemes}" IsVisible="{Binding AreSubthemesVisible}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding Name}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ContextMenu>
<ContextMenu>
<MenuItem Header="Rename" />
<MenuItem Header="Delete"/>
</ContextMenu>
</ItemsControl.ContextMenu>
</ItemsControl>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ContextMenu>
<ContextMenu>
<MenuItem Header="Rename" Command="{Binding RenameThemeCommand}" CommandParameter="...How to bind it to button which cause menu to open"/>
<MenuItem Header="Delete"/>
</ContextMenu>
</ItemsControl.ContextMenu>
</ItemsControl>
</Grid>
</ScrollViewer>
</StackPanel>
Maybe markup isn't quite clear, but the point is that I have external ItemsControl and internal one. External one's items have structure: button (theme name) + internal ItemsControl which presents subthemes' names as buttons too. Here is Screenshot:
Each ItemsControl has its own ContextMenu, but I can't bind them properly. I want "Rename" MenuItems to be bind to Button.Content of Button which right click caused menu to open. How should I do this here?
I am using MVVM Avalonia, but I hope in WPF it's working the same way so that I could add WPF hashtag to this question.
You don't need to add ContextMenu to ItemsControl. It needs to be added to the items ItemsControl.
This is done in ItemContainerStyle.
In such a case, in ContextMenu DataContext will contain the element on which the ContextMenu is called.
Example:
<Window.Resources>
<spec:StringCollection x:Key="strings">
<sys:String>First</sys:String>
<sys:String>Second</sys:String>
<sys:String>Third</sys:String>
</spec:StringCollection>
<Style x:Key="ItemStyle" TargetType="ContentPresenter">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<TextBlock Text="{Binding}"/>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<ItemsControl ItemsSource="{StaticResource strings}"
ItemContainerStyle="{StaticResource ItemStyle}"/>

Handling TreeViews context menu with WPF and MVVM

I have a application that shows a TreeView. The TreeView has a context menu. I use Caliburn.Micro as MVVM framework.
The line cal:Message.Attach="[Event Click]=[Action Remove()]" is required so that something happens if the ContextMenu-Item is clicked. This line makes that Caliburn.Micro searches in the ViewModel of the TreeItem for a method with the name Remove.
But I want that the click on the ContextMenu Item will call the method Remove of the ViewModel of the screen. How to do that?
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Name}">
<TextBlock.ContextMenu>
<ContextMenu>
<MenuItem cal:Message.Attach="[Event Click]=[Action Remove()]" Name="Remove" Header="Remove item" />
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
Try this:
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Name}">
<TextBlock.ContextMenu>
<ContextMenu>
<MenuItem Tag="{Binding RelativeSource={RelativeSource AncestorType=TreeView}}"
cal:Action.TargetWithoutContext="{Binding Path=PlacementTarget.Tag.DataContext, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}}"
cal:Message.Attach="[Event Click]=[Action RemoveResource()]" Name="Remove" Header="Remove item" />
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
Please refer to my answer here for more information:
Caliburn Micro Action inside ItemContainerStyle - No target found for method
Somebody had the similar problem “Bubbling” events from bound viewmodel goes to parent.
And according to author himself Bind a Command to a Button inside a ListView with Caliburn.Micro.
So just call it by convention or like this:
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<cal:ActionMessage MethodName="{Binding Name}" />
</i:EventTrigger>
</i:Interaction.Triggers>

How to get controls from ItemTemplate in ItemsControl in code behind?

I have a MenuItem that loads sub menu items in ItemsControl.
<MenuItem SubmenuOpened="MenuItem_OnSubmenuOpened" Style="{StaticResource MenuItemImageStyle}" >
<ItemsControl ItemsSource="{Binding WorkshopList}" x:Name="ItemsControl" HorizontalContentAlignment="Stretch" Margin="-35 0" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<MenuItem Header="{Binding Name}" x:Name="MenuItem" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.LeaveCommand}" CommandParameter="{Binding Id}" Style="{StaticResource MenuItemImageStyle}" Padding="20">
</MenuItem>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</MenuItem>
I want to focus first item of ItemsControl when open submenu.
I use SubmenuOpened event, and want to find first item and focus it. but How do i get controls of ItemsControl.
var container = ItemsControl.ItemContainerGenerator.ContainerFromItem(ItemsControl.Items.CurrentItem) as FrameworkElement;
but container is null.

How to find datacontext of parent's parent for dynamic control

We have following xaml code to display context menu and fire command when delete menu is clicked.
For the context menu item, I would like to bind command which is present in DataContext of ItemsControl. We tried with RelativeSource={RelativeSource TemplatedParent} but as the tree is created dynamically (using dataContext) it is not able to find DeleteCommand.
<Grid x:Name="MyGrid" >
<ItemsControl ItemsSource="{Binding Path=TreeRoot.Children}">
<ItemsControl.ItemTemplate>
<HierarchicalDataTemplate>
<StackPanel>
<Label Background="Black" Content="{Binding Path=DisplayText}"/>
<TreeView ItemsSource="{Binding Converter={StaticResource myCompositeNodeConverter}}" >
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:CompositeNode}" ItemsSource="{Binding Path=Children}" >
<TextBlock Text="{Binding Path=DisplayText}" Foreground="Black"></TextBlock>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:LeafNode}" ItemsSource="{Binding Path=Children}">
<TextBlock Text="{Binding Path=DisplayText}"></TextBlock>
</HierarchicalDataTemplate>
</TreeView.Resources>
<TreeView.ContextMenu>
<ContextMenu >
<MenuItem Header="Delete" Command="{Binding DeleteCommand}" />
</ContextMenu>
</TreeView.ContextMenu>
</TreeView>
</StackPanel>
</HierarchicalDataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
How do we bind DeleteCommand for context menu item to DataContext of ItemsControl?

WPF MenuItem Header and HeaderTemplate

I want to bind a list of KeyValuePair to a list of MenuItems.
I thought I should use MenuIten.HeaderTemplate, but it didn't work. I only got blank headers.
<MenuItem
Header="Template"
ItemsSource="{Binding Path=Samples}">
<MenuItem.ItemTemplate>
<DataTemplate>
<MenuItem>
<MenuItem.HeaderTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Key}" FontWeight="Bold"/>
<TextBlock Text="{Binding Path=Value}" FontStyle="Italic" Margin="5,0,0,0"/>
</StackPanel>
</DataTemplate>
</MenuItem.HeaderTemplate> </MenuItem>
</DataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>
Then I replaced MenuItem.HeaderTemplate with MenuItem.Header, it worked.
<MenuItem
Header="Template"
ItemsSource="{Binding Path=Samples}">
<MenuItem.ItemTemplate>
<DataTemplate>
<MenuItem>
<MenuItem.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Key}" FontWeight="Bold"/>
<TextBlock Text="{Binding Path=Value}" FontStyle="Italic" Margin="2,0,0,0"/>
</StackPanel>
</MenuItem.Header>
</MenuItem>
</DataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>
Can anyone explain to me why HeaderTemplate doesn't work here?
Micah is correct. In the first approach I told the menu item how to template itself but never told it what data it binds to! The following works:
<MenuItem
Header="Template"
ItemsSource="{Binding Path=Samples}">
<MenuItem.ItemTemplate>
<DataTemplate>
<MenuItem Header="{Binding}">
<MenuItem.HeaderTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Key}" FontWeight="Bold"/>
<TextBlock Text="{Binding Path=Value}" FontStyle="Italic" Margin="5,0,0,0"/>
</StackPanel>
</DataTemplate>
</MenuItem.HeaderTemplate>
</MenuItem>
</DataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>
Because the HeaderTemplate doesn't have access to the data being bound to the menu item.
The purpose of the Template is to add some elements to the VisualTree. DataTemplate is used for the sub-items ([Sub]MenuItem, ListBoxItem in ListBox, and so on) and is applied to the items holder, it is contrary to the ControlTemplate, wich is applied to the control itself.
What you actually did by this
<MenuItem
Header="Template"
ItemsSource="{Binding Path=Samples}">
<MenuItem.ItemTemplate>
<DataTemplate>
....
</DataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>
is telling "I want take MenuItem content and Insert the data, wich must be visualized". And then insert this insted dots:
<MenuItem Header="{Binding}">... </MenuItem>
So you are inserting additional menu item to the currently iterating menu item. I can't see the point.
Next is more clear:
<MenuItem Header="Template" ItemsSource="{Binding Samples}">
<MenuItem.Resources>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command" Value="{Binding SomeCommand}" />
</Style>
</MenuItem.Resources>
<MenuItem.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>
The HeaderTemplate definition should be a DataTemplate, not direct UI content :
...
<MenuItem.HeaderTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Key}" FontWeight="Bold"/>
<TextBlock Text="{Binding Path=Value}" FontStyle="Italic" Margin="2,0,0,0"/>
</StackPanel>
</DataTemplate>
</MenuItem.HeaderTemplate>
...

Resources