Tuesday, March 30, 2010

WPF DataGrid ScrollIntoView

Technorati Tags: ,,,

In my previous post I talked about using Attached Behaviors to scroll selected item into view. It turns out there is a bug in WPF DataGrid and ScrollIntoView could sometimes throw NullReferenceException when VirtualizingStackPanel.IsVirtualizing="True" .

To avoid this exception there was a solution suggested on this forum http://wpf.codeplex.com/Thread/View.aspx?ThreadId=39458 which basically executes ScrollIntoView on a thread with a very low priority.

Here is my previous solution with suggested work around.

public class DataGridBehavior
{
#region AutoScrollIntoView

public static bool GetAutoScrollIntoView(DataGrid dataGrid)
{
return (bool)dataGrid.GetValue(AutoScrollIntoViewProperty);
}

public static void SetAutoScrollIntoView(
DataGrid dataGrid, bool value)
{
dataGrid.SetValue(AutoScrollIntoViewProperty, value);
}

public static readonly DependencyProperty AutoScrollIntoViewProperty =
DependencyProperty.RegisterAttached(
"AutoScrollIntoView",
typeof(bool),
typeof(DataGridBehavior),
new UIPropertyMetadata(false, OnAutoScrollIntoViewWhenSelectionChanged));

static void OnAutoScrollIntoViewWhenSelectionChanged(
DependencyObject depObj, DependencyPropertyChangedEventArgs e)
{
DataGrid dataGrid = depObj as DataGrid;
if (dataGrid == null)
return;

if (!(e.NewValue is bool))
return;

if ((bool)e.NewValue)
dataGrid.SelectionChanged += OnDataGridSelectionChanged;
else
dataGrid.SelectionChanged -= OnDataGridSelectionChanged;
}

static void OnDataGridSelectionChanged(object sender, RoutedEventArgs e)
{
// Only react to the SelectionChanged event raised by the DataGrid
// Ignore all ancestors.
if (!Object.ReferenceEquals(sender, e.OriginalSource))
return;

DataGrid dataGrid = e.OriginalSource as DataGrid;
if (dataGrid != null && dataGrid.SelectedItem != null)
{
// this is a workaround to fix the layout issue.
// otherwise ScrollIntoView should work directly.
dataGrid.Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new DispatcherOperationCallback(ScrollItemIntoView),dataGrid);
}
}

static object ScrollItemIntoView(object sender)
{
DataGrid dataGrid = sender as DataGrid;
if (dataGrid != null && dataGrid.SelectedItem != null)
{
dataGrid.ScrollIntoView(dataGrid.SelectedItem);
}
return null;
}

#endregion // AutoScrollIntoView





Happy Coding!

4 comments:

  1. Thank you for this solution. Works great for me.

    ReplyDelete
  2. Really good tip, thanks!

    ReplyDelete
  3. Thank you a lot, I really was crazy with that stuff.

    ReplyDelete
  4. How to reproduce the bug ?

    ReplyDelete