I think I am stupid. I searched now for 15 minutes, and found several different solutions for scrolling on datagrids, but none seems to work for me.
I am using WPF w
I know this is a late answer, but just for the people that are searching around, I found THE EASYEST way to scroll to the bottom of a DataGrid. in the DataContextChanged
event put this in:
myDataGrid.ScrollIntoView(CollectionView.NewItemPlaceholder);
Easy huh?
This is why it works: On every data grid there is a place at the bottom of the DataGrid where you can add a new item to your list that it's bound to. That is a CollectionView.NewItemPlaceholder
, and there will only be one of those in your DataGrid. So you can just scroll to that.
Following code works for me;
Private Sub DataGrid1_LoadingRow(sender As Object, e As DataGridRowEventArgs) Handles DataGrid1.LoadingRow
DataGrid1.ScrollIntoView(DataGrid1.Items.GetItemAt(DataGrid1.Items.Count - 1))
End Sub
If you used dataview for the datagrid.datacontext, you can use this:
private void dgvRecords_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
var dv = dgvRecords.DataContext as DataView;
if (dv.Count > 0)
{
var drv = dv[dv.Count - 1] as DataRowView;
dgvRecords.ScrollIntoView(drv);
}
}
If you are looking for a MVVM way of doing autoscroll, then you can use autoscroll behavior. The behavior scrolls to a selected item, just add a reference to System.Windows.Interactivity.dll:
public class ScrollIntoViewBehavior : Behavior<DataGrid>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.SelectionChanged += new SelectionChangedEventHandler(AssociatedObject_SelectionChanged);
}
void AssociatedObject_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (sender is DataGrid)
{
DataGrid grid = (sender as DataGrid);
if (grid?.SelectedItem != null)
{
grid.Dispatcher.InvokeAsync(() =>
{
grid.UpdateLayout();
grid.ScrollIntoView(grid.SelectedItem, null);
});
}
}
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.SelectionChanged -=
new SelectionChangedEventHandler(AssociatedObject_SelectionChanged);
}
}
XAML
<DataGrid>
<i:Interaction.Behaviors>
<local:ScrollIntoViewBehavior/>
</i:Interaction.Behaviors>
</DataGrid>
I've found that the easiest way to do this is to call the ScrollIntoView method from the ScrollViewer.ScrollChanged attached event. This can be set in XAML as follows:
<DataGrid
...
ScrollViewer.ScrollChanged="control_ScrollChanged">
The ScrollChangedEventArgs object has various properties that can be helpful for computing layout and scroll position (Extent, Offset, Viewport). Note that these are typically measured in numbers of rows/columns when using the default DataGrid virtualization settings.
Here's an example implementation that keeps the bottom item in view as new items are added to the DataGrid, unless the user moves the scrollbar to view items higher up in the grid.
private void control_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
// If the entire contents fit on the screen, ignore this event
if (e.ExtentHeight < e.ViewportHeight)
return;
// If no items are available to display, ignore this event
if (this.Items.Count <= 0)
return;
// If the ExtentHeight and ViewportHeight haven't changed, ignore this event
if (e.ExtentHeightChange == 0.0 && e.ViewportHeightChange == 0.0)
return;
// If we were close to the bottom when a new item appeared,
// scroll the new item into view. We pick a threshold of 5
// items since issues were seen when resizing the window with
// smaller threshold values.
var oldExtentHeight = e.ExtentHeight - e.ExtentHeightChange;
var oldVerticalOffset = e.VerticalOffset - e.VerticalChange;
var oldViewportHeight = e.ViewportHeight - e.ViewportHeightChange;
if (oldVerticalOffset + oldViewportHeight + 5 >= oldExtentHeight)
this.ScrollIntoView(this.Items[this.Items.Count - 1]);
}
Here's another excellent solution.
public sealed class CustomDataGrid : DataGrid
{
protected override void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
{
base.OnItemsSourceChanged(oldValue, newValue);
}
protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
{
base.OnItemsChanged(e);
if (this.Items.Count > 0) this.ScrollIntoView(this.Items[this.Items.Count - 1]);
}
}