WPF绑定TreeView切换TabControl标签时选中状态重置问题
问题解决:WPF TabControl切换时TreeView状态丢失
需要在模型中保存每个TreeView的选中项和展开项状态,因为TabControl默认会销毁非活动标签对应的内容模板实例,导致TreeView的临时状态(选中、展开)丢失。以下是具体实现方案:
1. 调整模型结构
为每个树形节点添加展开状态属性,同时让模型实现INotifyPropertyChanged以支持数据绑定更新:
TreeItemViewModel(树形节点模型)
using System.ComponentModel; using System.Runtime.CompilerServices; public class TreeItemViewModel : INotifyPropertyChanged { private string _name; private bool _isExpanded; public string Name { get => _name; set { _name = value; OnPropertyChanged(); } } public bool IsExpanded { get => _isExpanded; set { _isExpanded = value; OnPropertyChanged(); } } public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
扩展后的ProjectModel
using System.Collections.ObjectModel; using System.ComponentModel; using System.Runtime.CompilerServices; public class ProjectModel : INotifyPropertyChanged { private string _name; private TreeItemViewModel _selectedTreeItem; private ObservableCollection<TreeItemViewModel> _structure; public string Name { get => _name; set { _name = value; OnPropertyChanged(); } } public ObservableCollection<TreeItemViewModel> Structure { get => _structure; set { _structure = value; OnPropertyChanged(); } } public TreeItemViewModel SelectedTreeItem { get => _selectedTreeItem; set { _selectedTreeItem = value; OnPropertyChanged(); } } public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
2. 修改XAML绑定
将TreeView的选中项、每个节点的展开状态与模型属性绑定:
<TabControl x:Name="tcTest" > <!-- TabControl Style --> <TabControl.Resources> <Style TargetType="TabItem" BasedOn="{StaticResource {x:Type TabItem}}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type TabItem}"> <Grid SnapsToDevicePixels="true"> <Border Padding="8,3" BorderThickness="1,1,1,0" Height="27" Background="CadetBlue" > <DockPanel> <TextBlock Text="{Binding Name}" /> </DockPanel> </Border> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style> <!-- 绑定TreeViewItem的展开状态 --> <Style TargetType="TreeViewItem"> <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" /> </Style> </TabControl.Resources> <!-- TabControl Content Template --> <TabControl.ContentTemplate> <DataTemplate> <StackPanel> <TreeView ItemsSource="{Binding Path=Structure}" SelectedItem="{Binding SelectedTreeItem, Mode=TwoWay}"> <TreeView.ItemTemplate> <DataTemplate> <TextBlock Text="{Binding Name}" /> </DataTemplate> </TreeView.ItemTemplate> </TreeView> </StackPanel> </DataTemplate> </TabControl.ContentTemplate> </TabControl>
3. 更新初始化代码
用TreeItemViewModel替换原来的字符串集合:
using System.Collections.ObjectModel; public ObservableCollection<ProjectModel> Projects = new ObservableCollection<ProjectModel>(); public MainWindow() { InitializeComponent(); Projects.Add(new ProjectModel { Name = "Test", Structure = new ObservableCollection<TreeItemViewModel> { new TreeItemViewModel { Name = "Item 1" }, new TreeItemViewModel { Name = "Item 2" }, new TreeItemViewModel { Name = "Item 3" }, new TreeItemViewModel { Name = "Item 4" }, } }); Projects.Add(new ProjectModel { Name = "Help", Structure = new ObservableCollection<TreeItemViewModel> { new TreeItemViewModel { Name = "Item 5" }, new TreeItemViewModel { Name = "Item 6" }, new TreeItemViewModel { Name = "Item 7" }, new TreeItemViewModel { Name = "Item 8" }, } }); tcTest.ItemsSource = Projects; }
替代方案(不推荐)
如果不想修改模型,可以禁用TabControl的内容虚拟化,但会增加内存占用:
<TabControl x:Name="tcTest" VirtualizingStackPanel.IsVirtualizing="False"> <!-- 原有内容 --> </TabControl>
内容的提问来源于stack exchange,提问作者SideSky
相关产品推荐
相关产品推荐

