Using a GTK# TreeView Widget in C# – Very Simply

I’ve been experimenting with using GTK# and C# to build GUI applications which will run on Linux, Windows and Mac. Monodevelop has a graphical form designer for Gtk# front ends, but it’s not as simple as building WinForms applications under Visual Studio. An example is the TreeView widget – it’s designed from an MVC perspective, with a ListStore or TreeStore object containing the model (or data), various objects collectively representing the view (columns, cells, cell renderers etc) and the controller functions allow you to sort and filter the data. All the building needs to be done in code rather than at design time. This is a lot to wrap your head around if you simply want to display some data in a tabular format, as I did, when I wanted to display the parsed contents of a log file!

To make this easier for me in future, I’ve written a small class which hides just about everything. Read on…

Slap a TreeView widget on your form, then in the form’s constructor, ¬†you can simply do this:

SimpleTreeView tree=new SimpleTreeView(controlOnForm);
tree.AddColumn("Col1"); tree.AddColumn("Col2"); // adding columns
tree.AddData(string, ...); // add data, one string per column
tree.Finish(); // finalise the process with this

All cells are string data only, there’s no sorting or anything. Just a grid of data. The code for the class is below. Yes, it’s rough around the edges, yes, it could do with some tidying, and I’m sure much improvement, but that’s left as an exercise for the reader :) …

using System;
using System.Collections.Generic;

namespace tim{
  // MCD Marengo 2012
  // This class assumes that you have a GTK tree view on a form - just
  // instantiate this class with that control and this does all the
  // hard stuff. Note all columns are treated as text.
  // To use:
  // SimpleTreeView tree=new SimpleTreeView(controlOnForm);
  // tree.AddColumn("Col1"); tree.AddColumn("Col2"); // adding columns
  // tree.AddData(string, ...); // add data, one string per column
  // tree.Finish(); // finalise the process with this
  public class SimpleTreeView{
    List<Gtk.TreeViewColumn> _cols;
    List<Gtk.CellRendererText> _cells;
    Gtk.TreeView _tree;
    Gtk.ListStore _list=null;
    bool _colsAdded=false;

    public SimpleTreeView(Gtk.TreeView tree){
      _tree=tree;
      _cols=new List<Gtk.TreeViewColumn>();
      _cells=new List<Gtk.CellRendererText>();
    }

    public void AddColumn(string colName){
      if(_colsAdded == true){
        // can't add columns after you've started adding data.
        throw new Exception("Cannot add columns after data has been added");
      }
      _cols.Add(new Gtk.TreeViewColumn()); 
      _cols[_cols.Count - 1].Title = colName;
    }

    public void AddData(params string[] fieldData){
      // it is assumed when this is called that the user has finished adding columns.
      _colsAdded = true;
      if(_cols.Count != fieldData.Length){
        throw new Exception("Mismatch on number of columns defined and items of data passed");
      }

      if(_list == null){
        Type[] t = new Type[_cols.Count];
        for(int n=0; n<_cols.Count; ++n){
          t[n] = typeof(string);
        }
        _list = new Gtk.ListStore(t);
      }
      _list.AppendValues(fieldData);
    }

    public void Finish(){
      for(int n=0; n<_cols.Count; ++n){
        _cells.Add(new Gtk.CellRendererText());
        _cols[n].PackStart(_cells[_cells.Count-1], true);
      }
      for(int n=0; n<_cols.Count; ++n){
        _cols[n].AddAttribute(_cells[n], "text", n);
      }
      for(int n=0; n<_cols.Count; ++n){
        _tree.AppendColumn(_cols[n]);
      }
      _tree.Model=_list;
    }

    public List<string> GetSelected(){
      // returns the cols of the current line
      Gtk.TreeSelection selection =_tree.Selection;
      Gtk.TreeModel model;
      Gtk.TreeIter iter;
      List<string> rc=new List<string>();
      if(selection.GetSelected(out model, out iter)){
        for(int n=0; n<_cols.Count; n++){
          rc.Add(model.GetValue(iter,n).ToString());
        }
      }
      return rc;
    }
  }
}

Comments 11