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
You should use the datagrid method
datagrid.ScrollIntoView(itemInRow);
or
datagrid.ScrollIntoView(itemInRow, column);
this way provides no messing around finding the scroll viewer etc.
I've written an attached property for grid autoscroll:
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Windows;
using System.Windows.Controls;
public static class DataGridBehavior
{
public static readonly DependencyProperty AutoscrollProperty = DependencyProperty.RegisterAttached(
"Autoscroll", typeof(bool), typeof(DataGridBehavior), new PropertyMetadata(default(bool), AutoscrollChangedCallback));
private static readonly Dictionary<DataGrid, NotifyCollectionChangedEventHandler> handlersDict = new Dictionary<DataGrid, NotifyCollectionChangedEventHandler>();
private static void AutoscrollChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
{
var dataGrid = dependencyObject as DataGrid;
if (dataGrid == null)
{
throw new InvalidOperationException("Dependency object is not DataGrid.");
}
if ((bool)args.NewValue)
{
Subscribe(dataGrid);
dataGrid.Unloaded += DataGridOnUnloaded;
dataGrid.Loaded += DataGridOnLoaded;
}
else
{
Unsubscribe(dataGrid);
dataGrid.Unloaded -= DataGridOnUnloaded;
dataGrid.Loaded -= DataGridOnLoaded;
}
}
private static void Subscribe(DataGrid dataGrid)
{
var handler = new NotifyCollectionChangedEventHandler((sender, eventArgs) => ScrollToEnd(dataGrid));
handlersDict.Add(dataGrid, handler);
((INotifyCollectionChanged)dataGrid.Items).CollectionChanged += handler;
ScrollToEnd(dataGrid);
}
private static void Unsubscribe(DataGrid dataGrid)
{
NotifyCollectionChangedEventHandler handler;
handlersDict.TryGetValue(dataGrid, out handler);
if (handler == null)
{
return;
}
((INotifyCollectionChanged)dataGrid.Items).CollectionChanged -= handler;
handlersDict.Remove(dataGrid);
}
private static void DataGridOnLoaded(object sender, RoutedEventArgs routedEventArgs)
{
var dataGrid = (DataGrid)sender;
if (GetAutoscroll(dataGrid))
{
Subscribe(dataGrid);
}
}
private static void DataGridOnUnloaded(object sender, RoutedEventArgs routedEventArgs)
{
var dataGrid = (DataGrid)sender;
if (GetAutoscroll(dataGrid))
{
Unsubscribe(dataGrid);
}
}
private static void ScrollToEnd(DataGrid datagrid)
{
if (datagrid.Items.Count == 0)
{
return;
}
datagrid.ScrollIntoView(datagrid.Items[datagrid.Items.Count - 1]);
}
public static void SetAutoscroll(DependencyObject element, bool value)
{
element.SetValue(AutoscrollProperty, value);
}
public static bool GetAutoscroll(DependencyObject element)
{
return (bool)element.GetValue(AutoscrollProperty);
}
}
Usage:
<DataGrid c:DataGridBehavior.Autoscroll="{Binding AutoScroll}"/>
Actually...
I had the same problem as well when I was learning about Collection Views about doing DataContext in WPF.
I too was faced with a task of slapping together a WPF program that I need to programmically to move up and down on the DataGrid using buttons since I needed to put it on a resistive touchscreen ONLY for the production builders \t my company, and there's no mouse or keyboard for them to use.
But this example worked for me using the ScrollIntoView
method as previously mentioned in this post:
private void OnMoveUp(object sender, RoutedEventArgs e)
{
ICollectionView myCollectView = CollectionViewSource.GetDefaultView(Orders);
if (myCollectView.CurrentPosition > 0)
myCollectView.MoveCurrentToPrevious();
if (myCollectView.CurrentItem != null)
theDataGrid.ScrollIntoView(myCollectView.CurrentItem);
}
private void OnMoveDown(object sender, RoutedEventArgs e)
{
ICollectionView myCollectView = CollectionViewSource.GetDefaultView(Orders);
if (myCollectView.CurrentPosition < Orders.Count)
myCollectView.MoveCurrentToNext();
if (myCollectView.CurrentItem !=null)
theDataGrid.ScrollIntoView(myCollectView.CurrentItem);
}
Where Orders is a List<T>
collection
in XAML:
<StackPanel Grid.Row="1"
Orientation="Horizontal">
<Button Click="OnMoveUp">
<Image Source="Up.jpg" />
</Button>
<Button Click="OnMoveDown">
<Image Source="Down.jpg" />
</Button>
</StackPanel>
<DataGrid Grid.Row="2"
x:Name="theDataGrid"
ItemSource="{Binding Orders}"
ScrollViewer.CanContentScroll="True"
ScrollViewer.VerticalScrollBarVisibility="Auto" Margin="0,0,0,5">
<< code >>
</DataGrid>
Do follow the previous advice and keep the DataGrid by itself and not in a stack panel. For the Row Definition for the DataGrid (the third row in this case), I set the Height at 150, and the scrollbar works.
listbox.Add(foo);
listbox.SelectedIndex = count - 1;
listbox.ScrollIntoView(listbox.SelectedItem);
listbox.SelectedIndex = -1;
If you are using MVVM pattern, you can have a combination of this article with this other: http://www.codeproject.com/KB/WPF/AccessControlsInViewModel.aspx.
The idea is to use attached properties to access the control in your ViewModel class. Once you do that, you would need to check that the datagrid is not null, and it has any items.
if ((mainDataGrid != null) && (mainDataGrid.Items.Count > 0)){
//Same snippet
}
What you need is to get the reference to the ScrollViewer object for your DataGrid. You can then manipulate the VerticalOffset property to scroll to the bottom.
To add even more flare to your app...you could add a Spline animation to the scroll so everything looks up to par with the rest of the application.