在 TabControl TabItems 之间切换时捕获和设置 DataGrid 滚动位置
Capturing and setting DataGrid scroll position when toggling between TabControl TabItems
我有 TabControl
,每个 TabItem
里面有一个 DataGrid
。它都是通过绑定填充的。我使用行详细信息扩展功能,因此将 VirtualizingPanel
ScrollUnit
设置为 Pixel
,这样滚动会更自然一些。
在 TabItems 之间切换时,我的行选择行为正确。但是,在 DataGrid 的 ScrollViewer
上设置垂直偏移,使其与您离开 TabItem
时的位置完全相同,但无法正常工作。
目前它的工作方式是,我在 DataGrid
上有一个行为 class。在 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,这是 'previously' 部分可见行的顶部
当您在 VirtualizingPanel 中加载展开的行时,它变得更加奇怪,跳跃更加引人注目。
切换前
切换回原始 TabItem 后
我希望看到滚动位置与我离开时的位置完全相同,我该如何实现?
最简单的方法可能是在切换选项卡时阻止 TabControl
卸载可视化树。那么每个tab的内容都要保留。
有关这方面的更多信息,请参阅以下链接。
How to stop Wpf Tabcontrol to unload Visual tree on Tab change
WPF - Elements inside DataTemplate property issue when no binding?
我有 TabControl
,每个 TabItem
里面有一个 DataGrid
。它都是通过绑定填充的。我使用行详细信息扩展功能,因此将 VirtualizingPanel
ScrollUnit
设置为 Pixel
,这样滚动会更自然一些。
在 TabItems 之间切换时,我的行选择行为正确。但是,在 DataGrid 的 ScrollViewer
上设置垂直偏移,使其与您离开 TabItem
时的位置完全相同,但无法正常工作。
目前它的工作方式是,我在 DataGrid
上有一个行为 class。在 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
当您在 VirtualizingPanel 中加载展开的行时,它变得更加奇怪,跳跃更加引人注目。
切换前
我希望看到滚动位置与我离开时的位置完全相同,我该如何实现?
最简单的方法可能是在切换选项卡时阻止 TabControl
卸载可视化树。那么每个tab的内容都要保留。
有关这方面的更多信息,请参阅以下链接。
How to stop Wpf Tabcontrol to unload Visual tree on Tab change
WPF - Elements inside DataTemplate property issue when no binding?