ganchito55
ganchito55

Reputation: 3607

Custom control: binding in ItemContainerStyle

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:

  1. If I use the same code with a button after the TreeView (with both in a Stackpanel) it works. So I think that the problem is the MenuItem DataContext
  2. I tried to change the MenuItem DataContext however I didn't get it.

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

Answers (2)

ganchito55
ganchito55

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.

Source

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

AzzamAziz
AzzamAziz

Reputation: 2171

Proposed Solution:

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.

Extra Info:

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.

  1. Make a DelegateCommand class that inherits from ICommand. This will be your concrete implementation of ICommand. You can find that here:http://www.wpftutorial.net/delegatecommand.html
  2. Instantiate your ICommands 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

Related Questions