I have TabControl
that has a DataGrid
inside each TabItem
. It's all populated via binding. I use the row details expansion functionality, so have set the VirtualizingPanel
ScrollUnit
to Pixel
, so scrolling is a bit more natural.
When changing between TabItems I have row selection behaving correctly. However, setting the vertical offset on the DataGrid's ScrollViewer
so it is in the exact same position as it was when you left the TabItem
is not working correctly.
The way it works at the moment is, I have a behavior class on the DataGrid
. On a Scrollviewer
ScrollChangedEvent
it saves the VerticalOffset
. Upon changing to a new TabItem
and changing back to the original TabItem
, in the DataGrid's DataContextChanged
event I set the ScrollViewer's VerticalOffset
to the saved 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;
}
}
What is happening is you scroll down and the top DataGridRow is partially displayed (You only see half of it). Then when you toggle between two TabItems, the program sets the VerticalOffset correctly, but then it resets again automatically to the top of the partially displayed row (showing it fully).
Before toggling. Saves VertcialOffset to 4327.2
After toggling back to the original TabItem Sets VerticalOffset to 4327.2, then for some reason and somehow automatically resets VerticalOffset to 4321.5, which is the top of the 'previously' partially visible row
It gets even weirder when you have an expanded row loaded in the VirtualizingPanel, the jump is more dramatic.
Before toggling
After toggling back to the original TabItem
I would like to see the scroll position in exactly the same spot as when I left it, how can I accomplish this?