Display a Dictionary/KeyValuePair as Grid in WPF

Every time I work with WPF, I constantly think “I hate this shit” and “why is everything so damn hard”, but once I figure it out, I realize how powerful it really is and what I can really do with it.  I remember starting out with WPF and trying to figure out how to bind data to a control took me 2 days of reading several articles and a book.

Anyway, I am currently working on a MongoDb GUI – there are none out there – and I decided to build it in WPF.  I am using the MongoDb driver by samus (link).  Displaying the data returned from a query as a table proved to be a lot harder than I thought.

Remember that the application doesn’t know anything about the domain model, so I can’t use strongly-typed objects.  This is the code to retrieve the list of user objects stored in MongoDb:

This will return the results as ICursor<Document>.  Think of Document as the base type for all objects stored in MongoDb.  The actual collection of documents/objects/users is in results.Documents.  A Document implements the ICollection<KeyValuePair<string,object>>, this means that each document is basically a key/value list of all the object properties.  So a user object will be stored like this:

Key Value
FirstName John
LastName Smith
CreatedOn 2010-07-20T05:39:35.5220000Z
Account { "SubscriptionType": "paid", "CreatedOn": "2010-07-20T05:39:35.5220000Z", "ModifiedOn": "2010-07-20T05:39:35.5220000Z" }

What I want to do is to take this list of documents and display as a grid.  I want it to look something like this:

image

I am no WPF expert but after Googling and reading tons of articles for hours, here is the solution I came up with.  If you know a better one, please let me know.

Here is the XAML for the ListView

</ListView>

The first thing I need to do is define the grid columns:

I use the returned GridView to set the ListView’s Grid

So that takes care of setting up the grid column header.  Now, I need to bind the data.  I want to treat each document as a “Row”, so I create a custom class called CustomRow.  Some of the stuff in there won’t make sense right now, but bear with me.

 

Now, all I have to do is convert each document to CustomRow:

This is the full code used to setup the ListView:

Did you notice the line with the CellTemplateSelector in the CreateGridViewColumns method above?  Well, I wanted my cells to display differently based on whether they are displaying string, date, an embedded object/document or a list of other documents.  So, I created a custom CellTemplateSelector:

Basically, I am returning a different DataTemplate based on the object stored in the cell.  As an example, here is dateCell template defined in the App.xaml file.

This is simply a TextBlock that is bound to the Value property of the CustomRow class (will explain later).  The cellConverter is custom logic that will convert the value of the cell accordingly.  For example, date is converted to short date, an embedded object is simply converted to an “(object)” string as shown above.  Here is my converter (ConvertBack hasn’t been implemented yet):

Explanations

At first, I tried to just bind directly to the list of values in each document.  The problem with that the DataTempalte only display the first item in the collection.  So my grid looks exactly like above but all the cells were set to the value of Id.  That is why I created the CustomRow class and that is why the Value property increments the indexer.  This way everytime I pull out the value for a cell, I increment the indexer so that the next bound cell will get the next value and so on. 

Again, I don’t know if this is the best solution, so if you do know of a better one then please let me know in the comments.  It will help all my readers.  Thanks in advance.