C# – How to pin one control below another in Silverlight

csilverlight-2.0

I have a project that uses a DataGrid with a custom template so that I can add a special row to the bottom of the data rows. I would like this special row to be pinned under the last row but not as part of the ScrollViewer, such that it remains pinned under the last row until the bottom of the special row hits the bottom of the data grid, then I would like the rows region to size to the space inbetween and scroll accordingly, with the special row always visible.

So far, I have my special row as part of the ScrollViewer along with the RowsPresenter. Both the presenter and the special row are in auto-sized rows of a Grid within the ScrollViewer, with the ScrollViewer in a star-sized grid row so that the scrollbar will appear when it runs out of space. How do I get from this, where the rows and special row scroll together to where I want to be, where the rows scroll, but the special row is pinned at the bottom and always visible?

Although my example uses a DataGrid, I am sure this can be simplified down to just a scrollable element of varying height, and a control pinned beneath it. So far, I imagine I need a Canvas rather than a Grid to host my ScrollViewer and companion special row, with some logic to adjust heights and positions when the ScrollViewer grows (if I can detect that), but I haven't yet tried this. Is there a better way or is the Canvas approach the best one available?

Best Answer

I was able to solve this by using a Grid with two auto-sized rows; a row for the DataGrid and a row for my pinned row. I then monitor the sizing of the Grid and, upon resizing, look to see if the Grid's ActualHeight is greater than the screen real-estate it is given to occupy. If it is, I change the DataGrid's row to star-sized, which results in the pinned row appearing pinned to the bottom of the parent control and the DataGrid displaying a scrollbar for its rows. I change the row back to auto-sizing when more space is made available.

This would obviously work for any scenario where one row must always be on screen but must also be pinned to the bottom of another.

The pinning code looks something like this:

RowDefinition row = this.mainGrid.RowDefinitions[0];
if (row.Height.GridUnitType == GridUnitType.Auto)
{
    if (this.mainGrid.ActualHeight > this.ActualHeight)
    {
        row.Height = new GridLength(1, GridUnitType.Star);
    }
}
else
{
    if (this.dataGrid.DesiredSize.Height < row.ActualHeight)
    {
        row.Height = GridLength.Auto;
    }
}
Related Topic