简体   繁体   中英

Multi Select with Multiple Level in WPF TreeView

I am trying to implement Multi-Select with Multi-Level WPF Tree View. For this, I disabled the WPF selection and, instead highlighted the selected items and processed it.

This implementation works fine for Ctrl button and select random.

But for shift select if I try to highlight Tree Node which is not expanded, I can highlight Tree Node but not its children. Requirement is to expand Tree Node and highlight children too.

I am not able to get container for children node from ItemContainerGenerator after expanding Tree Node.It returns null always

Whatever I tried till now

1.Use UpdateLayout() after expanding Parent TreeViewItem; ( ItemContainerGenerator.ContainerFromItem(item) return null)

  1. Turn off VirtualizationStack.

Is there any way to make ItemContainerGenerator generate items instataneously after expanding TreeNode

Any help is appreciated!!!

You should not operate on the item containers but on the items.
To do so, you need to add a IsSelected property to your model. You also need to select the child items of a node.

In order to register a MouseBinding on the TreeViewItem you would have to override the default ControlTemplate of TreeViewItem . The MouseBinding defines a MouseGesture to trigger on "CTRL + LeftClick" . A ICommand registered with the MouseBinding executes the actual selection of the item by toggling the IsSelected property.

To get the selected items you simply iterate over the source collection (traverse the tree structure) to collect all items where IsSelected equals true .

TreeItem.cs

public class TreeItem : INotifyPropertyChanged
{
  public TreeItem(string value)
  {
    this.Value = value;
    this.Children = new ObservableCollection<TreeItem>();
  }

  private void SelectAllChildren(IEnumerable<TreeItem> children, bool isSelected)
  {
    foreach (TreeItem child in children)
    {
      child.IsSelected = isSelected;
      SelectAllChildren(child.Children, isSelected);
    }
  }

  public string Value { get; set; }
  public ObservableCollection<TreeItem> Children { get; set; }

  // Toggle IsSelected
  public ICommand SelectItemCommand => new RelayCommand(param => this.IsSelected ^= true);

  private bool isSelected;   
  public bool IsSelected
  {
    get => this.isSelected;
    set 
    { 
      this.isSelected = value; 
      OnPropertyChanged();

      SelectAllChildren(this.Children, this.IsSelected);
    }
  }

  #region Implementation of INotifyPropertyChanged

  public event PropertyChangedEventHandler PropertyChanged;
  protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
  {
    this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
  }

  #endregion Implementation of INotifyPropertyChanged
}

ViewModel.cs

public class ViewModel : INotifyPropertyChanged
{
  public ViewModel()
  {
    this.TreeItems = new ObservableCollection<TreeItem>();
  }

  private IEnumerable<TreeItem> GetSelectedItems(IEnumerable<TreeItem> items)
  {
    List<TreeItem> selectedItems = new List<TreeItem>();
    foreach (TreeItem item in items)
    {
      if (item.IsSelected)
      {
        selectedItems.Add(item);
      }
      else // Check if unselected node has selected child nodes
      {
        var selectedChildItems = GetSelectedItems(item.Children);
        selectedItems.AddRange(selectedChildItems);
      }
    }
    return selectedItems;
  }

  public ObservableCollection<TreeItem> TreeItems { get; set; }
}

MainWindow.xaml

<Window>
  <Window.Resources>
    <ViewModel />
  </Window.Resources>

  <TreeView ItemsSource="{Binding TreeItems}">
    <TreeView.Resources>
      <HierarchicalDataTemplate DataType="{x:Type TreeItem}"
                                ItemsSource="{Binding Children}">
        <TextBlock Text="{Binding Value}" />
      </HierarchicalDataTemplate>
    </TreeView.Resources>

    <TreeView.ItemContainerStyle>
      <Style TargetType="TreeViewItem">
        <Setter Property="Template">
          <Setter.Value>
            <ControlTemplate TargetType="TreeViewItem">
              <Border Background="{TemplateBinding Background}"
                      BorderBrush="{TemplateBinding BorderBrush}"
                      BorderThickness="{TemplateBinding BorderThickness}">
                <StackPanel>
                  <StackPanel Orientation="Horizontal">
                    <ToggleButton x:Name="Expander"
                                  IsChecked="{Binding IsExpanded, RelativeSource={RelativeSource TemplatedParent}}"
                                  Background="Transparent"
                                  BorderThickness="0"
                                  RenderTransformOrigin="0.5, 0.5">
                      <ToggleButton.RenderTransform>
                        <RotateTransform />
                      </ToggleButton.RenderTransform>
                      <ToggleButton.Content>
                        <TextBlock Text="&#xE76C;" 
                                   FontFamily="Segoe MDL2 Assets" />
                      </ToggleButton.Content>
                    </ToggleButton>

                    <Border x:Name="SelectionBorder">
                      <ContentPresenter x:Name="PART_Header"
                                        Content="{TemplateBinding Header}">

                        <!-- Register to handle CTRL+LeftClick gesture -->
                        <ContentPresenter.InputBindings>
                          <MouseBinding Command="{Binding SelectItemCommand}">
                            <MouseBinding.Gesture>
                              <MouseGesture Modifiers="Control"
                                            MouseAction="LeftClick" />
                            </MouseBinding.Gesture>
                          </MouseBinding>
                        </ContentPresenter.InputBindings>
                      </ContentPresenter>
                    </Border>
                  </StackPanel>

                  <ItemsPresenter x:Name="ItemsHost"
                                  Visibility="Collapsed"
                                  Margin="12,0,0,0" />
                </StackPanel>

                <!-- Animate the node's expander -->
                <VisualStateManager.VisualStateGroups>
                  <VisualStateGroup x:Name="ExpansionStates">
                    <VisualState x:Name="Expanded">
                      <Storyboard>
                        <DoubleAnimation Storyboard.TargetName="Expander"
                                         Storyboard.TargetProperty="(UIElement.RenderTransform).(RotateTransform.Angle)"
                                         To="90" 
                                         Duration="0:0:0.1" />
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ItemsHost"                         
                                                       Storyboard.TargetProperty="(UIElement.Visibility)">
                          <DiscreteObjectKeyFrame KeyTime="0:0:0" 
                                                  Value="{x:Static Visibility.Visible}" />
                        </ObjectAnimationUsingKeyFrames>
                      </Storyboard>
                    </VisualState>
                    <VisualState x:Name="Collapsed" />
                  </VisualStateGroup>
                </VisualStateManager.VisualStateGroups>
              </Border>

              <ControlTemplate.Triggers>
                <Trigger Property="HasItems" Value="False">
                  <Setter TargetName="Expander" 
                          Property="Visibility" 
                          Value="Collapsed" />
                </Trigger>

                <!-- Handle item selection visuals -->
                <DataTrigger Binding="{Binding IsSelected}" Value="True">
                  <Setter TargetName="SelectionBorder" 
                          Property="Background" 
                          Value="DodgerBlue" />
                </DataTrigger>
              </ControlTemplate.Triggers>
            </ControlTemplate>
          </Setter.Value>
        </Setter>
      </Style>
    </TreeView.ItemContainerStyle>
  </TreeView>
</Window>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM