-1

I am new in c# and wpf. I have a treeveiew and what I need is - when user click on one of the items, mark this item as selected. For example as it is implemented in DataGrid when user click on the row it marks as selected and user can easaly understand which row was selected.

My implementation of TreeView is

<TreeView Name="Tv_request" 
                                  Grid.Row="1"
                                  HorizontalAlignment="Stretch"
                                  VerticalAlignment="Stretch"
                                  ItemsSource="{Binding Path=RequestSet}">
                            
                            <TreeView.ItemTemplate>
                                <HierarchicalDataTemplate 
                                    ItemsSource="{Binding Childrens}"
                                    DataType="{x:Type local:IRequest}">
                                    <TreeViewItem MouseUp="TreeViewItem_MouseUp" 
                                                  Header="{Binding TypePage}"/>
                                </HierarchicalDataTemplate>
                            </TreeView.ItemTemplate>
                            
                            <TreeView.ItemContainerStyle>
                                <Style TargetType="{x:Type TreeViewItem}">
                                    <Setter Property="IsExpanded" Value="True" />
                                </Style>
                            </TreeView.ItemContainerStyle>
                            
                        </TreeView>

Way I can get clicked item - by TreeViewItem_MouseUp event, but I need to implement mark selection item.

How to implement it?

EDIT

In order to get clicked item I am using such approach

private void TreeViewItem_MouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            var tree = sender as TreeViewItem;

            if (tree != null)
            {
                IRequest item = tree.DataContext as IRequest;
            
                Presenter?.TreeViewSelectedItem(item);
            }
        }

Such way I can have access to my selected item, but if I change it to

private void TreeViewItem_MouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            var tree = sender as ContentPresenter;

            if (tree != null)
            {
                IRequest item = tree.DataContext as IRequest;
            
                Presenter?.TreeViewSelectedItem(item);
            }
        }

So, tree.DataContext return me NodeType and logically it is ok, because I set

<ContentPresenter Content="{Binding NodeType}"
                                                      MouseUp="TreeViewItem_MouseUp"/>

instead of

<TreeViewItem MouseUp="TreeViewItem_MouseUp" 
                                                  Header="{Binding NodeType}"/>

So, question is, how I can get entire clicked object not just his NodeType?

Teti
  • 7
  • 3
  • You've added another question inside the one you posted. When you click "Ask Question" that's one. One question in the heading with explanation and code in the body. Not explanation, code and... another question. – Andy Aug 30 '20 at 18:54

2 Answers2

0

Firstly, your item template is creating a TreeViewItem inside a TreeViewItem. It should look like this:

<HierarchicalDataTemplate 
    ItemsSource="{Binding Childrens}"
    DataType="{x:Type local:IRequest}">
    <ContentPresenter Content="{Binding TypePage}"/>
</HierarchicalDataTemplate>

The TreeViewItem will be automatically created by WPF, and the HierarchicalDataTemplate you provide will be applied to it.

Secondly, TreeViewItem has a property called IsSelected. You can use the Style to bind this property to a property of your IRequest object:

<Style TargetType="{x:Type TreeViewItem}">
  <Setter Property="IsExpanded" Value="True" />
  <Setter Property="IsSelected" Value="{Binding RequestIsSelected}" />
</Style>

Or you can use an EventSetter to provide a handler for the TreeViewItem.Selected event if you prefer:

<Style TargetType="{x:Type TreeViewItem}">
  <Setter Property="IsExpanded" Value="True" />
  <EventSetter RoutedEvent="Selected" Value="TreeViewItem_Selected" />
</Style>

You could also use the TreeView.SelectionChanged event instead of dealing with individual tree view items, but be aware that you (annoyingly!) can't select a new item from the TreeView itself.

Artfunkel
  • 1,832
  • 17
  • 23
  • Thanks! But I faced some issue with such approach, could you please help me I edited my question – Teti Aug 30 '20 at 17:38
  • I've already described this in the answer. You use the `Selected` event / `IsSelected` property of the TreeViewItem. – Artfunkel Aug 30 '20 at 18:47
0

If you don't mess things up by nesting a hierarchicaltemplate inside an itemtemplate you don't need to do anything at all to get a selected treeview item coloured.

Look at the xaml here:

https://learn.microsoft.com/en-us/dotnet/api/system.windows.hierarchicaldatatemplate?view=netcore-3.1

  <HierarchicalDataTemplate DataType    = "{x:Type src:League}"
                            ItemsSource = "{Binding Path=Divisions}">
    <TextBlock Text="{Binding Path=Name}"/>
  </HierarchicalDataTemplate>

  <HierarchicalDataTemplate DataType    = "{x:Type src:Division}"
                            ItemsSource = "{Binding Path=Teams}">
    <TextBlock Text="{Binding Path=Name}"/>
  </HierarchicalDataTemplate>

  <DataTemplate DataType="{x:Type src:Team}">
    <TextBlock Text="{Binding Path=Name}"/>
  </DataTemplate>

Notice what you give it as a template appears inside a treeviewitem. Not hierarchicaldatatemplate within itemtemplate.

Similarly, here: https://www.wpf-tutorial.com/treeview-control/treeview-data-binding-multiple-templates/

    <TreeView Name="trvFamilies">
        <TreeView.Resources>
            <HierarchicalDataTemplate DataType="{x:Type self:Family}" ItemsSource="{Binding Members}">
                <StackPanel Orientation="Horizontal">
                    <Image Source="/WpfTutorialSamples;component/Images/group.png" Margin="0,0,5,0" />
                    <TextBlock Text="{Binding Name}" />
                    <TextBlock Text=" [" Foreground="Blue" />
                    <TextBlock Text="{Binding Members.Count}" Foreground="Blue" />
                    <TextBlock Text="]" Foreground="Blue" />
                </StackPanel>
            </HierarchicalDataTemplate>
            <DataTemplate DataType="{x:Type self:FamilyMember}">
                <StackPanel Orientation="Horizontal">
                    <Image Source="/WpfTutorialSamples;component/Images/user.png" Margin="0,0,5,0" />
                    <TextBlock Text="{Binding Name}" />
                    <TextBlock Text=" (" Foreground="Green" />
                    <TextBlock Text="{Binding Age}" Foreground="Green" />
                    <TextBlock Text=" years)" Foreground="Green" />
                </StackPanel>
            </DataTemplate>
        </TreeView.Resources>
    </TreeView>

If you try that code in the last link, notice when you click a node - a family or member - you get a blue background.

This is how you mark a selected treeviewitem. Don't mess up your templates and it "just works".

If you need to know which one was selected then this is a bit tricky because you can't just bind selecteditem like a datagrid. This is indirectly due to the hierarchical nature of the control meaning each node has it's own itemcontrol for children. A treeviewitem is a headereditemscontrol.

This answer illustrates one of the behaviors commonly used to enable binding of selecteditem:

Data binding to SelectedItem in a WPF Treeview

Once you bind the selected item like this you can then work with that property in the viewmodel. When it changes the setter will be hit and you can put code in there to do work with that item.

Andy
  • 11,864
  • 2
  • 17
  • 20