WPF层级树形视图实现求助:基于公司-部门-员工数据模型
问题描述
我在实现WPF TreeView时遇到了问题,现有如下数据模型:
public class FullCompany { public int Id { get; set; } public string Name { get; set; } public DateTime DateOfFoundation { get; set; } public string LegalAddress { get; set; } public List<Posts> Posts { get; set; } } public class Posts { public int Id { get; set; } public string Name { get; set; } public string Director { get; set; } public int OrganizationId { get; set; } public List<Employee> Employees { get; set; } }
我已经从数据库获取到了该数据,但不知道如何构建公司→部门→员工的层级树形视图。我尝试编写了以下XAML代码,需要实现按钮折叠/展开功能,但查找示例后仍无法解决当前问题:
<TreeView ItemsSource="{Binding FullCompanies}" Grid.Row="1" x:Name="MainTreeView" HorizontalAlignment="Stretch" Margin="10" VerticalAlignment="Stretch"> <TreeView.ItemContainerStyle> <Style TargetType="{x:Type TreeViewItem}"> <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" /> <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" /> <Setter Property="FontWeight" Value="Normal" /> <Style.Triggers> <Trigger Property="IsSelected" Value="True"> <Setter Property="FontWeight" Value="Bold" /> </Trigger> </Style.Triggers> </Style> </TreeView.ItemContainerStyle> <TreeView.ItemTemplate> <HierarchicalDataTemplate> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="200"/> <ColumnDefinition /> </Grid.ColumnDefinitions> <StackPanel Grid.Column="0"> <Label Content="{Binding Name}"/> <TreeView ItemsSource="{Binding Posts}" x:Name="SubTreeView" HorizontalAlignment="Stretch" Margin="10" VerticalAlignment="Stretch"> <TreeView.ItemTemplate> <HierarchicalDataTemplate> <Grid> <StackPanel Grid.Column="0"> <Label Content="{Binding Name}"/> <TreeView ItemsSource="{Binding Employees}" x:Name="SubTreeView1" HorizontalAlignment="Stretch" Margin="10" VerticalAlignment="Stretch"> <TreeView.ItemTemplate> <HierarchicalDataTemplate> <Label Content="{Binding FirstName}"/> </HierarchicalDataTemplate> </TreeView.ItemTemplate> </TreeView> </StackPanel> </Grid> </HierarchicalDataTemplate> </TreeView.ItemTemplate> </TreeView> </StackPanel> <StackPanel Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right"> </StackPanel> </Grid> </HierarchicalDataTemplate> </TreeView.ItemTemplate> </TreeView>
解决方案
1. 修正数据模型,添加绑定属性
你的数据模型需要实现INotifyPropertyChanged接口,并添加IsExpanded、IsSelected属性——因为你在Style中绑定了这些属性,但原始模型没有提供:
public class BaseViewModel : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } public class FullCompany : BaseViewModel { public int Id { get; set; } public string Name { get; set; } public DateTime DateOfFoundation { get; set; } public string LegalAddress { get; set; } public List<Posts> Posts { get; set; } private bool _isExpanded; public bool IsExpanded { get => _isExpanded; set { _isExpanded = value; OnPropertyChanged(); } } private bool _isSelected; public bool IsSelected { get => _isSelected; set { _isSelected = value; OnPropertyChanged(); } } } public class Posts : BaseViewModel { public int Id { get; set; } public string Name { get; set; } public string Director { get; set; } public int OrganizationId { get; set; } public List<Employee> Employees { get; set; } private bool _isExpanded; public bool IsExpanded { get => _isExpanded; set { _isExpanded = value; OnPropertyChanged(); } } private bool _isSelected; public bool IsSelected { get => _isSelected; set { _isSelected = value; OnPropertyChanged(); } } } public class Employee : BaseViewModel { public string FirstName { get; set; } private bool _isSelected; public bool IsSelected { get => _isSelected; set { _isSelected = value; OnPropertyChanged(); } } }
2. 重构TreeView的XAML结构
你当前嵌套TreeView的写法是错误的——WPF TreeView本身支持层级结构,只需通过HierarchicalDataTemplate的ItemsSource指定子级集合即可,无需嵌套TreeView控件:
<TreeView ItemsSource="{Binding FullCompanies}" Grid.Row="1" x:Name="MainTreeView" HorizontalAlignment="Stretch" Margin="10" VerticalAlignment="Stretch"> <TreeView.ItemContainerStyle> <Style TargetType="{x:Type TreeViewItem}"> <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" /> <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" /> <Setter Property="FontWeight" Value="Normal" /> <Style.Triggers> <Trigger Property="IsSelected" Value="True"> <Setter Property="FontWeight" Value="Bold" /> </Trigger> </Style.Triggers> </Style> </TreeView.ItemContainerStyle> <!-- 公司层级模板 --> <TreeView.ItemTemplate> <HierarchicalDataTemplate ItemsSource="{Binding Posts}"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="200"/> <ColumnDefinition /> </Grid.ColumnDefinitions> <Label Content="{Binding Name}" /> <!-- 自定义展开/折叠按钮 --> <StackPanel Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right"> <Button Content="{Binding IsExpanded, Converter={StaticResource BoolToExpandCollapseConverter}}" Command="{Binding ToggleExpandCommand}" Width="80" Margin="5"/> </StackPanel> </Grid> <!-- 部门层级模板 --> <HierarchicalDataTemplate.ItemTemplate> <HierarchicalDataTemplate ItemsSource="{Binding Employees}"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="200"/> <ColumnDefinition /> </Grid.ColumnDefinitions> <Label Content="{Binding Name}" /> <StackPanel Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right"> <Button Content="{Binding IsExpanded, Converter={StaticResource BoolToExpandCollapseConverter}}" Command="{Binding ToggleExpandCommand}" Width="80" Margin="5"/> </StackPanel> </Grid> <!-- 员工层级模板 --> <HierarchicalDataTemplate.ItemTemplate> <DataTemplate> <Label Content="{Binding FirstName}" /> </DataTemplate> </HierarchicalDataTemplate.ItemTemplate> </HierarchicalDataTemplate> </HierarchicalDataTemplate.ItemTemplate> </HierarchicalDataTemplate> </TreeView.ItemTemplate> </TreeView>
3. 添加转换器和命令实现自定义折叠/展开按钮
布尔值转文本转换器
用于根据展开状态切换按钮文本:
public class BoolToExpandCollapseConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { return (bool)value ? "折叠" : "展开"; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } }
在XAML资源中声明转换器:
<Window.Resources> <local:BoolToExpandCollapseConverter x:Key="BoolToExpandCollapseConverter"/> </Window.Resources>
在BaseViewModel中添加切换命令
用于按钮触发展开/折叠操作:
public class BaseViewModel : INotifyPropertyChanged { // 之前的INotifyPropertyChanged代码... public ICommand ToggleExpandCommand => new RelayCommand(ToggleExpand); private void ToggleExpand() { IsExpanded = !IsExpanded; } // 基础RelayCommand实现 public class RelayCommand : ICommand { private readonly Action _execute; private readonly Func<bool> _canExecute; public event EventHandler CanExecuteChanged; public RelayCommand(Action execute, Func<bool> canExecute = null) { _execute = execute ?? throw new ArgumentNullException(nameof(execute)); _canExecute = canExecute; } public bool CanExecute(object parameter) => _canExecute?.Invoke() ?? true; public void Execute(object parameter) => _execute(); public void RaiseCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty); } }
关键说明
- 禁止嵌套TreeView:WPF TreeView通过
HierarchicalDataTemplate自动构建层级,嵌套TreeView会导致样式和交互混乱。 - 必须实现
INotifyPropertyChanged:确保UI能响应属性变化,比如IsExpanded改变时视图自动更新。 - 自定义按钮通过命令绑定:手动控制展开/折叠状态,配合转换器实现按钮文本动态切换。
内容的提问来源于stack exchange,提问作者Alexandr Gevorkyan
相关产品推荐
相关产品推荐

