Reputation: 3607
I'm working in a Explorer tree view (custom wpf control designed by me). I have this code in Generic.xaml:
<Style TargetType="{x:Type local:ExplorerControl}">
<Setter Property="Template" >
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ExplorerControl}">
<Border>
<TreeView Name="myTreeView" >
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem x:Name="myTemplate" Header="Remove" Command="{TemplateBinding RemoveCommand}"></MenuItem>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</TreeView.ItemContainerStyle>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Nodes}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" VerticalAlignment="Center" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
In ExplorerControl I have my Dependency Property:
public class ExplorerControl : Control{
public ExplorerControl()
{
Nodes = new ObservableCollection<Node>();
}
private ObservableCollection<Node> Nodes { get; }
public ICommand RemoveCommand
{
get { return (ICommand)GetValue(RemovedCommandProperty); }
set { SetValue(RemovedCommandProperty, value); }
}
public static readonly DependencyProperty RemovedCommandProperty =
DependencyProperty.Register("RemoveCommand", typeof(ICommand), typeof(ExplorerControl));
}
Node class
public class Node {
public string Name {get;set;}
}
My problem is that I don't know how to get that the MenuItem Command works
I have tried these:
I hope you can help me.
Edit: I delete the part of code about DataContext. Thanks for you answers.
I use this control in my MainView:
<treeViewExplorerControl:ExplorerControl
SelectedItemName="{Binding SelectedItemName}"
SelectedItemPath="{Binding SelectedItemPath}"
RemoveCommand="{Binding ExplorerControlItemRemovedCommand}"/>
Upvotes: 0
Views: 920
Reputation: 3607
Finally I found a solution.
First of all I found that about ContextMenu:
Because a ContextMenu in WPF does not exist within the visual tree of your page/window/control per se, data binding can be a little tricky.
With the example, I wrote this code and I got that it works well:
<Style TargetType="{x:Type local:ExplorerControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ExplorerControl}">
<Border>
<TreeView Name="myTreeView">
<TreeView.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="#FF003BB0" />
</TreeView.Resources>
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="True" />
</Style>
</TreeView.ItemContainerStyle>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Nodes}">
<StackPanel Orientation="Horizontal"
Tag="{Binding TemplatedParent,RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TreeView}}}">
<TextBlock Text="{Binding Name}" VerticalAlignment="Center" />
<StackPanel.ContextMenu>
<ContextMenu>
<MenuItem x:Name="myTemplate"
Header="Remove"
DataContext="{Binding PlacementTarget, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}}"
Command="{Binding Path=Tag.RemoveCommand}"
CommandParameter="{Binding Path=DataContext}">
</MenuItem>
</ContextMenu>
</StackPanel.ContextMenu>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I save in the Stackpanel tag, the reference of my explorerControl, then I use PlacementTarget to get the Stackpanel reference
I hope this code helps other people in future.
Upvotes: 1
Reputation: 2171
In your MenuItem
Commands
try using Ancestral Binding.
<MenuItem x:Name="myTemplate" Header="Remove"
Command="{Binding RelativeSource={RelativeSource Path=RemoveCommand, AncestorType={x:Type ExplorerControl}, Mode=FindAncestor" />
I believe the reason your DataContext
is changing is because you're pointing to Nodes
and showing each Node
in the MenuItem
. However, Node
does not contain the command you are trying to bind to.
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Nodes}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" VerticalAlignment="Center" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
If you are unsure of your DataContext
you can use Snoop to see what is the current DataContext.
I don't think you need to point DataContext
of your TreeView
. It's passed down automatically.
<TreeView.DataContext>
<local:ExplorerControl x:Name="explorer" />
</TreeView.DataContext>
You don't have to use a DependencyProperty
with an ICommand
. In the constructor of your ExplorerControl
you can instantiate the ICommand
to a DelegateCommand
.
DelegateCommand
class that inherits from ICommand
. This will be your concrete implementation of ICommand
. You can find that here:http://www.wpftutorial.net/delegatecommand.htmlICommands
with a DelegateCommand
and pass your method, that is on ExplorerControl
, to the constructor.For example:
public class ExplorerControl : UserControl
{
public DelegateCommand RemoveCommand { get; set; }
public ExplorerControl()
{
RemoveCommand = new DelegateCommand(Remove);
}
private void Remove()
{
// Do something here.
}
}
Upvotes: 1