You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WPF MVVM模式下TreeView绑定SelectedItem及功能实现求助

WPF MVVM 开发问题解决方案

1. TreeView SelectedItem 双向绑定处理

TreeView的SelectedItem属性仅提供getter,无法直接双向绑定。我们可以通过**行为(Behavior)**将选中项传递到ViewModel,同时在ViewModel中维护选中状态属性。

2. 使用Microsoft.Xaml.Behaviors.Wpf处理SelectedItemChanged事件

在XAML中通过EventTrigger结合InvokeCommandAction,将TreeView的SelectedItemChanged事件绑定到ViewModel的命令,无需修改MainWindow.xaml.cs:

修改MainWindow.xaml中的TreeView行为部分

<Behaviors:Interaction.Triggers>
    <Behaviors:EventTrigger EventName="SelectedItemChanged">
        <Behaviors:InvokeCommandAction 
            Command="{Binding SelectedItemChangedCommand}"
            CommandParameter="{Binding SelectedItem, RelativeSource={RelativeSource AncestorType=TreeView}}"/>
    </Behaviors:EventTrigger>
</Behaviors:Interaction.Triggers>

在MainWindowViewModel中添加命令和选中项属性

public partial class MainWindowViewModel : ObservableObject
{
    [ObservableProperty]
    private object _selectedItem;

    [ObservableProperty]
    private ICollectionView _selectedGroupItems;

    public IRelayCommand<object> SelectedItemChangedCommand { get; }

    public MainWindowViewModel()
    {
        // 原有初始化代码...
        SelectedItemChangedCommand = new RelayCommand<object>(OnSelectedItemChanged);
    }

    private void OnSelectedItemChanged(object selectedItem)
    {
        SelectedItem = selectedItem;
        UpdateSelectedGroupItems();
    }

    private void UpdateSelectedGroupItems()
    {
        if (SelectedItem is MyModel model)
        {
            // 选中的是MyModel,获取其所在的GroupName分组数据
            var filtered = myModelList.Where(m => m.GroupName == model.GroupName).ToList();
            SelectedGroupItems = CollectionViewSource.GetDefaultView(filtered);
        }
        else if (SelectedItem is CollectionViewGroup group)
        {
            // 选中的是分组,获取该分组下的所有子项
            SelectedGroupItems = CollectionViewSource.GetDefaultView(group.Items);
        }
        else
        {
            SelectedGroupItems = null;
        }
    }

    // 原有代码...
}

3. 选中节点后展示详情与父组数据

右侧绿色面板展示MyModel详情

修改MainWindow.xaml中绿色Grid的内容,绑定SelectedItem的属性(自动匹配数据类型模板):

<Grid Grid.Row="2" Grid.Column="2" Background="green" Grid.RowSpan="2">
    <ContentControl Content="{Binding SelectedItem}">
        <ContentControl.Resources>
            <DataTemplate DataType="{x:Type local:MyModel}">
                <StackPanel Padding="10">
                    <TextBlock FontWeight="Bold" Text="{Binding Name}" FontSize="18"/>
                    <StackPanel Margin="0,5">
                        <TextBlock Text="文本内容:"/>
                        <TextBlock Text="{Binding Text}" Margin="5,0"/>
                    </StackPanel>
                    <StackPanel Margin="0,5">
                        <TextBlock Text="描述:"/>
                        <TextBlock Text="{Binding Description}" Margin="5,0"/>
                    </StackPanel>
                    <StackPanel Margin="0,5">
                        <TextBlock Text="额外信息:"/>
                        <TextBlock Text="{Binding EvenMoreInfo}" Margin="5,0"/>
                    </StackPanel>
                </StackPanel>
            </DataTemplate>
            <DataTemplate DataType="{x:Type CollectionViewGroup}">
                <StackPanel Padding="10">
                    <TextBlock FontWeight="Bold" Text="选中分组:"/>
                    <TextBlock Text="{Binding Name}" Margin="5,0" FontSize="16"/>
                    <TextBlock Text="包含项数:" Margin="5,0"/>
                    <TextBlock Text="{Binding Items.Count}" Margin="10,0"/>
                </StackPanel>
            </DataTemplate>
        </ContentControl.Resources>
    </ContentControl>
</Grid>

下方蓝色面板展示父组数据

修改蓝色Grid,添加DataGrid绑定SelectedGroupItems:

<Grid Grid.Row="3" Grid.Column="1" Background="LightBlue">
    <DataGrid 
        ItemsSource="{Binding SelectedGroupItems}"
        AutoGenerateColumns="False"
        CanUserAddRows="False" CanUserDeleteRows="False"
        VirtualizingStackPanel.IsVirtualizing="True"
        VirtualizingStackPanel.VirtualizationMode="Recycling"
        AlternatingRowBackground="LightCyan">
        <DataGrid.Columns>
            <DataGridTextColumn Header="名称" Binding="{Binding Name}" Width="*"/>
            <DataGridTextColumn Header="分组" Binding="{Binding GroupName}" Width="*"/>
            <DataGridTextColumn Header="缩写" Binding="{Binding ShortName}" Width="*"/>
            <DataGridTextColumn Header="编号" Binding="{Binding Number}" Width="Auto"/>
        </DataGrid.Columns>
    </DataGrid>
</Grid>

4. 155000条数据性能优化补充技巧

除了已启用的虚拟化,还可以通过以下方式进一步优化:

  • 异步加载数据:在ViewModel中异步加载数据,避免阻塞UI线程:
    public MainWindowViewModel()
    {
        _ = LoadDataAsync();
    }
    
    private async Task LoadDataAsync()
    {
        // 异步从文件读取原始数据
        var rawData = await Task.Run(() => LoadRawDataFromFile());
        // 转换为MyModel列表
        myModelList = TransformRawDataToMyModel(rawData);
        
        // 切换回UI线程更新CollectionView
        Application.Current.Dispatcher.Invoke(() =>
        {
            TreeViewCollection = CollectionViewSource.GetDefaultView(myModelList);
            // 原有分组、排序、过滤设置...
            OnPropertyChanged(nameof(TreeViewCollection));
        });
    }
    
  • 完善虚拟化配置:确保TreeView和DataGrid的虚拟化生效:
    <TreeView 
        VirtualizingStackPanel.IsVirtualizing="True"
        VirtualizingStackPanel.VirtualizationMode="Recycling"
        ScrollViewer.CanContentScroll="True" <!-- 必须开启才能激活虚拟化 -->
        VirtualizingStackPanel.ScrollUnit="Item"/> <!-- 按项滚动提升性能 -->
    
  • 简化模板与绑定:TreeView和DataGrid的模板尽量使用轻量级控件(如TextBlock),避免嵌套复杂布局;只绑定需要展示的属性,减少不必要的属性监听。
  • 分组数据预缓存:提前按ShortName和GroupName分组缓存数据,避免每次筛选时重复计算。
  • 过滤优化:将过滤逻辑改为大小写不敏感的索引匹配,或使用HashSet存储关键词,提升过滤速度:
    private bool FilterCollection(object obj)
    {
        if (obj is not MyModel model || string.IsNullOrEmpty(FilterValue))
            return true;
        return model.Name.IndexOf(FilterValue, StringComparison.OrdinalIgnoreCase) >= 0;
    }
    

内容的提问来源于stack exchange,提问作者Myszax

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 00:22:01