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
相关产品推荐
相关产品推荐

