How to use GtkTreeView correctly
Asked Answered
Y

1

5

I am using a TreeView with a ListStore as model. When the user clicks on a row I want to take some action but not using the values in the cells, but using the data I created the row from...

Currently I have the TreeView, the TreeModel (ListStore) and my own data (which I ironically call model)..

So the Questions are:

Is it "right" to have a model - an object representation of the data I want to display and fill a ListStore with that data to display in a TreeView, or would it be better to implement an own version of TreeModel (wrapping my data-model) to display the data?

And also:

If someone double-clicks in a row I can get the RowActivated event (using C#/Gtk#) which provides a Path to the activated row. With that I can get a TreeIter and using that I can get the value of a cell. But what is the best practice to find the data object from which the row was constructed in the first place?\ (Somehow this question got me to the first one - by thinking would getting the data object more easy if I tried to implement my own TreeModel...)

Yesteryear answered 22/12, 2010 at 16:21 Comment(0)
K
7

It's quite awkward/difficult to implement TreeModel, so most people simply synch the data from their "real" model into a TreeStore or ListStore.

The columns in the store do not have to match the columns in the view in any way. For example, you can have a column that contains your real managed data objects.

When you add a cellrenderer to a TreeView (visual) column, you can add mappings between its properties and the columns of the store. For example, you could map one store column to the font of a text cellrenderer, and another store column to the text property of the same cellrenderer. Each time the cellrenderer is used to render a particular cell, the mappings will be used to retrieve the values from the store and apply them to the properties of the renderer before it renders.

Here's an example of a mapping:

treeView.AppendColumn ("Title", renderer, "text", 0, "editable", 4);

This maps store column 0 to the renderer's text GTK property and maps store column 4 to the editable property. For GTK property names you can check the GTK docs. Note that the example above uses a convenience method that adds a column, adds a renderer to it and add an arbitrary number of mapping via params. To add mappings directly to a column, for example a column with multiple renderers, pack the renderers into the column then use TreeViewColumn.AddAttribute or TreeViewColumn.SetAttributes.

You can also set up a custom data function that will be used instead of mappings. This allows you to set the properties of the renderer directly, given a TreeIter and the store - so, if all the data you want to display is trivially derived from your real data objects, you could even have your store only contain a single column of these objects, and use data funcs for all the view columns.

Here's an example of a data func that does exactly what the mapping example above does:

treeColumn.SetCellDataFunc (renderer, delegate (TreeViewColumn col,
    CellRenderer cell, TreeModel model, TreeIter iter)
{
    var textCell = (CellRendererText) cell;
    textCell.Text = (string) model.GetValue (iter, 0);
    textCell.Editable = (bool) model.GetValue (iter, 4);
});

Obviously data functions are much more powerful because they enable you not only to use properties of more complex GTK objects, but also to implement more complex display logic - for example, lazily processing derived values only when the cell is actually rendered.

Kirkland answered 22/12, 2010 at 18:56 Comment(1)
thanks for the info, I have now modified the ListStore to hold exactly one of my data-objects and set the CellRenderer via SetCellDataFunc. Could you elaborate on using a "mapping" between properties of the store and properties of the cellRenderer?Yesteryear

© 2022 - 2024 — McMap. All rights reserved.