since we have installed the .Net 4.8 Framework patches KB5020687 (and KB5020613 ?) from November '22 my WPF DataGrids show a different behaviour.
Before the patch, when I added new items to a bound ObservableCollection my DataGrids, which are sorted from newest item to oldest, added the new items nicely sorted on top and "scrolled" towards the top row.
After the patch was installed, my newly added items are equally nicely sorted above the top row, but the automatic "scrolling" doesn't appear to happen. You can't see the new row, you only see that the datagrid's scrollbar is slightly shifted. To see the new added row in the DataGrid you have to manually scroll upwards.
I couldnt't find anyone with the same issue. How can I avoid the "new feature", or automatically scroll to the top row?
I tried using some sort of trigger/event in my DataGrid to programatically scroll to the to row, but it didn't work out as expected because i couldn't find any event that triggers, when new rows are added to the bound ObservableCollection.
CodePudding user response:
You could programmatically scroll to the top like this:
var border = VisualTreeHelper.GetChild(dataGrid, 0) as Border;
if (border != null)
{
var scrollViewer = border.Child as ScrollViewer;
scrollViewer?.ScrollToTop();
}
CodePudding user response:
You can scroll programmatically. Your implementation details are undisclosed, therefore the solution can only be very generic.
Extend DataGrid
Encapsulate the logic.
Alternatively, move the logic to an attached behavior.
public class AutoScrollDataGrid : DataGrid
{
protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
{
base.OnItemsChanged(e);
// In case you want to scroll to a particular added/moved item, you can
// use the 'NotifyCollectionChangedEventArgs.NewItems' property.
// Depending on the exact requirement, this can be useful
// if the added item is neither at the beginning nor at the end,
// for example due to sorting.
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
ScrollToTop();
//ScrollToItem(e.NewItems[^1]);
break;
case NotifyCollectionChangedAction.Remove:
break;
case NotifyCollectionChangedAction.Replace:
break;
case NotifyCollectionChangedAction.Move:
break;
case NotifyCollectionChangedAction.Reset:
break;
}
}
private void ScrollToTop()
=> ScrollIntoView(this.Items[0]);
private void ScrollToBottom()
=> ScrollIntoView(this.Items[this.CanUserAddRows ? ^2 : ^1]);
private void ScrollToItem(object item)
=> ScrollIntoView(item);
}
Reference DataGrid
<Window>
<DataGrid x:Name="DataGrid" />
</Window>
partial class MainWindow : Window
{
// The ItemsSource of the DataGrid
public ObservableCollection<object> Items { get; }
public MainWindow()
{
this.Items.CollectionChanged = OnItemsChanged;
}
private void OnItemsChanged(object sender, NotifyCollectionChangedEventArgs e)
{
// In case you want to scroll to a particular added/moved item, you can
// use the 'NotifyCollectionChangedEventArgs.NewItems' property.
// Depending on the exact requirement, this can be useful
// if the added item is neither at the beginning nor at the end,
// for example due to sorting.
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
ScrollToTop();
//ScrollToItem(e.NewItems[^1]);
break;
case NotifyCollectionChangedAction.Remove:
break;
case NotifyCollectionChangedAction.Replace:
break;
case NotifyCollectionChangedAction.Move:
break;
case NotifyCollectionChangedAction.Reset:
break;
}
}
private void ScrollToTop()
=> this.DataGrid.ScrollIntoView(this.Items.FirstOrDefault());
private void ScrollToBottom()
=> this.DataGrid.ScrollIntoView(this.Items.LastOrDefault());
private void ScrollToItem(object item)
=> this.DataGrid.ScrollIntoView(item);
}
Remarks
For a solution that requires pixel based offset scrolling, you would have to get the ScrollViewer
of the DataGrid
. But there should be no need for this in common scenarios, especially when UI virtualization is enabled (row virtualization is enabled by default for the DataGrid
).