Change context menu in WPF TreeView for data
This is example for ListBox, I think you can easily modify it to work with TreeView.
XAML:
...<Window.Resources> <ContextMenu x:Key="FileContextMenu"> ... </ContextMenu> <ContextMenu x:Key="DirContextMenu"> ... </ContextMenu> <local:ItemToContextMenuConverter x:Key="ContextMenuConverter" /> </Window.Resources>...<ListBox x:Name="SomeList"> <ListBox.ItemTemplate> <DataTemplate> <Label Content="{Binding Path=Name}" ContextMenu="{Binding Converter={StaticResource ContextMenuConverter}}"/> </DataTemplate> </ListBox.ItemTemplate></ListBox>
Code:
class Item{ public string Name { get; set; } public bool IsFile { get; set; }}[ValueConversion(typeof(Item), typeof(ContextMenu))]public class ItemToContextMenuConverter : IValueConverter{ public static ContextMenu FileContextMenu; public static ContextMenu DirContextMenu; public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { Item item = value as Item; if (item == null) return null; return item.IsFile ? FileContextMenu : DirContextMenu; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new Exception("The method or operation is not implemented."); }}private void Window_Loaded(object sender, RoutedEventArgs e) { ItemToContextMenuConverter.FileContextMenu = this.Resources["FileContextMenu"] as ContextMenu; ItemToContextMenuConverter.DirContextMenu = this.Resources["DirContextMenu"] as ContextMenu; List<Item> items = new List<Item>(); items.Add(new Item() { Name = "First", IsFile = true }); items.Add(new Item() { Name = "Second", IsFile = false }); SomeList.ItemsSource = items; }
Hi I am doing similar thing on TreeView and I don't like that ItemToContextMenuConverter is executed on each item even if it is not used. It's maybe ok in a small project but if you add Enable/Disable code for each MenuItem than it can be slow.
This is maybe not the best (I just started with WPF), but I will share it with you.
Menu Resources:
<Window.Resources> <ContextMenu x:Key="MnuFolderFavorites" StaysOpen="True"> <MenuItem Header="Remove from Favorites" Click="MnuFolder_RemoveFromFavorites_Click"></MenuItem> </ContextMenu> <ContextMenu x:Key="MnuFolder" StaysOpen="True"> <MenuItem Header="New Folder"></MenuItem> <MenuItem Header="Rename" x:Name="MnuFolderRename" Click="MnuFolder_Rename_Click"></MenuItem> <MenuItem Header="Add to Favorites" Click="MnuFolder_AddToFavorites_Click"></MenuItem> </ContextMenu></Window.Resources>
TreeView:
<TreeView x:Name="TvFolders"> <TreeView.ItemTemplate> <HierarchicalDataTemplate DataType="{x:Type data:Folder}" ItemsSource="{Binding Items}"> <StackPanel Orientation="Horizontal" PreviewMouseRightButtonDown="TvFoldersStackPanel_PreviewMouseRightButtonDown"> <Image Width="20" Height="20" Source="{Binding ImagePath}" /> <TextBlock Text="{Binding Title}" Margin="5,0,0,0" /> </StackPanel> </HierarchicalDataTemplate> </TreeView.ItemTemplate></TreeView>
Code:
private void TvFoldersStackPanel_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e) { ((StackPanel) sender).ContextMenu = null; Data.Folder item = (Data.Folder) ((StackPanel) sender).DataContext; if (!item.Accessible) return; if (item.Parent != null && item.Parent.Title.Equals("Favorites")) { ((StackPanel) sender).ContextMenu = MainWindow.Resources["MnuFolderFavorites"] as ContextMenu; } else { ((StackPanel) sender).ContextMenu = MainWindow.Resources["MnuFolder"] as ContextMenu; foreach (MenuItem menuItem in ((StackPanel) sender).ContextMenu.Items) { switch (menuItem.Name) { case "MnuFolderRename": { menuItem.IsEnabled = item.Parent != null; break; } } } }}private void MnuFolder_RemoveFromFavorites_Click(object sender, RoutedEventArgs e) { string path = ((Data.Folder)((MenuItem)sender).DataContext).FullPath; Settings.Default.FolderFavorites.Remove(path); Settings.Default.Save(); FavoritesFolder?.AddFavorites(true);}