WPF Context Menu command Binding - c#

I have tried few solutions given in SO, but still i'm unable to trigger the command.
XAML:
<Image Source="{Binding CurrentImage.Source, Mode=OneWay}" Grid.Row="0" Grid.Column="1" Tag="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType= Window}}">
<Image.ContextMenu>
<ContextMenu DataContext="{Binding PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Edit Image" Command="{Binding PlacementTarget.Tag.EditImageCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}}"></MenuItem>
</ContextMenu>
</Image.ContextMenu>
ViewModel:
private ICommand _EditImageCommand;
public ICommand EditImageCommand
{
get
{
return _EditImageCommand ?? (_EditImageCommand = new CommandHandler(() => EditImage(), _canExecute));
}
}
public void EditImage()
{
}

Change:
private ICommand _EditImageCommand;
private ICommand EditImageCommand
{
get
{
return _EditImageCommand ?? (_EditImageCommand = new CommandHandler(() => EditImage(), _canExecute));
}
}
public void EditImage()
{
}
to
private ICommand _EditImageCommand;
public ICommand EditImageCommand // has to be public
{
get
{
return _EditImageCommand ?? (_EditImageCommand = new CommandHandler(() => EditImage(), _canExecute));
}
}
public void EditImage()
{
}
Commands have to be public to be accessed (or internal for the sake of correctness).
Also, change your xaml to:
<Image Source="{Binding CurrentImage.Source, Mode=OneWay}" Grid.Row="0" Grid.Column="1" Tag="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType= Window}}">
<Image.ContextMenu>
<ContextMenu DataContext="{Binding PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Edit Image" Command="{Binding EditImageCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}}"></MenuItem>
</ContextMenu>
</Image.ContextMenu>

Have changed my XAML to,
<Window.Resources>
<local:ImageList x:Key="SliderViewModel"></local:ImageList>
</Window.Resources>
<Image Source="{Binding CurrentImage.Source, Mode=OneWay}" Grid.Row="0" Grid.Column="1">
<Image.ContextMenu>
<ContextMenu>
<MenuItem Header="Edit Image" Command="{Binding EditImageCommand, Source={StaticResource SliderViewModel}}"></MenuItem>
</ContextMenu>
</Image.ContextMenu>
</Image>
Working fine. Thanks

Another nice workaround is to declare a static instance of your view model in App.xaml: <ViewModelTypeName x:Key="ViewModelName" d:IsDataSource="True" />and bind like this Command="{Binding Source={StaticResource ViewModelName}, Path=MyCommand}" I had the same issue when i needed to bind from Window and menuitem's native DataContext simultaneously. Also this solution looks not that complicated.

Related

How do I get double click edit to work on one row in my list view?

I have a simple list view with gridview to display each row.
I added a key binding for delete which is working fine.
<ListView.InputBindings>
<KeyBinding Key="Delete" Command="{Binding Path=DeleteKeyCommand}" CommandParameter="{Binding ElementName=DatabasesLstVw, Path=SelectedItem}"/>
</ListView.InputBindings>
But when I add a Mousebinding for LeftDoubleClick to edit its not firing the command.
<MouseBinding Gesture="LeftDoubleClick" Command="{Binding Path=LeftDoubleClickCommand}" CommandParameter="{Binding ElementName=DatabasesLstVw, Path=SelectedItem}" />
After spending the last two hours trying to figure it out the only thing I have come up with is that its firing the double click on the entire list view and not the listview item???
How do I get double click edit to work on one row in my list view? I am using MVVM I don't want to break that so I cant use code behind to hack it. There must be a way to map the command back to my view model.
Update more code:
<ListView x:Name="DatabasesLstVw" ItemsSource="{Binding Path=ClientDetails.Databases}" ItemContainerStyle="{StaticResource alternatingStyle}" AlternationCount="2" Grid.Row="2" Grid.ColumnSpan="4" VerticalAlignment="Top" >
<ListView.InputBindings>
<KeyBinding Key="Delete" Command="{Binding Path=DeleteKeyCommand}" CommandParameter="{Binding ElementName=DatabasesLstVw, Path=SelectedItem}"/>
<MouseBinding Gesture="LeftDoubleClick" Command="{Binding Path=LeftDoubleClickCommand}" CommandParameter="{Binding ElementName=DatabasesLstVw, Path=SelectedItem}" />
</ListView.InputBindings>
As the referenced answer is missing some code, this is how it should be:
public class AddToInputBinding
{
public static System.Windows.Input.InputBinding GetBinding(DependencyObject obj)
{
return (System.Windows.Input.InputBinding)obj.GetValue(BindingProp);
}
public static void SetBinding(DependencyObject obj, System.Windows.Input.InputBinding value)
{
obj.SetValue(BindingProp, value);
}
public static readonly DependencyProperty BindingProp = DependencyProperty.RegisterAttached(
"Binding", typeof(System.Windows.Input.InputBinding), typeof(AddToInputBinding), new PropertyMetadata
{
PropertyChangedCallback = (obj, e) =>
{
((UIElement)obj).InputBindings.Add((System.Windows.Input.InputBinding)e.NewValue);
}
});
}
Then, in your XAML, you would do something like this:
<Window x:Class="WpfApplication.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<ResourceDictionary>
<Style TargetType="ListViewItem">
<Setter Property="local:AddToInputBinding.Binding">
<Setter.Value>
<MouseBinding Gesture="LeftDoubleClick" Command="{Binding DataContext.ItemDoubleClick,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListView}}}"
CommandParameter="{Binding}"/>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
</Window.Resources>
<Grid>
<ListView ItemsSource="{Binding Patients}">
<ListView.View>
<GridView>
<GridViewColumn Header="Test" />
</GridView>
</ListView.View>
</ListView>
</Grid>
In your viewModel, the command definition would be like this:
RelayCommand<string> _ItemDoubleClick;
public ICommand ItemDoubleClick
{
get
{
if (_ItemDoubleClick == null)
{
_ItemDoubleClick = new RelayCommand<string>(this.ItemDoubleClickExecuted,
param => this.ItemDoubleClickCanExecute());
}
return _ItemDoubleClick;
}
}
private bool ItemDoubleClickCanExecute()
{
return true;
}
private void ItemDoubleClickExecuted(string item)
{
//In item you've got the text of double clicked ListViewItem
}
Note that in this sample, the ListView binded ObservableCollection is of type string. If this was other type, you should change the types in the ICommand definitions. Don't forget also to set the Window DataContext to your ViewModel.
Hope this is clearer now.

Cannot Access Command in WPF UserControl's DataGrid Row

I am not able to execute Command (TestCommand) in user control may be because DataGrid of usercontrol using FileDetailsList(List)
The following is the wpf form and i am using MVVM
<Window>
<TabControl>
<TabItem Header="Result">
<USERCONTROL:FileSearchResult></USERCONTROL:FileSearchResult>
</TabItem>
</TabControl>
</Window>
The below is the user control
<UserControl>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="30"></RowDefinition>
</Grid.RowDefinitions
<DataGrid Grid.Row="0" ItemSource="{Binding FileDetailsList}">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button>View
<Button.ContextMenu>
<ContextMenu FontSize="11">
<MenuItem Command="{Binding TestCommand}" CommandParameter="{Binding FileId}" Header="Splitter Errors"/>
</ContextMenu>
</Button.ContextMenu>
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Binding="{Binding FileId}" Header="File ID"/>
<DataGridTextColumn Binding="{Binding FileName}" Header="File Name"/>
<DataGrid.Columns>
<DataGrid Grid.Row="0">
</Grid>
</UserControl>
The below is the view model
public class FileDetailsViewModel : INotifyPropertyChanged
{
private List<FileDetail> _fileDetailsList = new List<FileDetail>();
public RelayCommand<Int32> TestCommand { get; private set; }
public FileDetailsViewModel()
{
TestCommand = new RelayCommand<int>(OpenTestCommand);
}
private void OpenTestCommand(int fileId)
{
///Some code
}
public List<FileDetail> FileDetailsList { get { return _fileDetailsList; } set { _fileDetailsList = value; NotifyPropertyChanged("FileDetailsList"); } }
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
Please help solve this issue
you try this it will work 100%,
<Button Tag="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}">
View
<!-- Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path=DataContext. -->
<Button.ContextMenu>
<ContextMenu FontSize="11">
<MenuItem Command="{Binding Path=PlacementTarget.Tag.TestCommand,
RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}"
CommandParameter="{Binding FileId}"
Header="Splitter Errors" />
</ContextMenu>
</Button.ContextMenu>
</Button>
use menuitem command binding like this,
<MenuItem Command="{Binding DataContext.TestCommand, RelativeSource={RelativeSource AncestorType=DataGrid }}"
WPF rule number 1:
When binding does not work, check your output window!
you would see error saying, that TestCommand does not exist in FileDetails. This is because DataContext of each row in DataGrid and all descendant elements including your ContextMenu is not FileDetailsViewModel but FileDetail
easiest workaround is to move TestCommand to FileDetail class.
alternativelly, you can modify the binding to point to FileDetailsViewModel. Give name to root element in your UserControl or Window, e.g: LayoutRoot. Then use ElementName in the binding:
<MenuItem Command="{Binding LayoutRoot.DataContext.TestCommand, ElementName=LayoutRoot}"
CommandParameter="{Binding FileId}"
Initialized="ContextMenu_Initialized"/>
private void ContextMenu_Initialized(object sender, EventArgs e)
{
NameScope.SetNameScope((ContextMenu)sender, NameScope.GetNameScope(this));
}
another way how to fix the binding is workaround proposed by #jobyjames85, but it is little hacky. On the other side it is plain xaml solution with no codebehind involved

Binding property in ContextMenu from Parent's Parent's DataContext

I wanted to bind a property in a MenuItem within an ItemsControl container. I have a hierarchical model ; Item class has a list of SubItem. The ViewModel itself has a list of Item (so there's two ItemsControl, one being in the ItemTemplate of the other).
I found several other questions on SO asking about that (this one for instance) and I learned that the Visual Tree of a ContextMenu is separated from the rest.
I managed to do it and it works (but it feels kind of hacky) by "transferring" the model's data via the Tag property.
Here's the two model classes:
public class SubItem
{
public int Current { get; set; }
public Subitem(int current)
{
Current = current;
}
}
public class Item
{
public ObservableCollection<SubItem> SubItems { get; set; }
public string Parent { get; set; }
public Item(string Parent)
{
Parent = Parent;
SubItems = new ObservableCollection<SubItem>();
}
}
Here's the view model:
public class ViewModel
{
public ObservableCollection<Item> Items { get; set; }
public ViewModel()
{
Items = new ObservableCollection<Item>();
FillData();
}
private void FillData()
{
//...
}
}
And here's the ItemsControl at the root of the page (the page's DataContext is an instance of the ViewModel class):
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding SubItems}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Current}"
Tag="{Binding DataContext.Parent, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}, Mode=FindAncestor}}">
<TextBlock.ContextMenu>
<ContextMenu>
<MenuItem Header="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}, Mode=FindAncestor}}"/>
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
My question is: is this the right way to do it? I tried many other ways to avoid binding the property to the Tag but couldn't make it work.
The ugly part is specifically:
Tag="{Binding DataContext.Parent, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}, Mode=FindAncestor}}"
Followed by:
Header="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}, Mode=FindAncestor}}"
I think it's kind of ugly and I'm sure there's a better way to do that. The solution must work with .NET 4.0.
You need to add a tag to the menu's container and bind to it using placement target.
View this example:
<StackPanel x:Key="ConfigurationListItem" x:Shared="False" Tag="{Binding ElementName=UserControl}">
<StackPanel Orientation="Horizontal">
<Button>
<Button.InputBindings>
<MouseBinding Gesture="LeftDoubleClick" Command="{Binding ElementName=UserControl, Path=LaunchCommand}" CommandParameter="{Binding}" />
<MouseBinding Gesture="LeftClick" Command="{Binding ElementName=UserControl, Path=SelectCommand}" CommandParameter="{Binding}" />
</Button.InputBindings>
</StackPanel>
<StackPanel.ContextMenu>
<ContextMenu DataContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}" Tag="{Binding}">
<MenuItem Header="Sync Environment Dependencies"
Command="{Binding Parent.PlacementTarget.Tag.SyncEnvironmentCommand, RelativeSource={RelativeSource Self}}"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}, Path=PlacementTarget.DataContext}" />
</ContextMenu>
</StackPanel.ContextMenu>
</StackPanel>

Delete item from ItemControl

My class is has a ObservableCollection of my viewmodel class and I set the itemsource of the Itemcontrol in xaml as below
<ItemsControl ItemsSource="{Binding ConditionItems}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Expander Background="#FFD0D7EB">
<StackPanel>
<Button Content="Delete" HorizontalAlignment="Right" Width="180" Margin="0,0,12,10" Command="{Binding DeleteItem}" CommandParameter="{Binding}">
</Button> </StackPanel>
</Expander>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
For some reason my DeleteItem is never called.
private RelayCommand _DeleteRule;
private void DoDeleteRule(object item)
{
if (item != null)
{
MessageBox.Show("in del");
}
}
public ICommand DeleteItem
{
get
{
if (_DeleteRule == null)
_DeleteRule = new RelayCommand(o => DoDeleteRule(o));
return _DeleteRule;
}
}
Am I doing anything wrong in xaml?
The ItemsControl is bound using {Binding ConditionItems}, so it expects the DeleteItem command to be inside the subitems of that list. I guess this is not the case, the DeleteItem exists on the ViewModel.
You could bind to the DataContext of the Window for example, where you can find the DeleteItem command. Or create a proxy element.
I found it. My xaml should be
<Button Content="Delete" Command="{Binding DataContext.DeleteItem,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ItemsControl}}}" CommandParameter="{Binding}">
</Button>

retrieve datacontext of treeviewitem from child xaml

I have a DataBound treeview, from which the treeviewitems are created/displayed and feature a context menu. This is working correctly. I am then trying to use a custom menuitem class so that the datacontext of the treeviewitem can be retrieved through the commands fired from the contextmenu. Sorry if this is confusing. I may be going the wrong way about doing this.
Here is my xaml.
<UserControl x:Class="Pipeline_General.Custom_Controls.ProjectTree"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:pm="clr-namespace:Pipeline_General"
mc:Ignorable="d"
DataContext = "{Binding RelativeSource={RelativeSource Self}}"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<TreeView Name="StructureTree" Background="{x:Static pm:myBrushes.defaultBG}" ItemsSource="{Binding ProjectList}">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="True"/>
<Setter Property="Margin" Value="5,5,5,5" />
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu Background="{x:Static pm:myBrushes.defaultBG}" Foreground="{x:Static pm:myBrushes.gray}">
<pm:ProjectTreeMenuItem Header="Add Episode.."
Element="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type TreeViewItem}}}"
Command="{x:Static pm:MyCommands.AddEpCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Self}}"
Margin="20,0"/>
<pm:ProjectTreeMenuItem Header="Add Sequence.."
Element="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type TreeViewItem}}}"
Command="{x:Static pm:MyCommands.AddSeqCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Self}}"
Margin="20,0"/>
<pm:ProjectTreeMenuItem Header="Add Scene.."
Element="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type TreeViewItem}}}"
Command="{x:Static pm:MyCommands.AddScCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Self}}"
Margin="20,0"/>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</TreeView.ItemContainerStyle>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type pm:ProjectRoot}" ItemsSource="{Binding Episodes}">
<TextBlock Text="{Binding Path=Title}" Foreground="{x:Static pm:myBrushes.pink}"
FontFamily="Calibri" FontSize="18"/>
<HierarchicalDataTemplate.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="True"/>
<Setter Property="Margin" Value="5,5,5,5" />
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu Background="{x:Static pm:myBrushes.defaultBG}" Foreground="{x:Static pm:myBrushes.gray}">
<pm:ProjectTreeMenuItem Header="Add Sequence.."
Element="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type TreeViewItem}}}"
Command="{x:Static pm:MyCommands.AddSeqCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Self}}"
Margin="20,0"/>
<pm:ProjectTreeMenuItem Header="Add Scene.."
Element="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type TreeViewItem}}}"
Command="{x:Static pm:MyCommands.AddScCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Self}}"
Margin="20,0"/>
<Separator Height="15" Margin="20,0"/>
<pm:ProjectTreeMenuItem Header="Cut"
Element="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type TreeViewItem}}}"
Command="{x:Static pm:MyCommands.CutCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Self}}"
Margin="20,0"/>
<pm:ProjectTreeMenuItem Header="Un-Cut"
Element="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type TreeViewItem}}}"
Command="{x:Static pm:MyCommands.UnCutCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Self}}"
Margin="20,0"/>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</HierarchicalDataTemplate.ItemContainerStyle>
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type pm:Episode}" ItemsSource="{Binding Sequences}">
<TextBlock Text="{Binding Path=Key}" Foreground="{x:Static pm:myBrushes.yellow}"
FontFamily="Calibri" FontSize="14"/>
<HierarchicalDataTemplate.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="True"/>
<Setter Property="Margin" Value="5,5,5,5" />
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu Background="{x:Static pm:myBrushes.defaultBG}" Foreground="{x:Static pm:myBrushes.gray}">
<pm:ProjectTreeMenuItem Header="Add Scene.."
Element="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type TreeViewItem}}}"
Command="{x:Static pm:MyCommands.AddScCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Self}}"
Margin="20,0"/>
<Separator Height="15" Margin="20,0"/>
<pm:ProjectTreeMenuItem Header="Cut"
Element="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type TreeViewItem}}}"
Command="{x:Static pm:MyCommands.CutCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Self}}"
Margin="20,0"/>
<pm:ProjectTreeMenuItem Header="Un-Cut"
Element="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type TreeViewItem}}}"
Command="{x:Static pm:MyCommands.UnCutCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Self}}"
Margin="20,0"/>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</HierarchicalDataTemplate.ItemContainerStyle>
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type pm:Sequence}" ItemsSource="{Binding Scenes}">
<TextBlock Text="{Binding Path=Key}" Foreground="{x:Static pm:myBrushes.yellow}"
FontFamily="Calibri" FontSize="14"/>
<HierarchicalDataTemplate.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu Background="{x:Static pm:myBrushes.defaultBG}" Foreground="{x:Static pm:myBrushes.gray}">
<pm:ProjectTreeMenuItem Header="Cut"
Element="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type TreeViewItem}}}"
Command="{x:Static pm:MyCommands.CutCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Self}}"
Margin="20,0"/>
<pm:ProjectTreeMenuItem Header="Un-Cut"
Element="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type TreeViewItem}}}"
Command="{x:Static pm:MyCommands.UnCutCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Self}}"
Margin="20,0"/>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</HierarchicalDataTemplate.ItemContainerStyle>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Grid>
and my custom menu item class and commands.
public static class MyCommands
{
static MyCommands()
{
AddEpCommand = new SimpleDelegateCommand(p =>
{
var menuItem = p as ProjectTreeMenuItem;
var Element = menuItem.Element as ProjectElement;
Console.WriteLine(Element.Key);
Console.WriteLine("EP");
});
AddSeqCommand = new SimpleDelegateCommand(p =>
{
var menuItem = p as ProjectTreeMenuItem;
var Element = menuItem.Element as ProjectElement;
Console.WriteLine(Element.Key);
Console.WriteLine("SEQ");
});
AddScCommand = new SimpleDelegateCommand(p =>
{
var menuItem = p as ProjectTreeMenuItem;
var Element = menuItem.Element as ProjectElement;
Console.WriteLine(Element.Key);
Console.WriteLine("SC");
});
CutCommand = new SimpleDelegateCommand(p =>
{
var menuItem = p as ProjectTreeMenuItem;
var Element = menuItem.Element as ProjectElement;
Console.WriteLine(Element.Key);
Console.WriteLine("SC");
});
UnCutCommand = new SimpleDelegateCommand(p =>
{
var menuItem = p as ProjectTreeMenuItem;
var Element = menuItem.Element as ProjectElement;
Console.WriteLine(Element.Key);
Console.WriteLine("SC");
});
}
public static ICommand AddEpCommand { get; set; }
public static ICommand AddSeqCommand { get; set; }
public static ICommand AddScCommand { get; set; }
public static ICommand CutCommand { get; set; }
public static ICommand UnCutCommand { get; set; }
}
public class SimpleDelegateCommand : ICommand
{
public SimpleDelegateCommand(Action<object> executeAction)
{
_executeAction = executeAction;
}
private Action<object> _executeAction;
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
_executeAction(parameter);
}
}
public class ProjectTreeMenuItem : MenuItem
{
#region Element (DependencyProperty)
public ProjectElement Element
{
get { return (ProjectElement)GetValue(ElementProperty); }
set { SetValue(ElementProperty, value); }
}
public static readonly DependencyProperty ElementProperty =
DependencyProperty.Register("Element", typeof(ProjectElement), typeof(ProjectTreeMenuItem),
new PropertyMetadata { PropertyChangedCallback = ElementChanged });
private static void ElementChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ProjectTreeMenuItem so = d as ProjectTreeMenuItem;
if (so != null && e.NewValue != null)
{
so.Element = (ProjectElement)e.NewValue;
Console.WriteLine("HERE " + so.Element.Title);
}
}
#endregion
public ProjectTreeMenuItem()
:base()
{
}
}
The projectElement class ( which im using in hundreds of other places in the project ) has a String Key, String Title attribute which is working fine elsewhere. Im pretty sure my issues lies in setting the Element Property through the xaml via the datacontext.
Figured it out eventually.. I used the command property of the menuitem and used it to send back the datacontext directly instead of using a dependency property on a custom menu item class.
The larger issue was that i was trying to use find the ancestor from the visualtree, which in a contextmenu doesnt work, and i think also because i was within a hierachialdatatemplate it wouldnt work anyway.
the following does the trick though.
<ContextMenu Background="{x:Static pm:myBrushes.defaultBG}" Foreground="{x:Static pm:myBrushes.gray}"
DataContext="{Binding PlacementTarget, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Add Episode.."
Command="{x:Static pm:MyCommands.AddEpCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ContextMenu}}, Path=PlacementTarget.DataContext}"
Margin="20,0"/>
<MenuItem Header="Add Sequence.."
Command="{x:Static pm:MyCommands.AddSeqCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ContextMenu}}, Path=PlacementTarget.DataContext}"
Margin="20,0"/>
<MenuItem Header="Add Scene.."
Command="{x:Static pm:MyCommands.AddScCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ContextMenu}}, Path=PlacementTarget.DataContext}"
Margin="20,0"/>
</ContextMenu>

Categories

Resources