WPF TabControl切换时DataGrid绑定数据错乱问题求助
WPF TabControl切换标签页后DataGrid绑定数据错乱问题
在TabControl中包含两个DataGrid,分别对应“Files”和“Symbols”标签页。切换到“Symbols”标签页后再切回“Files”时,Files DataGrid的显示模板正确,但加载的却是Symbols的数据,仿佛两个DataGrid的绑定在切换标签时发生了交换。尝试过在切换标签时强制设置当前显示DataGrid的ItemSource作为临时解决方法,但会导致行项断开关联。
相关代码
XAML代码
<TabControl Grid.Row="1" x:Name="tabsControl" SelectionChanged="OnTabChanged_TabsControl"> <TabItem x:Name="filesTab"> <TabItem.HeaderTemplate> <DataTemplate> <StackPanel Orientation="Horizontal"> <Image Source="{DynamicResource FilesTabIcon}" Width="19" Height="19"/> <TextBlock Text="Files" Margin="4,0,0,0" VerticalAlignment="Center"/> </StackPanel> </DataTemplate> </TabItem.HeaderTemplate> <Grid> <!-- data --> <DataGrid x:Name="filesDataGrid" ItemsSource="{Binding Files}" Style="{DynamicResource SearchDataGrid}" Sorting="OnDataSort_Files" SelectionMode="Single" AutoGenerateColumns="False" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="10" IsReadOnly="True" Visibility="Collapsed"> <DataGrid.Columns> <DataGridTemplateColumn Header="Name" Width="auto" SortMemberPath="Name"> <DataGridTemplateColumn.CellTemplate> <DataTemplate> <StackPanel Orientation="Horizontal" HorizontalAlignment="Left"> <Image Source="{Binding ImageSource}" Width="16" Height="16" Margin="0,0,4,0"/> <TextBlock Text="{Binding Name}" HorizontalAlignment="Stretch" MouseLeftButtonDown="OnClick_FilesItem"/> </StackPanel> </DataTemplate> </DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn> <DataGridTemplateColumn Header="Path" Width="*" SortMemberPath="Path"> <DataGridTemplateColumn.CellTemplate> <DataTemplate> <StackPanel Orientation="Horizontal"> <Button Click="OnClick_FileLink" ToolTip="Copy path to clipboard" Height="16" Width="25" Padding="0,-2,0,0" Style="{DynamicResource ButtonStyle}"> <Image Source="{DynamicResource LinkImageResource}" Width="19" Height="19" Margin="0"/> </Button> <TextBlock Text="{Binding Path}" Margin="4,0,0,0" MouseLeftButtonDown="OnClick_FilesItem"/> </StackPanel> </DataTemplate> </DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn> </DataGrid.Columns> </DataGrid> </Grid> </TabItem> <TabItem x:Name="symbolsTab"> <TabItem.HeaderTemplate> <DataTemplate> <StackPanel Orientation="Horizontal"> <Image Source="{DynamicResource SymbolsTabIcon}" Width="19" Height="19"/> <TextBlock Text="Symbols" Margin="4,0,0,0" VerticalAlignment="Center"/> </StackPanel> </DataTemplate> </TabItem.HeaderTemplate> <Grid> <!-- data --> <DataGrid x:Name="symbolsDataGrid" ItemsSource="{Binding Symbols}" Style="{DynamicResource SearchDataGrid}" Sorting="OnDataSort_Symbols" SelectionMode="Single" AutoGenerateColumns="False" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="10" IsReadOnly="True" Visibility="Collapsed"> <DataGrid.Columns> <DataGridTextColumn Header="Name" Binding="{Binding Name}" Width="auto" SortMemberPath="Name"/> <DataGridTemplateColumn Header="Name" Width="auto" SortMemberPath="Name"> <DataGridTemplateColumn.CellTemplate> <DataTemplate> <StackPanel Orientation="Horizontal" HorizontalAlignment="Left"> <Button Click="OnClick_SymbolLink" ToolTip="Copy path to clipboard" Height="16" Width="25" Padding="0,-2,0,0" Style="{DynamicResource ButtonStyle}"> <Image Source="{DynamicResource LinkImageResource}" Width="19" Height="19" Margin="0"/> </Button> <TextBlock Text="{Binding Path}" HorizontalAlignment="Stretch" Margin="4,0,0,0" MouseLeftButtonDown="OnClick_SymbolsItem"/> </StackPanel> </DataTemplate> </DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn> </DataGrid.Columns> </DataGrid> </Grid> </TabItem> </TabControl>
后台代码
public partial class MyControl : UserControl, INotifyPropertyChanged { private List<SearchDataItem> _files = null; public List<SearchDataItem> Files { get { return _files; } set { if (_files != value) { _files = value; OnPropertyChanged(nameof(Files)); } } } private List<SearchDataItem> _symbols = null; public List<SearchDataItem> Symbols { get { return _symbols; } set { if (_symbols != value) { _symbols = value; OnPropertyChanged(nameof(Symbols)); } } } public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } public FilesSearchControl() { this.InitializeComponent(); DataContext = this; // make sure the DC is set to this object } ... }
问题原因
WPF TabControl默认会对未激活的TabItem进行虚拟化处理(延迟加载或卸载内容),当切换标签时,可能因DataTemplate复用、绑定上下文共享导致数据绑定错乱。加上两个DataGrid使用相同类型的数据源,进一步加剧了这个问题。
解决方法
1. 为每个TabItem显式隔离DataContext
避免TabItem之间共享绑定上下文,给每个TabItem的容器单独设置DataContext,确保绑定独立性:
<TabItem x:Name="filesTab"> <TabItem.HeaderTemplate> <!-- 原有HeaderTemplate内容 --> </TabItem.HeaderTemplate> <Grid DataContext="{Binding}"> <!-- 原有filesDataGrid内容 --> </Grid> </TabItem> <TabItem x:Name="symbolsTab"> <TabItem.HeaderTemplate> <!-- 原有HeaderTemplate内容 --> </TabItem.HeaderTemplate> <Grid DataContext="{Binding}"> <!-- 原有symbolsDataGrid内容 --> </Grid> </TabItem>
2. 禁用TabControl虚拟化
强制TabControl加载所有TabItem内容,避免卸载导致的绑定问题,适合TabItem数量较少的场景:
<TabControl Grid.Row="1" x:Name="tabsControl" SelectionChanged="OnTabChanged_TabsControl" VirtualizingPanel.IsVirtualizing="False"> <!-- 原有TabItem内容 --> </TabControl>
3. 改用ObservableCollection作为数据源
List<T>不会自动通知UI数据变化,替换为ObservableCollection<T>可提升绑定可靠性:
private ObservableCollection<SearchDataItem> _files = new ObservableCollection<SearchDataItem>(); public ObservableCollection<SearchDataItem> Files { get { return _files; } set { if (_files != value) { _files = value; OnPropertyChanged(nameof(Files)); } } } private ObservableCollection<SearchDataItem> _symbols = new ObservableCollection<SearchDataItem>(); public ObservableCollection<SearchDataItem> Symbols { get { return _symbols; } set { if (_symbols != value) { _symbols = value; OnPropertyChanged(nameof(Symbols)); } } }
4. 优化SelectionChanged事件的临时方案
如果要保留强制设置ItemSource的方式,先置空再重新赋值,避免绑定复用旧引用:
private void OnTabChanged_TabsControl(object sender, SelectionChangedEventArgs e) { if (tabsControl.SelectedItem == filesTab) { filesDataGrid.ItemsSource = null; filesDataGrid.ItemsSource = Files; } else if (tabsControl.SelectedItem == symbolsTab) { symbolsDataGrid.ItemsSource = null; symbolsDataGrid.ItemsSource = Symbols; } }
内容的提问来源于stack exchange,提问作者pf12345678910
相关产品推荐
相关产品推荐

