WPF TreeView 中具有多级的 Multi Select

Multi Select with Multiple Level in WPF TreeView

我正在尝试使用多级 WPF 树视图实现多Select。 为此,我禁用了 WPF selection,而是突出显示了 selected 项目并对其进行了处理。

此实现适用于 Ctrl 按钮和 select 随机。

但是对于 shift select 如果我尝试突出显示未展开的树节点,我可以突出显示树节点而不是它的子节点。 要求是展开树节点并突出显示子节点。

展开树后,我无法从 ItemContainerGenerator 获取子节点的容器 Node.It returns null always

到目前为止我都试过了

1.Use 展开Parent TreeViewItem后的UpdateLayout(); (ItemContainerGenerator.ContainerFromItem(项目) return 空)

  1. 关闭 VirtualizationStack。

有什么办法可以让ItemContainerGenerator展开TreeNode后瞬间生成it​​em

感谢任何帮助!!!

您不应该对物品容器进行操作,而应该对物品进行操作。
为此,您需要向模型添加 IsSelected 属性。您还需要 select 节点的子项。

为了在 TreeViewItem 上注册 MouseBinding,您必须覆盖 TreeViewItem 的默认 ControlTemplateMouseBinding 定义了一个 MouseGesture "CTRL + LeftClick" 上触发。 ICommand 注册到 MouseBinding 通过切换 IsSelected 属性.

执行项目的实际 selection

要获得 selected 项目,您只需遍历源集合(遍历树结构)以收集 IsSelected 等于 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>