7

Greetings,

I've managed to scroll to the selected item using http://www.codeproject.com/Tips/125583/ScrollIntoView-for-a-DataGrid-when-using-MVVM.aspx but this only scrolls untill it gets to the selected item.

I want the selected item to show at the TOP of the datagrid, currently it shows at the bottom of the datagrid.

Is there any way to accomplish this ?

Theun Arbeider
  • 5,259
  • 11
  • 45
  • 68

3 Answers3

7

It seems like there's two scenarios here. One is when you manually select an item in the DataGrid and the other one is when the source property for SelectedItem changes in your viewmodel. The behavior in the link you provided will be triggered for both.

The way ScrollIntoView works is that it will scroll upwards if the newly selected item is above the previously selected item (leaving it on top) and scroll downwards if the newly selected item is below (leaving it on the bottom) and no scroll at all if the selected item is already visible to the user. So you won't always get the selected item on the bottom of the DataGrid

If you want the SelectedItem to always be displayed on the top of the grid (if possible) you can do that by scrolling to the bottom, before doing grid.ScrollIntoView(grid.SelectedItem, null);. This has the side-effect that the SelectedItem will always be displayed on the top, even if it was selected by Mouse, Keyboard etc.

To be able to scroll the DataGrid programmatically, you need to create a ScrollableDataGrid that derives from DataGrid

public class ScrollableDataGrid : DataGrid
{
    private IScrollProvider m_scrollProvider;
    public ScrollableDataGrid()
    {
        m_scrollProvider = OnCreateAutomationPeer() as IScrollProvider;
    }
    public void ScrollToBottom()
    {
        while (m_scrollProvider.VerticalScrollPercent < 100)
        {
            m_scrollProvider.Scroll(ScrollAmount.NoAmount, ScrollAmount.LargeIncrement);
        }
    }
}

Then you can modify the behavior slightly to get the "scrolled to top" effect

void AssociatedObject_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    if (sender is ScrollableDataGrid)
    {
        ScrollableDataGrid grid = (sender as ScrollableDataGrid);

        if (grid.SelectedItem != null)
        {
            grid.Dispatcher.BeginInvoke(delegate
            {
                grid.ScrollToBottom();
                grid.UpdateLayout();
                grid.ScrollIntoView(grid.SelectedItem, null);
            });
        }
    }
}
Fredrik Hedblad
  • 83,499
  • 23
  • 264
  • 266
  • This is a great answer and I think my only option but I'm still hoping it is possible without creating a new control.. since our datagrid already is a custom control for some other changes. – Theun Arbeider Jan 27 '11 at 08:07
  • This worked out well except when the vertical scrollbar was not showing. In this case the m_scrollProvider.VerticalScrollPercent was -1.0 causing an infinite loop in the ScrollToBottom method. Adding " m_scrollProvider.VerticalScrollPercent >= 0" to the while loop cured this. Anyway, this was a very helpful solution -- thanks! – Jersey Dude Sep 14 '11 at 22:45
  • One more odd case: I am not sure how to generate it, but I came across a case where VerticalScrollPercent reached 99.9999986 and would never go any higher. I changed the while to read: while (Math.Round(m_scrollProvider.VerticalScrollPercent, 0) < 100 && m_scrollProvider.VerticalScrollPercent >= 0 && --maxIterations > 0). I added to maxIterations check to prevent any other cases from hanging the app. – Jersey Dude Sep 14 '11 at 23:18
0
void AssociatedObject_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        if (sender is DataGrid)
        {
            DataGrid grid = (sender as DataGrid);
            if (grid.SelectedItems.Count > 1) // <-------- Add row
                return; //<-------- Add row
            if (grid.SelectedItem != null)
            {
                grid.Dispatcher.BeginInvoke(new Action (delegate()
                {                        
                    grid.UpdateLayout();
                    grid.ScrollIntoView(grid.SelectedItem, null);

                }));
            }
        }
    }
Evgen
  • 1
  • 1
  • 1
0

For a data grid, where you know the record number:

in your module declarations ....

Dim dv As DataView

Dim cm As CurrencyManager

in your code where you define your datagrid ....

DataGrid1.DataSource = ds.Tables("JoinedTables").DefaultView

dv = New DataView(ds.Tables("JoinedTables"))

cm = CType(Me.BindingContext(dv), CurrencyManager)

when you want to set your record number to be the top most

Public Sub SetPosition(ByVal recordnumber As Integer)

     On Error Resume Next

     'set position to bottom of grid 
     DataGrid1.CurrentRowIndex = ds.Tables(0).Rows.Count
     DataGrid1.Update()

     'set position at record number
     DataGrid1.CurrentRowIndex = recordnumber
     cm.Position = recordnumber

End sub
slavoo
  • 5,798
  • 64
  • 37
  • 39
Rob
  • 3,488
  • 3
  • 32
  • 27