在 TabItems 之间切换后设置 DataGridRow RowDetails 可见性

Setting DataGridRow RowDetails visibility after toggling between TabItems

我有一个TabControl,在每个TabItem中有一个DataGrid。每个 DataGridRowDataGridRowHeader 上都有一个按钮,用于展开和折叠 RowDetails

由于我使用了行详情扩展功能,所以将VirtualizingPanel ScrollUnit设置为Pixel,所以滚动更自然一些。

程序在 ObservableCollection<int> 中捕获哪些行已扩展,其中包含要扩展的行索引。

当我切换到新的 TabItem 并返回原来的 TabItem 时,它会丢失已展开的行。

在更改 DataContext 后,我想使用 ObservableCollection 来重置展开的行。我在 DataGrid DataContextChanged 事件中尝试过这个。

public class DataGridBehaviors : Behavior<DataGrid>{
    protected override void OnAttached(){
        base.OnAttached();
        this.AssociatedObject.DataContextChanged += DataGrid_DataContextChanged;
    }

    protected override void OnDetaching(){
        this.AssociatedObject.DataContextChanged -= DataGrid_DataContextChanged;
        base.OnDetaching();
    }

    private void DataGrid_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e){
        ModuleGeometry oldModuleGeometry = (ModuleGeometry)e.OldValue;
        ModuleGeometry newModuleGeometry = (ModuleGeometry)e.NewValue;

        Task.Factory.StartNew(() => {
            PerformRowExpansions(newModuleGeometry);
        });

        ScrollViewer scrollViewer = GetVisualChild<ScrollViewer>(this.AssociatedObject);
        if (scrollViewer != null){
            /*
             * do some stuff
             */
        }
    }

    private void PerformRowExpansions(ModuleGeometry newModuleGeometry){
        ObservableCollection<int> tempExpandedIndexes = new ObservableCollection<int>(newModuleGeometry.ExpandedIndexes);
        newModuleGeometry.ExpandedIndexes = new ObservableCollection<int>();

        if (this.Dispatcher.CheckAccess()){
            foreach (int index in tempExpandedIndexes)
            {
                DataGridRow row = this.AssociatedObject.ItemContainerGenerator.ContainerFromIndex(index) as DataGridRow;
                row.DetailsVisibility = Visibility.Visible;
            }
        }
        else{
            this.Dispatcher.Invoke(new Action(() =>{
                foreach (int index in tempExpandedIndexes){
                    DataGridRow row = this.AssociatedObject.ItemContainerGenerator.ContainerFromIndex(index) as DataGridRow;
                    row.DetailsVisibility = Visibility.Visible;
                }
            }));
        }
    }

    private static T GetVisualChild<T>(DependencyObject parent) where T : Visual{
        T child = default(T);

        int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
        for (int i = 0; i < numVisuals; i++){
            Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
            child = v as T;
            if (child == null)
                child = GetVisualChild<T>(v);
            if (child != null)
                break;
        }
        return child;
    }
}

但是,它不喜欢对当前不在 VirtualizingPanel 中的行执行此操作。

我怎样才能做到这一点?
我应该只扩展当前虚拟化的行吗?
DataGrid ScrollViewer 是否也应该以编程方式进行操作?我如何解释 ScrollViewer 中不可见的行?
除了我应该将 RowDetails Visibility 设置为 DataGridRow 之外,还有其他对象吗?

当前不在 VirtualizingPanel 中的行没有 DataGridRow 个容器。这就是虚拟化的工作原理。

如果你想在选项卡切换之间保留一些信息,你应该将要保留的数据存储在你的视图模型中。例如,您可以将 DataGridRowHeader 中的 Button 绑定到一些命令,该命令在基础数据 class 的集合中添加和删除索引,即父 DataContext DataGridRowTabItemTabControl.

除非禁用 UI 虚拟化,否则尝试遍历可视 DataGridRow 容器将无法工作。这当然可能会导致性能问题。