Search code examples
c#.netwpfdatagridoff-by-one

Visible rows in DataGrid is off by 1 (counted using ContainerFromItem)


I have a DataGrid of variable dimensions dependent upon screen-res. I need to know how many rows are visible to the user. Here's my code:

uint VisibleRows = 0;
var TicketGrid = (DataGrid) MyWindow.FindName("TicketGrid");

foreach(var Item in TicketGrid.Items) {
    var Row = (DataGridRow) TicketGrid.ItemContainerGenerator.ContainerFromItem(Item);
    if(Row != null && Row.IsVisible) {
        VisibleRows++;
    }
}

I'm using the following code to test the vars:

MessageBox.Show(String.Format("{0} of {1} rows visible", VisibleRows, TicketGrid.Items.Count));
  • When there are no rows in the grid, it correctly shows 0 of 0 rows visible:

  • When there is 1 row in the grid, it correctly shows 1 of 1 rows visible:

  • When there are 9 rows in the grid, it correctly shows 9 of 9 rows visible:

  • The next row is "half-visible", so I'll count it showing 10 of 10 rows visible as correct:

  • However the next row to be added is apparently visible, incorrectly showing 11 of 11 rows visible:

  • Rows added after this are correct (bar the stray 1), e.g. 11 of 18 rows visible:


I can't just - 1, because it's only incorrect after a certain number have been added. I can't check > 10, because the dimensions are variable.

How can I fix this?


Solution

  • Here's what finally worked for me:

    uint VisibleRows = 0;
    var TicketGrid = (DataGrid) MyWindow.FindName("TicketGrid");
    
    foreach(var Item in TicketGrid.Items) {
        var Row = (DataGridRow) TicketGrid.ItemContainerGenerator.ContainerFromItem(Item);
    
        if(Row != null) {
            /*
               This is the magic line! We measure the Y position of the Row, relative to
               the TicketGrid, adding the Row's height. If it exceeds the height of the
               TicketGrid, it ain't visible!
            */
    
            if(Row.TransformToVisual(TicketGrid).Transform(new Point(0, 0)).Y + Row.ActualHeight >= TicketGrid.ActualHeight) {
                break;
            }
    
            VisibleRows++;
        }
    }
    

    Upto and including row 9 shows 9 of 9 visible. The "half-visible" row 10 results in 9 of 10 visible. It's actually better for my purposes for this not to count as a visible row, so this'll do for me! :)

    Note: if you're reusing my code without using the break, any invisible rows after the offending row will throw a NullRefException.