c# - WPF TreeView中的多级多选
问题描述
我正在尝试使用多级 WPF 树视图实现多选。为此,我禁用了 WPF 选择,而是突出显示了所选项目并对其进行了处理。
此实现适用于 Ctrl 按钮并选择 random 。
但是对于移位选择,如果我尝试突出显示未展开的树节点,我可以突出显示树节点而不是它的子节点。要求是扩展树节点并突出显示子节点。
展开树节点后,我无法从 ItemContainerGenerator 获取子节点的容器。它总是返回 null
无论我尝试到现在
1.展开Parent TreeViewItem后使用UpdateLayout();(ItemContainerGenerator.ContainerFromItem(item)返回 null)
- 关闭虚拟化堆栈。
有什么方法可以让 ItemContainerGenerator 在扩展 TreeNode 后立即生成项目
任何帮助表示赞赏!
解决方案
您不应该对项目容器进行操作,而是对项目进行操作。
为此,您需要向IsSelected
模型添加一个属性。您还需要选择节点的子项。
为了在 上注册 aMouseBinding
你TreeViewItem
必须覆盖默认ControlTemplate
的TreeViewItem
. MouseBinding
定义在" CTRL + LeftClick"MouseGesture
上触发。向注册的A通过切换属性来执行项目的实际选择。ICommand
MouseBinding
IsSelected
要获取所选项目,您只需遍历源集合(遍历树结构)以收集IsSelected
equals的所有项目true
。
树项.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
}
视图模型.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; }
}
主窗口.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=""
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>
推荐阅读
- r - 为什么光栅文件大小与对象大小有很大不同?
- python - 尝试将 urls.py 设置为指向 index.html
- python - 如何从网站上抓取数据并在 R 中以指定格式写入 csv?
- php - SecurityError:阻止了一个带有原点的帧
- emacs - Elisp use apply on progn for list of functions
- python - Unable to use if statement before for loop in List Comprehension
- javascript - 数据表手动填充数据
- vue.js - Bootstrap Vue - make Dropdown List readonly
- rust - How do I avoid allocations in Iterator::flat_map?
- python - Print out objects stored in a list