2
votes

I have a TreeView. I would like to enable EditLeafCommand by clicking F2.

Model:

public class Leaf
{
    public string LeafName { get; set; }
    public bool HasChildren { get; set; }        
}

ViewModel of Window:

public MainWindowViewModel{

  public ReadOnlyCollection<LeafViewModel> Leafs
  {
        get { return leafs; }
  }
}

ViewModel of TreeView:

public class LeafViewModel : TreeViewItemViewModel
{
    public ObservableCollection<TreeViewItemViewModel> Children
    {
        get { return _children; }
    }
    public string LeafName { get; set; }   
    public RelayCommand EditLeafCommand { get; set; }     

}

XAML:

<TreeView  ItemsSource="{Binding Leafs}">               
   <TreeView.InputBindings>
      <KeyBinding Key="F2" Command="{Binding SelectedItem.EditLeafCommand, 
                           diag:PresentationTraceSources.TraceLevel=High}"/>
   </TreeView.InputBindings>
   <TreeView.Resources>
      <HierarchicalDataTemplate DataType="{x:Type vm:LeafViewModel}" 
                                     ItemsSource="{Binding Children}">
         <StackPanel Orientation="Horizontal">               
            <TextBox Text="{Binding LeafName}" IsReadOnly="{Binding IsReadOnlyItem}" 
            Tag="{Binding DataContext, RelativeSource={RelativeSource Self}}">
               <TextBox.ContextMenu>
                  <ContextMenu DataContext="{Binding PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
                     <MenuItem Command="{Binding EditLeafCommand}" CommandParameter="{Binding ALeaf}" Header="Edit" />
                  </ContextMenu>
               </TextBox.ContextMenu>
            </TextBox>
         </StackPanel>
      </HierarchicalDataTemplate>
   </TreeView.Resources>
</TreeView>

and Output window:

System.Windows.Data Warning: 56 : Created BindingExpression (hash=2683661) for Binding (hash=47044325)

System.Windows.Data Warning: 58 : Path: 'EditLeafCommand'

System.Windows.Data Warning: 60 : BindingExpression (hash=2683661): Default mode resolved to OneWay

System.Windows.Data Warning: 61 : BindingExpression (hash=2683661): Default update trigger resolved to PropertyChanged

System.Windows.Data Warning: 62 : BindingExpression (hash=2683661): Attach to System.Windows.Input.KeyBinding.Command (hash=29578451)

System.Windows.Data Warning: 64 : BindingExpression (hash=2683661): Use Framework mentor

System.Windows.Data Warning: 67 : BindingExpression (hash=2683661): Resolving source

System.Windows.Data Warning: 69 : BindingExpression (hash=2683661): Framework mentor not found

System.Windows.Data Warning: 65 : BindingExpression (hash=2683661): Resolve source deferred

System.Windows.Data Warning: 95 : BindingExpression (hash=2683661): Got InheritanceContextChanged event from KeyBinding (hash=29578451)

System.Windows.Data Warning: 67 : BindingExpression (hash=2683661): Resolving source

System.Windows.Data Warning: 70 : BindingExpression (hash=2683661): Found data context element: TreeView (hash=11903911) (OK)

System.Windows.Data Warning: 78 : BindingExpression (hash=2683661): Activate with root item MainWindowViewModel (hash=44115416)

System.Windows.Data Warning: 108 : BindingExpression (hash=2683661): At level 0 - for MainWindowViewModel.EditLeafCommand found accessor

System.Windows.Data Error: 40 : BindingExpression path error: 'EditLeafCommand' property not found on 'object' ''MainWindowViewModel' (HashCode=44115416)'. BindingExpression:Path=EditLeafCommand; DataItem='MainWindowViewModel' (HashCode=44115416); target element is 'KeyBinding' (HashCode=29578451); target property is 'Command' (type 'ICommand')

System.Windows.Data Warning: 80 : BindingExpression (hash=2683661): TransferValue - got raw value {DependencyProperty.UnsetValue}

System.Windows.Data Warning: 88 : BindingExpression (hash=2683661): TransferValue - using fallback/default value

System.Windows.Data Warning: 89 : BindingExpression (hash=2683661): TransferValue - using final value

I see this post and it is it is the same question, however, accepted answer does not have any code(I've tried to go through link, however, the offered approach didn't help me)

The error says:

System.Windows.Data Error: 40 :BindingExpression path error: 'EditLeafCommand' property not found on 'object' ''MainWindowViewModel'

But how can I direct to EditLeadViewModel?

How can I call EditLeafCommand from LeafViewModel and send parameter?

2
I don't see any XAML where you are binding to SetSelectedItemCommand, that seems to be the problem XAML, where is it?Glen Thomas
Yes I can see there is no property called SetSelectedItemCommand, but there must be somewhere in the XAML that is doing {Binding SetSelectedItemCommand}Glen Thomas
@GlenThomas please, see my updated answer. I've updated info and I would like to bind to LeafViewModel, not MainViewModel. They are really stupid mistakes. Sorry.StepUp

2 Answers

2
votes

Your MainViewModel does not have a SelectedItem property. You need to add this property to your view model and make sure that the INotifyPropertyChanged.PropertyChanged event is fired when that property changes.

You also need to update this property whenever the selected item of the tree view changes. There are various ways to do that. One way is to use the Nuget package System.Windows.Interactivity.WPF. You will have to add a namespace declaration:

xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"

Then, inside your tree view XAML element add the following (I'm only showing what to add - keep the rest of your XAML inside the TreeView element intact):

<TreeView Name="treeView" ItemsSource="{Binding Leafs}">
  <i:Interaction.Triggers>
    <i:EventTrigger EventName="SelectedItemChanged">
      <i:InvokeCommandAction Command="{Binding SetSelectedItemCommand, PresentationTraceSources.TraceLevel=High}" CommandParameter="{Binding SelectedItem, ElementName=treeView}" />
    </i:EventTrigger>
  </i:Interaction.Triggers>
</TreeView>

Note that the TreeView element has a name (treeView) that is used in the CommandParameter binding in the InvokeCommandAction element.

Also, note that you have to add a SetSelectedItemCommand on MainViewModel. This command should set the SelectedItem property that I described in my initial paragraph. Here are some code fragments using a generic RelayCommand:

class MainWindowViewModel {

  TreeViewItemViewModel selectedItem;

  public MainWindowViewModel() {
    SetSelectedItemCommand = new RelayCommand<TreeViewItemViewModel>(SetSelectedItem);
  }

  public TreeViewItemViewModel SelectedItem {
    get { return selectedItem; }
    set {
      selectedItem = value;
      OnPropertyChanged();
    }
  }

  void SetSelectedItem(TreeViewItemViewModel viewModel) {
    SelectedItem = viewModel;
  }

}

This is the the basic stuff needed to enable your key binding to SelectedItem.EditLeafCommand to work. However, you have another problem. Your HierarchicalDataTemplate defines a tree node to be a TextBox. When you click on a TextBox no click is bubbled to the TreeView and the selection is not changed. My suggestion is that you use a non-interactive representation of each tree node (e.g. a TextBlock). Then, when EditLeafCommand is invoked you put a TextBox on top allowing the user to edit the node.

0
votes

Andy ONeill has solved this problem. I am really excited by his solution:). I hurry to share this solution with you:

To bind to a command in LeafViewModel:

<TreeView  ItemsSource="{Binding Leafs}" Name="tv">
   <TreeView.Resources>
      <HierarchicalDataTemplate DataType="{x:Type local:LeafViewModel}" 
                                      ItemsSource="{Binding Children}">
         <StackPanel Orientation="Horizontal">
            <Label VerticalAlignment="Center" FontFamily="WingDings" Content="1"/>
               <TextBox Text="{Binding LeafName}" Tag="{Binding DataContext, 
                  RelativeSource={RelativeSource Self}}" Background="Transparent">
                  <TextBox.ContextMenu>
                     <ContextMenu DataContext="{Binding PlacementTarget.Tag, 
                                     RelativeSource={RelativeSource Self}}">
                        <MenuItem Command="{Binding EditLeafCommand}" 
                         CommandParameter="{Binding ALeaf}" Header="Edit" />
                     </ContextMenu>
                  </TextBox.ContextMenu>
               </TextBox>
            </StackPanel>
         </HierarchicalDataTemplate>
      </TreeView.Resources>
    <TreeView.InputBindings>
    <KeyBinding Key="F2" Command="{Binding SelectedItem.EditLeafCommand, ElementName=tv}"/>
    </TreeView.InputBindings>
</TreeView>