10

I have a DataGrid full of notes, and it's possible that a note will be taller then the DataGrid's height. When this happens, if you try and scroll down to read the bottom half of the note, the DataGrid immediately skips to the next row.

This not only prevents users from viewing the full note, but also causes the scrolling to feel choppy because it appears to jump around.

Is there a way to tell WPF to scroll smoothly past a long note without disabling the default DataGrid virtualization?

Rachel
  • 130,264
  • 66
  • 304
  • 490
  • Couldn't you wrap the note in a `ScrollViewer` whose `MaxHeight` is set to the DataGrid's height? – McGarnagle Nov 14 '12 at 17:08
  • 1
    @dbaseman I want the headers to always be visible, and that would disable the DataGrid's virtualization since it would render all items – Rachel Nov 14 '12 at 17:12
  • Have you tried setting the `CanContentScroll` to `False` for dataGrid's ScrollViewer? – Rohit Vats Nov 14 '12 at 17:24
  • @RV1987 Why would I do that? I want the scrolling behavior, but I want it to scroll smoothly through large items and not scroll down one item at a time. Setting `CanContentScroll` would set scrolling to false and disable virtualization – Rachel Nov 14 '12 at 17:25
  • Have you seen this link: http://stackoverflow.com/questions/3062540/wpf-datagrid-cancontentscroll-property-causing-odd-behavior – Jason Massey Nov 14 '12 at 17:31

2 Answers2

26

I believe you are looking for the VirtualizingPanel.ScrollUnit attached property which you can set on the DataGrid.

If you set its value to Pixel instead of the default Item, it should do what you want.

Peter Hansen
  • 8,807
  • 1
  • 36
  • 44
  • I am unfortunately using .Net 4.0, however its not too late for me to upgrade the version of the .Net framework I am using, so I will consider doing that if no other easy solution is found. Thank you. – Rachel Nov 14 '12 at 17:49
2

If you don't want to upgrade to .NET 4.5, you can still set the IsPixelBased property on the underlying VirtualizingStackPanel. However this property is internal in .NET 4.0, so you will have to do that through reflection.

public static class VirtualizingStackPanelBehaviors
{
    public static bool GetIsPixelBasedScrolling(DependencyObject obj)
    {
        return (bool)obj.GetValue(IsPixelBasedScrollingProperty);
    }

    public static void SetIsPixelBasedScrolling(DependencyObject obj, bool value)
    {
        obj.SetValue(IsPixelBasedScrollingProperty, value);
    }

    // Using a DependencyProperty as the backing store for IsPixelBasedScrolling.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty IsPixelBasedScrollingProperty =
        DependencyProperty.RegisterAttached("IsPixelBasedScrolling", typeof(bool), typeof(VirtualizingStackPanelBehaviors), new UIPropertyMetadata(false, OnIsPixelBasedScrollingChanged));

    private static void OnIsPixelBasedScrollingChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
    {
        var virtualizingStackPanel = o as VirtualizingStackPanel;
        if (virtualizingStackPanel == null)
            throw new InvalidOperationException();

        var isPixelBasedPropertyInfo = typeof(VirtualizingStackPanel).GetProperty("IsPixelBased", BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.NonPublic);
        if (isPixelBasedPropertyInfo == null)
            throw new InvalidOperationException();

        isPixelBasedPropertyInfo.SetValue(virtualizingStackPanel, (bool)(e.NewValue), null);
    }
}

And in your xaml :

<DataGrid>
    <DataGrid.ItemsPanel>
        <ItemsPanelTemplate>
            <VirtualizingStackPanel IsItemsHost="True" local:VirtualizingStackPanelBehaviors.IsPixelBasedScrolling="True" />
        </ItemsPanelTemplate>
    </DataGrid.ItemsPanel>
</DataGrid>
Sisyphe
  • 4,626
  • 1
  • 25
  • 39