WPF DataGrid: Dissecting the Visual Layout

I’m going to be dissecting and discussing the DataGrid visuals and how they are all assembled together to form the overall DataGrid. Note: This is really more of a post for people who want to understand how the DataGrid works internally. I’m not going to be going over how to use some of the APIs in this post.

If you haven’t already, get the binaries and source for it here.  For more related material take a look at these posts:

DataGrid Intro 

Stock and Template Columns

Working with DataGridComboBoxColumn (Part1)

Working with DataGridComboBoxColumn (Part2)

Understanding the visual tree isn’t the most important thing to know about basic DataGrid usage, but it is very helpful for such things like customizing its appearance through the different styling templates available, replacing the visual tree altogether, accessing visual properties, understanding routing events, or when you want to learn how the data maps to the visual tree. Here is the big picture view of the DataGrid:

DataGridVisual

You can view the equivalent in Generic.xaml in the source for DataGrid. The DataGrid, like a generic spreadsheet, is made of rows and cells. The rows are generated from the DataGrid’s GetContainerForItemOverride. That should sound familiar as DataGrid ultimately derives from ItemsControl. If you need a brush up on that, please check out Dr. WPF’s awesome series on ItemsControl. The cells are generated from DataGridCellsPresenter’s GetContainerForItemOverride. The important containers are DataGridRowsPresenter and DataGridCellsPanel. DataGridRowsPresenter derives from VSP and is a really small class that does some clean up on the ItemsHost and some scrolling work. If you plan to replace this container, be sure to handle scrolling. The DataGridCellsPanel on the other hand is a very specific implementation to the default DataGrid which handles sizing of each DataGridCell. Replacing this container will need a good understanding of the DataGrid internals which I will not go into here.

In my previous post I did a really brief intro of the DataGrid and just got it up and running. Well, now I want to break down how it actually works. Whether columns are auto-generated or built manually, the DataGrid will use those columns to map the data source to each cell. Notice that the list of DataGridColumns is not in the diagram as it is not really a visual. DataGridColumn is used as the glue between the data and the DataGridCells. So given this column implementation as an example,

<dg:DataGrid AutoGenerateColumns="False">

  <dg:DataGrid.Columns>

    <dg:DataGridTextColumn Binding="{Binding Path=FirstName}" />

    <dg:DataGridTextColumn Binding="{Binding Path=LastName}" />

    <dg:DataGridCheckBoxColumn Binding="{Binding Path=LikesCake}"/>

    <dg:DataGridHyperlinkColumn Binding="{Binding Path=Homepage}" />

  </dg:DataGrid.Columns>

</dg:DataGrid>

here is the sequence that follows in creating the DataGrid when the ItemsSource is set (assuming all the Path values are properties on the data source),

1. When ItemsSource is set on the DataGrid, PrepareContainerForItemOverride is called on each item of the data source collection which prepares a DataGridRow for each.

2. When a DataGridRow is prepared, it passes the item to its DataGridCellsPresenter which internally creates a copied collection of that item and sets its ItemsSource to that collection.

3. When DataGridCellsPresenter.ItemsSource is set, PrepareContainerForItemOverride is called on each copied item which prepares a DataGridCell for each.

4. When a DataGridCell is prepared (which is a ContentControl), it gets its corresponding column from the DataGrid and asks that column to generate the visual tree for DataGridCell’s Content property.

It is in the generation of the visual tree where the binding is hooked up from the data source to the UI. Recall in the column implementation, the Binding property was set to a property on the data source. In the visual tree generation it takes that Binding and applies it to the generated UIElement (which in step 4 is set to DataGridCell’s Content property).  That is basically it. You should also know that different column types will generate different UIElements. DataGridTextColumn will generate a textbox; DataGridCheckBoxColumn will generate a checkbox, etc. So hopefully some of the magic has been revealed on how the DataGrid populated itself.

In addition to understanding the mapping of the data to the UI, you should have a good grasp of the visuals involved from looking at the diagram. This helps when you want to style the DataGrid. Convenience properties for styling have also been added the DataGrid class itself so you can look there for the available styles as well, but let’s stop here for now as I will get more into styling and templating in an upcoming post.