DataBinding an IList as DataSource for the Gtk.TreeView

I did it again. I got arrogant while being excited about how we should make it more easy for language bindings to implement types that adapt the many C-isms we introduced in GObject and Gtk+. And as a result I got myself in the situation of having to prove the concept. Which most of the times isn’t a big problem. Often it’s just some work.

So by telling me that if I’d look at the NodeStore and NodeView implementations in gtk-sharp by Mike Kestner I could implement my ideas, toshok invited me to create such a proof of concept. I looked at it and decided that he was right about the fact that it’s possible. So I decided to do it.

The problem with the ListStore and TreeStore models is that in a correct Model View Controller paradigm, you don’t couple the model and the view. You decouple everything that is related to viewing the model, from the model. You put it in the view. That’s the purpose of the view! When implementing a GtkTreeModelIface you have to implement the typing of the columns in the model. “Columns”, is information about viewing the model. Fortunately for sanity but unfortunately for gtk-sharp this isn’t how it’s typically done in a programming environment like .NET.

In .NET (in this example System.Windows.Forms) you can do for example (say you have a Person object with a Name property):

DataGrid view = New DataGrid (); // Or use the .NET 2.0 DataGridView
DataGridTableStyle table = new DataGridTableStyle();
table.MappingName = "Person[]";
DataGridColumn column = new DataGridTextBoxColumn ();
column.MappingName = "Name";
table.DataGridColumns.Add (column);
view.DataGridTableStyles.Add (table);

Person [] persons = factory.GetPersons ();
view.DataSource = persons;

Types like the ComboBox also work like this. If you ask for the combo.SelectedItem, you get a Person instance. For the Gtk.TreeView the story is different: You can’t use a simple IList like the DataTable or collection types immediately. You have to flow them into a ListStore or a TreeStore first. And on top of that, you have to put column information in the model. Many OO designers will tell you that is wrong, others will tell you that now it no longer matters that the model and the view are two things. They are coupled for ever. So why do it in the first place? I’ve seen wrapper API’s that add a AddLine to the view. This is of course totally not the Model View Controller paradigm anymore.For a typical .NET developer, this is at least strange. It makes the model not reusable. So you end up creating user interface types and code that shouldn’t be there. And the Model View Controller paradigm eventually also gets lost. As the model is coupled with the view (it’s not reusable).

So basically: it’s not really how to do it correctly.

What you want is to set a datasource (the list model), and adjust the view so that it understands what to do with the datasource. But don’t require view related changes to the datasource (the list model). So I implemented a GtkTreeModelIface in C# and did it as an adaptor that will convert the C-isms to .NET.

The implementation will basically implement GtkTreeModelIface in C# (which was the hardest part). In stead of keeping my own store (for example by copying the references to an ArrayList), I inject the original IList into the custom tree model implementation and use that as store. Once done I simply use the integer index as TreeIter token and check for the Count property of the IList interface for letting the view know that it can ask for more rows/instances. That way it effectively adapts the IList to a TreeModel.

It’s an ugly ui application but read the code and run it while reading: you’ll understand what it does. You can get a checkout using this SVN repository. It should load in for example MonoDevelop.

So if I translate the above sample to this, you get something like this:

Person [] persons = factory.GetPersons ();
TreeViewAdaptor view = new TreeViewAdaptor ();
view.AppendColumn ("Full name", "Name");
view.DataSource = persons;

So when the GtkTreeView (the C thing) needs data from the model, it will ask the C# GtkTreeModelIface implementation for that data. And that implementation will use your Person instances and your IList instance. Not a copy. As a proof, feel free to put breakpoints at the getters and the setters in the Name, Surname and Number properties of that Person type. Or implement your own IList in stead of using the ArrayList (the demo is using an ArrayList) and follow when it’s asking for items by adding breakpoints in your IList implementation.

Know what? Simply check out the source of the demo. You’ll see.