【问题标题】:Capturing and setting DataGrid scroll position when toggling between TabControl TabItems在 TabControl TabItems 之间切换时捕获和设置 DataGrid 滚动位置
【发布时间】:2017-09-10 04:26:18
【问题描述】:

我有TabControl,每个TabItem 内部都有一个DataGrid。它都是通过绑定填充的。我使用了行详细信息扩展功能,所以将VirtualizingPanelScrollUnit设置为Pixel,这样滚动更自然一点。

在 TabItems 之间进行更改时,我的行选择行为正确。但是,在 DataGrid 的 ScrollViewer 上设置垂直偏移量,使其与您离开 TabItem 时的位置完全相同。

目前它的工作方式是,我在DataGrid 上有一个行为类。在 Scrollviewer ScrollChangedEvent 上,它保存了 VerticalOffset。在更改为新的TabItem 并更改回原来的TabItem 后,在DataGrid 的DataContextChanged 事件中,我将ScrollViewer 的VerticalOffset 设置为保存的VerticalOffset

public class DataGridBehaviors : Behavior<DataGrid>
{
    protected override void OnAttached()
    {
        base.OnAttached();
        this.AssociatedObject.DataContextChanged += DataGrid_DataContextChanged;
        this.AssociatedObject.AddHandler(ScrollViewer.ScrollChangedEvent, new ScrollChangedEventHandler(DataGridScrollViewer_ScrollChanged));
    }

    protected override void OnDetaching()
    {
        Console.WriteLine("OnDetaching");
        this.AssociatedObject.RemoveHandler(ScrollViewer.ScrollChangedEvent, new ScrollChangedEventHandler(DataGridScrollViewer_ScrollChanged));
        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;
        ScrollViewer scrollViewer = GetVisualChild<ScrollViewer>(this.AssociatedObject);
        if (scrollViewer != null)
        {
            scrollViewer.ScrollToVerticalOffset(newModuleGeometry.VerticalScrollPosition);
        }
    }

    private void DataGridScrollViewer_ScrollChanged(object sender, ScrollChangedEventArgs e)
    {
        ModuleGeometry modGeom = (ModuleGeometry)this.AssociatedObject.DataContext;
        modGeom.VerticalScrollPosition = e.VerticalOffset;
    }

    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;
    }
}

发生的情况是您向下滚动并且顶部的 DataGridRow 部分显示(您只能看到其中的一半)。然后,当您在两个 TabItem 之间切换时,程序会正确设置 VerticalOffset,但随后会再次自动重置到部分显示行的顶部(完全显示)。

切换之前。将 VertcialOffset 保存到 4327.2 切换回原始 TabItem 后将 VerticalOffset 设置为 4327.2,然后出于某种原因并以某种方式自动将 VerticalOffset 重置为 4321.5,这是“以前”部分可见行的顶部

当您在 VirtualizingPanel 中加载扩展行时,它会变得更加奇怪,跳跃更加剧烈。

切换前 切换回原始 TabItem 后

我想看到滚动位置与我离开时完全相同的位置,我该如何做到这一点?

【问题讨论】:

    标签: c# wpf datagrid tabcontrol scrollviewer


    【解决方案1】:

    最简单的方法可能是在您切换选项卡时阻止TabControl 卸载可视化树。然后应该保留每个选项卡的内容。

    有关更多信息,请参阅以下链接。

    How to stop Wpf Tabcontrol to unload Visual tree on Tab change

    WPF - Elements inside DataTemplate property issue when no binding?

    【讨论】:

    • 感谢您为这些类似问题所花费的链接和时间,但是,我并不是在寻找最简单的解决方案。
    • 好吧,在这种情况下,最简单的和最好的可能是相同的。另一个选项是捕获 ScrollBar 的垂直偏移量,将其绑定到某个源属性,然后在切换回选项卡时将其设置回来。听起来很多(不必要的?)努力。
    • 如果您不想重置 ScrollBar 位置,显然您不想卸载内容。
    • 也许,这些网格中会有很多数据。我已经捕获了垂直偏移,该位并没有花费很长时间,但是当我再次将其设置回来时,它会像我上面提到的那样发生变化。应该在什么时候设置 VerticalOffsetm
    • 是的,重置滚动查看器的位置,然后将其设置回 精确 相同的位置比您想象的要难。我已经就如何解决这个问题给了你我的建议。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-13
    • 1970-01-01
    相关资源
    最近更新 更多