L'élément graphique arborescence
L'élément graphique Gtk::TreeView peut incorporer des listes ou des arborescences de données, disposées en colonnes.

Le modèle
Chaque objet Gtk::TreeView possède un objet Gtk::TreeModel associé ; il contient les données à afficher par l'objet TreeView. Tout Gtk::TreeModel peut être utilisé par plus d'un Gtk::TreeView. Par exemple, il est possible d'afficher et modifier simultanément sous deux formes différentes les mêmes données sous-jacentes ; ou bien encore, les deux vues peuvent afficher des colonnes différentes à partir du même modèle de données, tout comme deux requêtes SQL (ou « vues ») sont susceptibles d'afficher des champs différents à partir d'une même base de données.
Même si vous pouvez théoriquement implémenter votre propre modèle, vous utiliserez normalement, soit la classe modèle ListStore, soit la classe modèle TreeStore.
Reference

Modèle listStore, pour des colonnes
Un objet ListStore contient de simples colonnes de données et chaque colonne n'a pas d'enfant.

TreeView - ListStore
Reference

Modèle treeStore, pour une hiérarchie
L'objet TreeStore contient des colonnes de données et chaque colonne peut avoir des colonnes enfants.

TreeView - TreeStore
Reference

Modèle colonnes
La classe TreeModelColumnRecord s'utilise pour enregistrer les colonnes et leurs types de données. Vous ajoutez des exemplaires de TreeModelColumn au ColumnRecord, puis utilisez ces TreeModelColumns pour obtenir ou placer les données dans les colonnes du modèle. Vous trouverez certainement commode de dériver un nouveau TreeModelColumnRecord incorporant vos exemplaires de TreeModelColumn en tant que données membres.

class ModelColumns : public Gtk::TreeModelColumnRecord
{
public:

  ModelColumns()
    { add(m_col_text); add(m_col_number); }

  Gtk::TreeModelColumn<Glib::ustring> m_col_text;
  Gtk::TreeModelColumn<int> m_col_number;
};

ModelColumns m_Columns;

Vous précisez le ColumnRecord à la création du modèle, ainsi :

Glib::RefPtr<Gtk::ListStore> refListStore =
    Gtk::ListStore::create(m_Columns);

Notez que les exemplaires de colonnes (tels que m_Columns ici) ne sont habituellement pas qualifiés « static », étant donné qu'ils doivent être instanciés après que glibmm l'ait été lui-même.

Ajout de lignes
Ajoutez des lignes au modèle avec les fonctions membres append(), prepend() ou insert().

Gtk::TreeModel::iterator iter = m_refListStore->append();

Vous pouvez déréférencer l'itérateur pour obtenir la ligne :

Gtk::TreeModel::Row row = *iter;

Ajout de colonnes enfants
Les modèles Gtk::TreeStore peuvent avoir des éléments enfants. Pour les ajouter, servez-vous des fonctions membres append(), prepend() ou insert(), ainsi :

Gtk::TreeModel::iterator iter_child =
    m_refTreeStore->append(row.children());

Définition des valeurs
Vous pouvez utiliser la surdéfinition de operator[] pour définir les données d'une colonne désignée dans la ligne en indiquant le TreeModelColumn utilisé pour créer le modèle.

row[m_Columns.m_col_text] = "textedonné";

Obtention des valeurs
Vous pouvez utiliser la surdéfinition de operator[] pour obtenir les données d'une colonne particulière dans une ligne, en précisant le TreeModelColumn qui a été utilisé pour créer le modèle.

Glib::ustring strText = row[m_Columns.m_col_text];
int number = row[m_Columns.m_col_number];

Le compilateur se plaint si vous utilisez un type inapproprié et génère, par exemple, l'erreur de compilation suivante :

//compiler error - no conversion from ustring to int.
int number = row[m_Columns.m_col_text];

Colonnes « cachées »
Vous pouvez associer des données supplémentaires à chaque ligne. Pour ce faire, ajoutez-les en tant que colonne du modèle, mais sans les placer dans la vue.

La vue
La vue est l'élément graphique (Gtk::TreeView) qui affiche concrètement les données du modèle (Gtk::TreeModel) et qui autorise l'utilisateur à interagir. Elle peut afficher, de diverses manières, toutes les colonnes du modèle ou seulement quelques unes.
Reference

Utilisation d'un modèle
Vous pouvez définir le modèle Gtk::TreeModel à la construction de l'objet vue Gtk::TreeView ou bien vous pouvez utiliser la fonction membre set_model(), comme suit :

m_TreeView.set_model(m_refListStore);

Ajout de colonnes à la vue
Vous pouvez utiliser la fonction membre append_column() pour demander à la vue d'afficher certaines colonnes du modèle, dans un certain ordre, avec un certain intitulé.

m_TreeView.append_column("Messages", m_Columns.m_col_text);

En utilisant cette simple surdéfinition de append_column(), l'objet TreeView affiche la donnée du modèle avec un CellRenderer approprié. Par exemple, les chaînes littérales et les nombres sont affichés dans un simple contrôle Gtk::Entry et les booléens dans un Gtk::CheckButton. Cela correspond aux besoins habituels. Pour des colonnes relatives à d'autres types, vous pouvez, soit connecter une fonction de rappel qui convertit avec TreeViewColumn::set_cell_data_func() le type en chaîne littérale le représentant, soit créer par dérivation un CellRenderer personnalisé. Notez que le type (unsigned) short n'est pas pris en charge par défaut — à la place, vous pouvez utiliser le type (unsigned) int ou (unsigned) long.

Plus d'une colonne de modèle par colonne de vue
Pour afficher à l'écran plus d'une colonne du modèle dans une colonne de vue, vous aurez besoin de créer un élément graphique TreeView::Column manuellement et d'utiliser la fonction membre pack_start() pour y ajouter les colonnes du modèle.
Then use append_column() to add the view Column to the View. Notice that Gtk::TreeView::append_column() is overridden to accept either a prebuilt Gtk::TreeView::Column widget, or just the TreeModelColumn from which it generates an appropriate Gtk::TreeView::Column widget.
Here is some example code from gtkmm/demos/gtk-demo/example_icontheme.cc, which has a pixbuf icon and a text name in the same column:

Gtk::TreeView::Column* pColumn =
  Gtk::manage(new Gtk::TreeView::Column("Icon Name"));

// m_columns.icon and m_columns.iconname are columns in the model.
// pColumn is the column in the TreeView:
pColumn->pack_start(m_columns.icon, /* expand= */ false);
pColumn->pack_start(m_columns.iconname);

m_TreeView.append_column(*pColumn);

Détails pour la définition du CellRenderer
The default CellRenderers and their default behaviour will normally suffice, but you might occasionally need finer control. For instance, this example code from gtkmm/demos/gtk-demo/example_treeview_treestore.cc, appends a Gtk::CellRenderer widget and instructs it to render the data from various model columns through various aspects of its appearance.

int cols_count = m_TreeView.append_column_editable("Alex", m_columns.alex);
Gtk::TreeViewColumn* pColumn = m_TreeView.get_column(cols_count-1);
if(pColumn)
{
  Gtk::CellRendererToggle* pRenderer =
    static_cast<Gtk::CellRendererToggle*>(pColumn->get_first_cell());
  pColumn->add_attribute(pRenderer->property_visible(), m_columns.visible);
  pColumn->add_attribute(pRenderer->property_activatable(), m_columns.world);

Vous pouvez aussi connecter les signaux du CellRenderer pour détecter des actions utilisateur. Par exemple :

Gtk::CellRendererToggle* pRenderer =
    Gtk::manage( new Gtk::CellRendererToggle() );
pRenderer->signal_toggled().connect(
    sigc::bind( sigc::mem_fun(*this,
        &Example_TreeView_TreeStore::on_cell_toggled), m_columns.dave)
);

Cellules modifiables

Cellules modifiables à enregistrement automatique.
Cells in a TreeView can be edited in-place by the user. To allow this, use the Gtk::TreeView insert_column_editable() and append_column_editable() methods instead of insert_column() and append_column(). When these cells are edited the new values will be stored immediately in the Model. Note that these methods are templates which can only be instantiated for simple column types such as Glib::ustring, int, and long.

Implémentation d'une logique personnalisée pour les cellules modifiables.
Toutefois, vous pouvez vouloir que les nouvelles valeurs ne soient pas immédiatement enregistrées. Vous voulez, par exemple, limiter la saisie à certains caractères ou plages de valeurs.
To achieve this, you should use the normal Gtk::TreeView insert_column() and append_column() methods, then use get_column_cell_renderer() to get the Gtk::CellRenderer used by that column.
Ensuite vous ferez un forçage du type de ce pointeur Gtk::CellRenderer* vers un CellRenderer donné, conforme à vos attentes. Vous utilisez ainsi une API particulière.
Par exemple, pour un CellRendererText, vous définissez la propriété editable égale à true, comme ceci :

cell.property_editable() = true;

Pour un « CellRendererToggle », vous paramétrez la propriété activatable à la place.
Vous pouvez alors connecter le signal « edited » approprié, par exemple, connecter à Gtk::CellRendererText::signal_edited() ou Gtk::CellRendererToggle::signal_toggled(). Si la colonne comprend plus d'un CellRenderer, vous aurez besoin d'utiliser Gtk::TreeView::get_column(), puis d'appeler get_cell_renderers() sur cette colonne de la vue.
Dans le gestionnaire de signal, vous devriez examiner la nouvelle valeur et l'enregistrer dans le modèle si vous la jugez appropriée pour l'application.

Itération parmi les colonnes du modèle
Gtk::TreeModel fournit un conteneur dans le style de la bibliothèque standard C++ pour ses enfants par l'intermédiaire de la fonction membre children(). Vous pouvez utiliser les fonctions membres habituelles begin() et end() pour incrémenter un itérateur, comme ceci :

typedef Gtk::TreeModel::Children type_children; // minimise la longueur du code.
type_children children = refModel->children();
for(type_children::iterator iter = children.begin();
    iter != children.end(); ++iter)
{
  Gtk::TreeModel::Row row = *iter;
  // Faire quelque chose avec la ligne - voir plus haut pour set/get.
}

Ligne enfant
When using a Gtk::TreeStore, the rows can have child rows, which can have their own children in turn. Use Gtk::TreeModel::Row::children() to get the container of child Rows:

Gtk::TreeModel::Children children = row.children();

La sélection
Pour retrouver quelles sont les lignes sélectionnées par l'utilisateur, obtenez l'objet Gtk::TreeView::Selection du TreeView ainsi :

Glib::RefPtr<Gtk::TreeSelection> refTreeSelection =
    m_TreeView.get_selection();

Sélection simple ou multiple
By default, only single rows can be selected, but you can allow multiple selection by setting the mode, like so:

refTreeSelection->set_mode(Gtk::SELECTION_MULTIPLE);

Les lignes sélectionnées
Pour une simple sélection, appelez simplement la fonction membre get_selected(), comme suit :

TreeModel::iterator iter = refTreeSelection->get_selected();
if(iter) // si quelque chose est sélectionné
{
  TreeModel::Row row = *iter;
  // Faire quelque chose avec la ligne.
}

Pour une sélection multiple, vous aurez besoin de définir une fonction de rappel et de la fournir en paramètre à selected_foreach(), selected_foreach_path() ou selected_foreach_iter(), ainsi :

refTreeSelection->selected_foreach_iter(
    sigc::mem_fun(*this, &TheClass::selected_row_callback) );

void TheClass::selected_row_callback(
    const Gtk::TreeModel::iterator& iter)
{
  TreeModel::Row row = *iter;
  // Faire quelque chose avec la ligne.
}

Le signal « changed »
Pour répondre à l'utilisateur qui clique sur une ligne ou une plage de lignes, connectez le signal ainsi :

refTreeSelection->signal_changed().connect(
    sigc::mem_fun(*this, &Example_IconTheme::on_selection_changed)
);

Empêcher la sélection de lignes
Il est possible que l'utilisateur n'ait pas le droit de sélectionner tous les éléments d'une liste ou d'une arborescence. Ainsi, dans le programme gtk-demo, vous pouvez choisir un exemple pour voir le code source, mais choisir une catégorie d'exemples n'a pas de sens.
Pour contrôler quelles lignes peuvent être sélectionnées, utilisez la fonction membre set_select_function() et passez lui en paramètre un signal de rappel sigc::slot. Par exemple :

m_refTreeSelection->set_select_function( sigc::mem_fun(*this,
    &DemoWindow::select_function) );

puis

bool DemoWindow::select_function(
    const Glib::RefPtr<Gtk::TreeModel>& model,
    const Gtk::TreeModel::Path& path, bool)
{
  const Gtk::TreeModel::iterator iter = model->get_iter(path);
  // n'autoriser que la sélection des nœuds d'extrémité
  return iter->children().empty(); 
}

Modification de la sélection
Pour modifier la sélection, précisez un Gtk::TreeModel::iterator ou Gtk::TreeModel::Row, ainsi :

Gtk::TreeModel::Row row = m_refModel->children()[5]; // 5ième ligne.
if(row)
  refTreeSelection->select(row);

ou

Gtk::TreeModel::iterator iter = m_refModel->children().begin()
if(iter)
  refTreeSelection->select(iter);

Tris
The standard tree models (TreeStore and ListStore) derive from TreeSortable, so they offer sorting functionality. For instance, call set_sort_column(), to sort the model by the specified column. Or supply a callback function to set_sort_func() to implement a more complicated sorting algorithm.
TreeSortable Reference

Tri en cliquant sur l'en-tête de colonne
So that a user can click on a TreeView's column header to sort the TreeView's contents, call Gtk::TreeView::Column::set_sort_column(), supplying the model column on which model should be sorted when the header is clicked. For instance:

Gtk::TreeView::Column* pColumn = treeview.get_column(0);
if(pColumn)
  pColumn->set_sort_column(m_columns.m_col_id);

Vues d'un même modèle triées de manière indépendante
Nous avons déjà vu que la classe TreeView vous permet d'afficher le même TreeModel dans deux éléments graphiques TreeView distincts. Si vous avez besoin qu'un des TreeView trie le modèle de manière différente de l'autre, vous devez utiliser un objet TreeModelSort au lieu d'utiliser uniquement Gtk::TreeViewModel::set_sort_column(), par exemple. TreeModelSort est un modèle contenant une version triée d'un autre modèle. Par exemple, vous pouvez ajouter une autre version triée d'un modèle à un objet TreeView ainsi :

Glib::RefPtr<Gtk::TreeModelSort> sorted_model =
    Gtk::TreeModelSort::create(model);
sorted_model->set_sort_column(columns.m_col_name, Gtk::SORT_ASCENDING);
treeview.set_model(sorted_model);

Notez, toutefois, que la vue arborescente fournit des itérateurs sur le modèle trié. Vous devez les convertir en itérateurs du modèle enfant sous-jacent pour effectuer des opérations sur ce modèle. Par exemple :

void ExampleWindow::on_button_delete()
{
  Glib::RefPtr<Gtk::TreeSelection> refTreeSelection =
      m_treeview.get_selection();
  if(refTreeSelection)
  {
    Gtk::TreeModel::iterator sorted_iter =
        m_refTreeSelection->get_selected();
    if(sorted_iter)
    {
      Gtk::TreeModel::iterator iter =
          m_refModelSort->convert_iter_to_child_iter(sorted_iter);
      m_refModel->erase(iter);
    }
  }
}

TreeModelSort Reference

Glisser-déposer
Gtk::TreeView already implements simple drag-and-drop when used with the Gtk::ListStore or Gtk::TreeStore models. If necessary, it also allows you to implement more complex behaviour when items are dragged and dropped, using the normal Drag and Drop API.

Réarrangement de lignes
Si vous faites appel à la fonction membre Gtk::TreeView::set_reorderable(), les éléments d'un objet TreeView peuvent être déplacés au sein même de la vue arborescente. Ceci est montré dans l'exemple TreeStore.
However, this does not allow you any control of which items can be dragged, and where they can be dropped. If you need that extra control then you might create a derived Gtk::TreeModel from Gtk::TreeStore or Gtk::ListStore and override the Gtk::TreeDragSource::row_draggable() and Gdk::TreeDragDest::row_drop_possible() virtual methods. You can examine the Gtk::TreeModel::Paths provided and allow or disallow dragging or dropping by returning true or false.
Cette construction est montrée dans l'exemple du glisser-déposer.

Menu contextuel
Nombre de programmeurs ont besoin d'implémenter des menus contextuels s'ouvrant d'un clic droit de souris sur des objets TreeView. Pour vous faire gagner du temps, nous allons expliquer ici comment faire. À un ou deux points près, cela ressemble tout à fait à un menu contextuel normal tel que décrit dans le chapitre Menus.

Gestion du signal button_press_event
To detect a click of the right mouse button, you need to handle the button_press_event signal, and check exactly which button was pressed. Because the TreeView normally handles this signal completely, you need to either override the default signal handler in a derived TreeView class, or use connect_notify() instead of connect(). You probably also want to call the default handler before doing anything else, so that the right-click will cause the row to be selected first.
This is demonstrated in the Popup Context Menu example.

Exemples

ListStore
Voici un exemple d'élément graphique Gtk::TreeView avec un modèle Gtk::ListStore.

TreeView - ListStore
Source Code
File: examplewindow.h (For use with gtkmm 3, not gtkmm 2)

#ifndef GTKMM_EXAMPLEWINDOW_H
#define GTKMM_EXAMPLEWINDOW_H

#include <gtkmm.h>

class ExampleWindow : public Gtk::Window
{
public:
  ExampleWindow();
  virtual ~ExampleWindow();

protected:
  //Signal handlers:
  void on_button_quit();

  //Tree model columns:
  class ModelColumns : public Gtk::TreeModel::ColumnRecord
  {
  public:

    ModelColumns()
    { add(m_col_id); add(m_col_name); add(m_col_number); add(m_col_percentage);}

    Gtk::TreeModelColumn<unsigned int> m_col_id;
    Gtk::TreeModelColumn<Glib::ustring> m_col_name;
    Gtk::TreeModelColumn<short> m_col_number;
    Gtk::TreeModelColumn<int> m_col_percentage;
  };

  ModelColumns m_Columns;

  //Child widgets:
  Gtk::Box m_VBox;

  Gtk::ScrolledWindow m_ScrolledWindow;
  Gtk::TreeView m_TreeView;
  Glib::RefPtr<Gtk::ListStore> m_refTreeModel;

  Gtk::ButtonBox m_ButtonBox;
  Gtk::Button m_Button_Quit;
};

#endif //GTKMM_EXAMPLEWINDOW_H

File: examplewindow.cc (For use with gtkmm 3, not gtkmm 2)

#include <iostream>
#include "examplewindow.h"

ExampleWindow::ExampleWindow()
: m_VBox(Gtk::ORIENTATION_VERTICAL),
  m_Button_Quit("Quit")
{
  set_title("Gtk::TreeView (ListStore) example");
  set_border_width(5);
  set_default_size(400, 200);

  add(m_VBox);

  //Add the TreeView, inside a ScrolledWindow, with the button underneath:
  m_ScrolledWindow.add(m_TreeView);

  //Only show the scrollbars when they are necessary:
  m_ScrolledWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);

  m_VBox.pack_start(m_ScrolledWindow);
  m_VBox.pack_start(m_ButtonBox, Gtk::PACK_SHRINK);

  m_ButtonBox.pack_start(m_Button_Quit, Gtk::PACK_SHRINK);
  m_ButtonBox.set_border_width(5);
  m_ButtonBox.set_layout(Gtk::BUTTONBOX_END);
  m_Button_Quit.signal_clicked().connect( sigc::mem_fun(*this,
              &ExampleWindow::on_button_quit) );

  //Create the Tree model:
  m_refTreeModel = Gtk::ListStore::create(m_Columns);
  m_TreeView.set_model(m_refTreeModel);

  //Fill the TreeView's model
  Gtk::TreeModel::Row row = *(m_refTreeModel->append());
  row[m_Columns.m_col_id] = 1;
  row[m_Columns.m_col_name] = "Billy Bob";
  row[m_Columns.m_col_number] = 10;
  row[m_Columns.m_col_percentage] = 15;

  row = *(m_refTreeModel->append());
  row[m_Columns.m_col_id] = 2;
  row[m_Columns.m_col_name] = "Joey Jojo";
  row[m_Columns.m_col_number] = 20;
  row[m_Columns.m_col_percentage] = 40;

  row = *(m_refTreeModel->append());
  row[m_Columns.m_col_id] = 3;
  row[m_Columns.m_col_name] = "Rob McRoberts";
  row[m_Columns.m_col_number] = 30;
  row[m_Columns.m_col_percentage] = 70;

  //Add the TreeView's view columns:
  //This number will be shown with the default numeric formatting.
  m_TreeView.append_column("ID", m_Columns.m_col_id);
  m_TreeView.append_column("Name", m_Columns.m_col_name);

  m_TreeView.append_column_numeric("Formatted number", m_Columns.m_col_number,
          "%010d" /* 10 digits, using leading zeroes. */);

  //Display a progress bar instead of a decimal number:
  Gtk::CellRendererProgress* cell = Gtk::manage(new Gtk::CellRendererProgress);
  int cols_count = m_TreeView.append_column("Some percentage", *cell);
  Gtk::TreeViewColumn* pColumn = m_TreeView.get_column(cols_count - 1);
  if(pColumn)
  {
    pColumn->add_attribute(cell->property_value(), m_Columns.m_col_percentage);
  }

  //Make all the columns reorderable:
  //This is not necessary, but it's nice to show the feature.
  //You can use TreeView::set_column_drag_function() to more
  //finely control column drag and drop.
  for(guint i = 0; i < 2; i++)
  {
    Gtk::TreeView::Column* pColumn = m_TreeView.get_column(i);
    pColumn->set_reorderable();
  }

  show_all_children();
}

ExampleWindow::~ExampleWindow()
{
}

void ExampleWindow::on_button_quit()
{
  hide();
}

File: main.cc (For use with gtkmm 3, not gtkmm 2)

#include "examplewindow.h"
#include <gtkmm/application.h>

int main(int argc, char *argv[])
{
  Glib::RefPtr<Gtk::Application> app = Gtk::Application::create(argc, argv, "org.gtkmm.example");

  ExampleWindow window;

  //Shows the window and returns when it is closed.
  return app->run(window);
}

TreeStore
C'est exemple est tout à fait semblable au précédent, mais il utilise un modèle Gtk::TreeStore à la place et il ajoute des enfants aux colonnes.

TreeView - TreeStore
Source Code
File: examplewindow.h (For use with gtkmm 3, not gtkmm 2)

#ifndef GTKMM_EXAMPLEWINDOW_H
#define GTKMM_EXAMPLEWINDOW_H

#include <gtkmm.h>

class ExampleWindow : public Gtk::Window
{
public:
  ExampleWindow();
  virtual ~ExampleWindow();

protected:
  //Signal handlers:
  void on_button_quit();
  void on_treeview_row_activated(const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn* column);
   
  //Tree model columns:
  class ModelColumns : public Gtk::TreeModel::ColumnRecord
  {
  public:

    ModelColumns()
    { add(m_col_id); add(m_col_name); }

    Gtk::TreeModelColumn<int> m_col_id;
    Gtk::TreeModelColumn<Glib::ustring> m_col_name;
  };

  ModelColumns m_Columns;

  //Child widgets:
  Gtk::Box m_VBox;

  Gtk::ScrolledWindow m_ScrolledWindow;
  Gtk::TreeView m_TreeView;
  Glib::RefPtr<Gtk::TreeStore> m_refTreeModel;

  Gtk::ButtonBox m_ButtonBox;
  Gtk::Button m_Button_Quit;
};

#endif //GTKMM_EXAMPLEWINDOW_H

File: examplewindow.cc (For use with gtkmm 3, not gtkmm 2)

#include <iostream>
#include "examplewindow.h"

ExampleWindow::ExampleWindow()
: m_VBox(Gtk::ORIENTATION_VERTICAL),
  m_Button_Quit("Quit")
{
  set_title("Gtk::TreeView (TreeStore) example");
  set_border_width(5);
  set_default_size(400, 200);

  add(m_VBox);

  //Add the TreeView, inside a ScrolledWindow, with the button underneath:
  m_ScrolledWindow.add(m_TreeView);

  //Only show the scrollbars when they are necessary:
  m_ScrolledWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);

  m_VBox.pack_start(m_ScrolledWindow);
  m_VBox.pack_start(m_ButtonBox, Gtk::PACK_SHRINK);

  m_ButtonBox.pack_start(m_Button_Quit, Gtk::PACK_SHRINK);
  m_ButtonBox.set_border_width(5);
  m_ButtonBox.set_layout(Gtk::BUTTONBOX_END);
  m_Button_Quit.signal_clicked().connect(sigc::mem_fun(*this,
              &ExampleWindow::on_button_quit) );

  //Create the Tree model:
  m_refTreeModel = Gtk::TreeStore::create(m_Columns);
  m_TreeView.set_model(m_refTreeModel);

  //All the items to be reordered with drag-and-drop:
  m_TreeView.set_reorderable();

  //Fill the TreeView's model
  Gtk::TreeModel::Row row = *(m_refTreeModel->append());
  row[m_Columns.m_col_id] = 1;
  row[m_Columns.m_col_name] = "Billy Bob";

  Gtk::TreeModel::Row childrow = *(m_refTreeModel->append(row.children()));
  childrow[m_Columns.m_col_id] = 11;
  childrow[m_Columns.m_col_name] = "Billy Bob Junior";

  childrow = *(m_refTreeModel->append(row.children()));
  childrow[m_Columns.m_col_id] = 12;
  childrow[m_Columns.m_col_name] = "Sue Bob";

  row = *(m_refTreeModel->append());
  row[m_Columns.m_col_id] = 2;
  row[m_Columns.m_col_name] = "Joey Jojo";


  row = *(m_refTreeModel->append());
  row[m_Columns.m_col_id] = 3;
  row[m_Columns.m_col_name] = "Rob McRoberts";

  childrow = *(m_refTreeModel->append(row.children()));
  childrow[m_Columns.m_col_id] = 31;
  childrow[m_Columns.m_col_name] = "Xavier McRoberts";

  //Add the TreeView's view columns:
  m_TreeView.append_column("ID", m_Columns.m_col_id);
  m_TreeView.append_column("Name", m_Columns.m_col_name);

  //Connect signal:
  m_TreeView.signal_row_activated().connect(sigc::mem_fun(*this,
              &ExampleWindow::on_treeview_row_activated) );

  show_all_children();
}

ExampleWindow::~ExampleWindow()
{
}

void ExampleWindow::on_button_quit()
{
  hide();
}

void ExampleWindow::on_treeview_row_activated(const Gtk::TreeModel::Path& path,
        Gtk::TreeViewColumn* /* column */)
{
  Gtk::TreeModel::iterator iter = m_refTreeModel->get_iter(path);
  if(iter)
  {
    Gtk::TreeModel::Row row = *iter;
    std::cout << "Row activated: ID=" << row[m_Columns.m_col_id] << ", Name="
        << row[m_Columns.m_col_name] << std::endl;
  }
}

File: main.cc (For use with gtkmm 3, not gtkmm 2)

#include "examplewindow.h"
#include <gtkmm/application.h>

int main(int argc, char *argv[])
{
  Glib::RefPtr<Gtk::Application> app = Gtk::Application::create(argc, argv, "org.gtkmm.example");

  ExampleWindow window;

  //Shows the window and returns when it is closed.
  return app->run(window);
}

Cellules modifiables
Cet exemple est identique à l'exemple avec ListStore, mais il utilise TreeView::append_column_editable() à la place de TreeView::append_column().

TreeView - Cellules modifiables
Source Code
File: examplewindow.h (For use with gtkmm 3, not gtkmm 2)

#ifndef GTKMM_EXAMPLEWINDOW_H
#define GTKMM_EXAMPLEWINDOW_H

#include <gtkmm.h>

class ExampleWindow : public Gtk::Window
{
public:
  ExampleWindow();
  virtual ~ExampleWindow();

protected:
  //Signal handlers:
  void on_button_quit();

  void treeviewcolumn_validated_on_cell_data(Gtk::CellRenderer* renderer, const Gtk::TreeModel::iterator& iter);
  void cellrenderer_validated_on_editing_started(Gtk::CellEditable* cell_editable, const Glib::ustring& path);
  void cellrenderer_validated_on_edited(const Glib::ustring& path_string, const Glib::ustring& new_text);

  //Tree model columns:
  class ModelColumns : public Gtk::TreeModel::ColumnRecord
  {
  public:

    ModelColumns()
    { add(m_col_id); add(m_col_name); add(m_col_foo); add(m_col_number); add(m_col_number_validated); }

    Gtk::TreeModelColumn<unsigned int> m_col_id;
    Gtk::TreeModelColumn<Glib::ustring> m_col_name;
    Gtk::TreeModelColumn<bool> m_col_foo;
    Gtk::TreeModelColumn<int> m_col_number;
    Gtk::TreeModelColumn<int> m_col_number_validated;
  };

  ModelColumns m_Columns;

  //Child widgets:
  Gtk::Box m_VBox;

  Gtk::ScrolledWindow m_ScrolledWindow;
  Gtk::TreeView m_TreeView;
  Glib::RefPtr<Gtk::ListStore> m_refTreeModel;

  Gtk::ButtonBox m_ButtonBox;
  Gtk::Button m_Button_Quit;

  //For the validated column:
  //You could also use a CellRendererSpin or a CellRendererProgress:
  Gtk::CellRendererText m_cellrenderer_validated;
  Gtk::TreeView::Column m_treeviewcolumn_validated;
  bool m_validate_retry;
  Glib::ustring m_invalid_text_for_retry;
};

#endif //GTKMM_EXAMPLEWINDOW_H

File: examplewindow.cc (For use with gtkmm 3, not gtkmm 2)

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include "examplewindow.h"

using std::sprintf;
using std::strtol;

ExampleWindow::ExampleWindow()
: m_VBox(Gtk::ORIENTATION_VERTICAL),
  m_Button_Quit("Quit"),
  m_validate_retry(false)
{
  set_title("Gtk::TreeView Editable Cells example");
  set_border_width(5);
  set_default_size(400, 200);


  add(m_VBox);

  //Add the TreeView, inside a ScrolledWindow, with the button underneath:
  m_ScrolledWindow.add(m_TreeView);

  //Only show the scrollbars when they are necessary:
  m_ScrolledWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);

  m_VBox.pack_start(m_ScrolledWindow);
  m_VBox.pack_start(m_ButtonBox, Gtk::PACK_SHRINK);

  m_ButtonBox.pack_start(m_Button_Quit, Gtk::PACK_SHRINK);
  m_ButtonBox.set_border_width(5);
  m_ButtonBox.set_layout(Gtk::BUTTONBOX_END);
  m_Button_Quit.signal_clicked().connect( sigc::mem_fun(*this,
              &ExampleWindow::on_button_quit) );

  //Create the Tree model:
  m_refTreeModel = Gtk::ListStore::create(m_Columns);
  m_TreeView.set_model(m_refTreeModel);

  //Fill the TreeView's model
  Gtk::TreeModel::Row row = *(m_refTreeModel->append());
  row[m_Columns.m_col_id] = 1;
  row[m_Columns.m_col_name] = "Billy Bob";
  row[m_Columns.m_col_foo] = true;
  row[m_Columns.m_col_number] = 10;

  row = *(m_refTreeModel->append());
  row[m_Columns.m_col_id] = 2;
  row[m_Columns.m_col_name] = "Joey Jojo";
  row[m_Columns.m_col_foo] = true;
  row[m_Columns.m_col_number] = 20;

  row = *(m_refTreeModel->append());

  row[m_Columns.m_col_id] = 3;
  row[m_Columns.m_col_name] = "Rob McRoberts";
  row[m_Columns.m_col_foo] = false;
  row[m_Columns.m_col_number] = 30;

  //Add the TreeView's view columns:
  //We use the *_editable convenience methods for most of these,
  //because the default functionality is enough:
  m_TreeView.append_column_editable("ID", m_Columns.m_col_id);
  m_TreeView.append_column_editable("Name", m_Columns.m_col_name);
  m_TreeView.append_column_editable("foo", m_Columns.m_col_foo);
  m_TreeView.append_column_numeric_editable("foo", m_Columns.m_col_number,
          "%010d");


  //For this column, we create the CellRenderer ourselves, and connect our own
  //signal handlers, so that we can validate the data that the user enters, and
  //control how it is displayed.
  m_treeviewcolumn_validated.set_title("validated (<10)");
  m_treeviewcolumn_validated.pack_start(m_cellrenderer_validated);
  m_TreeView.append_column(m_treeviewcolumn_validated);

  //Tell the view column how to render the model values:
  m_treeviewcolumn_validated.set_cell_data_func(m_cellrenderer_validated,
          sigc::mem_fun(*this,
              &ExampleWindow::treeviewcolumn_validated_on_cell_data) );

  //Make the CellRenderer editable, and handle its editing signals:
  m_cellrenderer_validated.property_editable() = true;

  m_cellrenderer_validated.signal_editing_started().connect(
          sigc::mem_fun(*this,
        &ExampleWindow::cellrenderer_validated_on_editing_started) );

  m_cellrenderer_validated.signal_edited().connect( sigc::mem_fun(*this,
              &ExampleWindow::cellrenderer_validated_on_edited) );

  //If this was a CellRendererSpin then you would have to set the adjustment:
  //m_cellrenderer_validated.property_adjustment() = m_spin_adjustment;

  show_all_children();
}

ExampleWindow::~ExampleWindow()
{
}

void ExampleWindow::on_button_quit()
{
  hide();
}

void ExampleWindow::treeviewcolumn_validated_on_cell_data(
        Gtk::CellRenderer* /* renderer */,
        const Gtk::TreeModel::iterator& iter)
{
  //Get the value from the model and show it appropriately in the view:
  if(iter)
  {
    Gtk::TreeModel::Row row = *iter;
    int model_value = row[m_Columns.m_col_number_validated];

    //This is just an example.
    //In this case, it would be easier to use append_column_editable() or
    //append_column_numeric_editable()
    char buffer[32];
    sprintf(buffer, "%d", model_value); 

    Glib::ustring view_text = buffer;
    m_cellrenderer_validated.property_text() = view_text;
  }
}

void ExampleWindow::cellrenderer_validated_on_editing_started(
        Gtk::CellEditable* cell_editable, const Glib::ustring& /* path */)
{
  //Start editing with previously-entered (but invalid) text, 
  //if we are allowing the user to correct some invalid data. 
  if(m_validate_retry)
  {
    //This is the CellEditable inside the CellRenderer. 
    Gtk::CellEditable* celleditable_validated = cell_editable;

    //It's usually an Entry, at least for a CellRendererText:
    Gtk::Entry* pEntry = dynamic_cast<Gtk::Entry*>(celleditable_validated);
    if(pEntry)
    {
      pEntry->set_text(m_invalid_text_for_retry);
      m_validate_retry = false;
      m_invalid_text_for_retry.clear();
    }
  }

}

void ExampleWindow::cellrenderer_validated_on_edited(
        const Glib::ustring& path_string,
        const Glib::ustring& new_text)
{
  Gtk::TreePath path(path_string);

  //Convert the inputed text to an integer, as needed by our model column:
  char* pchEnd = 0;
  int new_value = strtol(new_text.c_str(), &pchEnd, 10);

  if(new_value > 10)
  {
    //Prevent entry of numbers higher than 10.

    //Tell the user:
    Gtk::MessageDialog dialog(*this,
            "The number must be less than 10. Please try again.",
            false, Gtk::MESSAGE_ERROR);
    dialog.run();

    //Start editing again, with the bad text, so that the user can correct it.
    //A real application should probably allow the user to revert to the
    //previous text.

    //Set the text to be used in the start_editing signal handler:
    m_invalid_text_for_retry = new_text;
    m_validate_retry = true;

    //Start editing again:
    m_TreeView.set_cursor(path, m_treeviewcolumn_validated,
            m_cellrenderer_validated, true /* start_editing */);
  }
  else
  {
    //Get the row from the path:
    Gtk::TreeModel::iterator iter = m_refTreeModel->get_iter(path);
    if(iter)
    {
      Gtk::TreeModel::Row row = *iter;

      //Put the new value in the model:
      row[m_Columns.m_col_number_validated] = new_value;
    }
  }
}

File: main.cc (For use with gtkmm 3, not gtkmm 2)

#include "examplewindow.h"
#include <gtkmm/application.h>

int main(int argc, char *argv[])
{
  Glib::RefPtr<Gtk::Application> app = Gtk::Application::create(argc, argv, "org.gtkmm.example");

  ExampleWindow window;

  //Shows the window and returns when it is closed.
  return app->run(window);
}

Glisser-déposer
This example is much like the TreeStore example, but has 2 extra columns to indicate whether the row can be dragged, and whether it can receive drag-and-dropped rows. It uses a derived Gtk::TreeStore which overrides the virtual functions as described in the TreeView Drag and Drop section.

TreeView - Glisser-déposer
Source Code
File: examplewindow.h (For use with gtkmm 3, not gtkmm 2)

#ifndef GTKMM_EXAMPLEWINDOW_H
#define GTKMM_EXAMPLEWINDOW_H

#include <gtkmm.h>
#include "treemodel_dnd.h"


class ExampleWindow : public Gtk::Window
{
public:
  ExampleWindow();
  virtual ~ExampleWindow();

protected:
  //Signal handlers:
  void on_button_quit();


  //Child widgets:
  Gtk::Box m_VBox;

  Gtk::ScrolledWindow m_ScrolledWindow;
  Gtk::TreeView m_TreeView;
  Glib::RefPtr<TreeModel_Dnd> m_refTreeModel;

  Gtk::ButtonBox m_ButtonBox;
  Gtk::Button m_Button_Quit;
};

#endif //GTKMM_EXAMPLEWINDOW_H

File: treemodel_dnd.h (For use with gtkmm 3, not gtkmm 2)

#ifndef GTKMM_EXAMPLE_TREEMODEL_DND_H
#define GTKMM_EXAMPLE_TREEMODEL_DND_H

#include <gtkmm.h>

class TreeModel_Dnd : public Gtk::TreeStore
{
protected:
  TreeModel_Dnd();

public:

  //Tree model columns:
  class ModelColumns : public Gtk::TreeModel::ColumnRecord
  {
  public:

    ModelColumns()
    { add(m_col_id); add(m_col_name); add(m_col_draggable); add(m_col_receivesdrags); }

    Gtk::TreeModelColumn<int> m_col_id;
    Gtk::TreeModelColumn<Glib::ustring> m_col_name;
    Gtk::TreeModelColumn<bool> m_col_draggable;
    Gtk::TreeModelColumn<bool> m_col_receivesdrags;
  };

  ModelColumns m_Columns;

  static Glib::RefPtr<TreeModel_Dnd> create();

protected:
  //Overridden virtual functions:
  virtual bool row_draggable_vfunc(const Gtk::TreeModel::Path& path) const;
  virtual bool row_drop_possible_vfunc(const Gtk::TreeModel::Path& dest, const Gtk::SelectionData& selection_data) const;
  
};

#endif //GTKMM_EXAMPLE_TREEMODEL_DND_H

File: examplewindow.cc (For use with gtkmm 3, not gtkmm 2)

#include <iostream>
#include "examplewindow.h"

ExampleWindow::ExampleWindow()
: m_VBox(Gtk::ORIENTATION_VERTICAL),
  m_Button_Quit("_Quit", true)
{
  set_title("Gtk::TreeView (Drag and Drop) example");
  set_border_width(5);
  set_default_size(400, 200);

  add(m_VBox);

  //Add the TreeView, inside a ScrolledWindow, with the button underneath:
  m_ScrolledWindow.add(m_TreeView);

  //Only show the scrollbars when they are necessary:
  m_ScrolledWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);

  m_VBox.pack_start(m_ScrolledWindow);
  m_VBox.pack_start(m_ButtonBox, Gtk::PACK_SHRINK);

  m_ButtonBox.pack_start(m_Button_Quit, Gtk::PACK_SHRINK);
  m_ButtonBox.set_border_width(5);
  m_ButtonBox.set_layout(Gtk::BUTTONBOX_END);
  m_Button_Quit.signal_clicked().connect(sigc::mem_fun(*this,
              &ExampleWindow::on_button_quit) );

  //Create the Tree model:
  //Use our derived model, which overrides some Gtk::TreeDragDest and
  //Gtk::TreeDragSource virtual functions:
  //The columns are declared in the overridden TreeModel.
  m_refTreeModel = TreeModel_Dnd::create();
  m_TreeView.set_model(m_refTreeModel);

  //Enable Drag-and-Drop of TreeView rows:
  //See also the derived TreeModel's *_vfunc overrides.
  m_TreeView.enable_model_drag_source();
  m_TreeView.enable_model_drag_dest();

  //Fill the TreeView's model
  Gtk::TreeModel::Row row = *(m_refTreeModel->append());
  row[m_refTreeModel->m_Columns.m_col_id] = 1;
  row[m_refTreeModel->m_Columns.m_col_name] = "Billy Bob";
  row[m_refTreeModel->m_Columns.m_col_draggable] = true;
  row[m_refTreeModel->m_Columns.m_col_receivesdrags] = true;

  Gtk::TreeModel::Row childrow = *(m_refTreeModel->append(row.children()));
  childrow[m_refTreeModel->m_Columns.m_col_id] = 11;
  childrow[m_refTreeModel->m_Columns.m_col_name] = "Billy Bob Junior";
  childrow[m_refTreeModel->m_Columns.m_col_draggable] = true;
  childrow[m_refTreeModel->m_Columns.m_col_receivesdrags] = true;

  childrow = *(m_refTreeModel->append(row.children()));
  childrow[m_refTreeModel->m_Columns.m_col_id] = 12;
  childrow[m_refTreeModel->m_Columns.m_col_name] = "Sue Bob";
  childrow[m_refTreeModel->m_Columns.m_col_draggable] = true;
  childrow[m_refTreeModel->m_Columns.m_col_receivesdrags] = true;

  row = *(m_refTreeModel->append());
  row[m_refTreeModel->m_Columns.m_col_id] = 2;
  row[m_refTreeModel->m_Columns.m_col_name] = "Joey Jojo";
  row[m_refTreeModel->m_Columns.m_col_draggable] = true;
  row[m_refTreeModel->m_Columns.m_col_receivesdrags] = true;

  row = *(m_refTreeModel->append());
  row[m_refTreeModel->m_Columns.m_col_id] = 3;
  row[m_refTreeModel->m_Columns.m_col_name] = "Rob McRoberts";
  row[m_refTreeModel->m_Columns.m_col_draggable] = true;
  row[m_refTreeModel->m_Columns.m_col_receivesdrags] = true;

  childrow = *(m_refTreeModel->append(row.children()));
  childrow[m_refTreeModel->m_Columns.m_col_id] = 31;
  childrow[m_refTreeModel->m_Columns.m_col_name] = "Xavier McRoberts";
  childrow[m_refTreeModel->m_Columns.m_col_draggable] = true;
  childrow[m_refTreeModel->m_Columns.m_col_receivesdrags] = true;

  //Add the TreeView's view columns:
  m_TreeView.append_column("ID", m_refTreeModel->m_Columns.m_col_id);
  m_TreeView.append_column("Name", m_refTreeModel->m_Columns.m_col_name);
  m_TreeView.append_column_editable("Draggable",
          m_refTreeModel->m_Columns.m_col_draggable);
  m_TreeView.append_column_editable("Receives Drags",
          m_refTreeModel->m_Columns.m_col_receivesdrags);

  show_all_children();
}

ExampleWindow::~ExampleWindow()
{
}

void ExampleWindow::on_button_quit()
{
  hide();
}

File: treemodel_dnd.cc (For use with gtkmm 3, not gtkmm 2)

#include "treemodel_dnd.h"
#include <iostream>

TreeModel_Dnd::TreeModel_Dnd()
{
  //We can't just call Gtk::TreeModel(m_Columns) in the initializer list
  //because m_Columns does not exist when the base class constructor runs.
  //And we can't have a static m_Columns instance, because that would be
  //instantiated before the gtkmm type system.
  //So, we use this method, which should only be used just after creation:
  set_column_types(m_Columns);
}

Glib::RefPtr<TreeModel_Dnd> TreeModel_Dnd::create()
{
  return Glib::RefPtr<TreeModel_Dnd>( new TreeModel_Dnd() );
}

bool
TreeModel_Dnd::row_draggable_vfunc(const Gtk::TreeModel::Path& path) const
{
  // Make the value of the "draggable" column determine whether this row can
  // be dragged:

  //TODO: Add a const version of get_iter to TreeModel:
  TreeModel_Dnd* unconstThis = const_cast<TreeModel_Dnd*>(this);
  const_iterator iter = unconstThis->get_iter(path);
  //const_iterator iter = get_iter(path);
  if(iter)
  {
    Row row = *iter;
    bool is_draggable = row[m_Columns.m_col_draggable];
    return is_draggable;
  }

  return Gtk::TreeStore::row_draggable_vfunc(path);
}

bool
TreeModel_Dnd::row_drop_possible_vfunc(const Gtk::TreeModel::Path& dest,
        const Gtk::SelectionData& selection_data) const
{
  //Make the value of the "receives drags" column determine whether a row can be
  //dragged into it:

  //dest is the path that the row would have after it has been dropped:
  //But in this case we are more interested in the parent row:
  Gtk::TreeModel::Path dest_parent = dest;
  bool dest_is_not_top_level = dest_parent.up();
  if(!dest_is_not_top_level || dest_parent.empty())
  {
    //The user wants to move something to the top-level.
    //Let's always allow that.
  }
  else
  {
    //Get an iterator for the row at this path:
    //We must unconst this. This should not be necessary with a future version
    //of gtkmm.
    //TODO: Add a const version of get_iter to TreeModel:
    TreeModel_Dnd* unconstThis = const_cast<TreeModel_Dnd*>(this);
    const_iterator iter_dest_parent = unconstThis->get_iter(dest_parent);
    //const_iterator iter_dest_parent = get_iter(dest);
    if(iter_dest_parent)
    {
      Row row = *iter_dest_parent;
      bool receives_drags = row[m_Columns.m_col_receivesdrags];
      return receives_drags;
    }
  }

  //You could also examine the row being dragged (via selection_data)
  //if you must look at both rows to see whether a drop should be allowed.
  //You could use
  //TODO: Add const version of get_from_selection_data(): Glib::RefPtr<const
  //Gtk::TreeModel> refThis = Glib::RefPtr<const Gtk::TreeModel>(this);
  //
  //Glib::RefPtr<Gtk::TreeModel> refThis =
  //Glib::RefPtr<Gtk::TreeModel>(const_cast<TreeModel_Dnd*>(this));
  //refThis->reference(); //, true /* take_copy */)
  //Gtk::TreeModel::Path path_dragged_row;
  //Gtk::TreeModel::Path::get_from_selection_data(selection_data, refThis,
  //path_dragged_row);

  return Gtk::TreeStore::row_drop_possible_vfunc(dest, selection_data);
}

File: main.cc (For use with gtkmm 3, not gtkmm 2)

#include "examplewindow.h"
#include <gtkmm/application.h>

int main(int argc, char *argv[])
{
  Glib::RefPtr<Gtk::Application> app = Gtk::Application::create(argc, argv, "org.gtkmm.example");

  ExampleWindow window;

  //Shows the window and returns when it is closed.
  return app->run(window);
}

Menu contextuel
Cet exemple est tout à fait semblable à l'exemple ListStore, excepté qu'il crée un TreeView personnalisé par dérivation pour permettre la surdéfinition de button_press_event et pour encapsuler le code du modèle d'arborescence dans cette classe dérivée. Consultez le paragraphe Menu contextuel et TreeView.

TreeView - Menu contextuel
Source Code
File: examplewindow.h (For use with gtkmm 3, not gtkmm 2)

#ifndef GTKMM_EXAMPLEWINDOW_H
#define GTKMM_EXAMPLEWINDOW_H

#include <gtkmm.h>
#include "treeview_withpopup.h"

class ExampleWindow : public Gtk::Window
{
public:
  ExampleWindow();
  virtual ~ExampleWindow();

protected:
  //Signal handlers:
  void on_button_quit();

 

  //Child widgets:
  Gtk::Box m_VBox;

  Gtk::ScrolledWindow m_ScrolledWindow;
  TreeView_WithPopup m_TreeView;

  Gtk::ButtonBox m_ButtonBox;
  Gtk::Button m_Button_Quit;
};

#endif //GTKMM_EXAMPLEWINDOW_H

File: treeview_withpopup.h (For use with gtkmm 3, not gtkmm 2)

#ifndef GTKMM_EXAMPLE_TREEVIEW_WITHPOPUP_H
#define GTKMM_EXAMPLE_TREEVIEW_WITHPOPUP_H

#include <gtkmm.h>

class TreeView_WithPopup : public Gtk::TreeView
{
public:
  TreeView_WithPopup();
  virtual ~TreeView_WithPopup();

protected:
  // Override Signal handler:
  // Alternatively, use signal_button_press_event().connect_notify()
  virtual bool on_button_press_event(GdkEventButton *ev);

  //Signal handler for popup menu items:
  void on_menu_file_popup_generic();

  
  //Tree model columns:
  class ModelColumns : public Gtk::TreeModel::ColumnRecord
  {
  public:

    ModelColumns()
    { add(m_col_id); add(m_col_name); }

    Gtk::TreeModelColumn<unsigned int> m_col_id;
    Gtk::TreeModelColumn<Glib::ustring> m_col_name;
  };
  
  ModelColumns m_Columns;

  //The Tree model:
  Glib::RefPtr<Gtk::ListStore> m_refTreeModel;

  Gtk::Menu m_Menu_Popup;
};

#endif //GTKMM_EXAMPLE_TREEVIEW_WITHPOPUP_H

File: examplewindow.cc (For use with gtkmm 3, not gtkmm 2)

#include <iostream>
#include "examplewindow.h"

ExampleWindow::ExampleWindow()
: m_VBox(Gtk::ORIENTATION_VERTICAL),
  m_Button_Quit("Quit")
{
  set_title("Gtk::TreeView (ListStore) example");
  set_border_width(5);
  set_default_size(400, 200);

  add(m_VBox);

  //Add the TreeView, inside a ScrolledWindow, with the button underneath:
  m_ScrolledWindow.add(m_TreeView);

  //Only show the scrollbars when they are necessary:
  m_ScrolledWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);

  m_VBox.pack_start(m_ScrolledWindow);
  m_VBox.pack_start(m_ButtonBox, Gtk::PACK_SHRINK);

  m_ButtonBox.pack_start(m_Button_Quit, Gtk::PACK_SHRINK);
  m_ButtonBox.set_border_width(5);
  m_ButtonBox.set_layout(Gtk::BUTTONBOX_END);
  m_Button_Quit.signal_clicked().connect( sigc::mem_fun(*this,
              &ExampleWindow::on_button_quit) );

  show_all_children();
}

ExampleWindow::~ExampleWindow()
{
}

void ExampleWindow::on_button_quit()
{
  hide();
}

File: main.cc (For use with gtkmm 3, not gtkmm 2)

#include "examplewindow.h"
#include <gtkmm/application.h>

int main(int argc, char *argv[])
{
  Glib::RefPtr<Gtk::Application> app = Gtk::Application::create(argc, argv, "org.gtkmm.example");

  ExampleWindow window;

  //Shows the window and returns when it is closed.
  return app->run(window);
}

File: treeview_withpopup.cc (For use with gtkmm 3, not gtkmm 2)

#include "treeview_withpopup.h"
#include <iostream>

TreeView_WithPopup::TreeView_WithPopup()
{
  //Create the Tree model:
  m_refTreeModel = Gtk::ListStore::create(m_Columns);
  set_model(m_refTreeModel);

  //Fill the TreeView's model
  Gtk::TreeModel::Row row = *(m_refTreeModel->append());
  row[m_Columns.m_col_id] = 1;
  row[m_Columns.m_col_name] = "right-click on this";

  row = *(m_refTreeModel->append());
  row[m_Columns.m_col_id] = 2;
  row[m_Columns.m_col_name] = "or this";

  row = *(m_refTreeModel->append());
  row[m_Columns.m_col_id] = 3;
  row[m_Columns.m_col_name] = "or this, for a popup context menu";

  //Add the TreeView's view columns:
  append_column("ID", m_Columns.m_col_id);
  append_column("Name", m_Columns.m_col_name);

  //Fill popup menu:
  Gtk::MenuItem* item = Gtk::manage(new Gtk::MenuItem("_Edit", true));
  item->signal_activate().connect(
    sigc::mem_fun(*this, &TreeView_WithPopup::on_menu_file_popup_generic) );
  m_Menu_Popup.append(*item);
    
  item = Gtk::manage(new Gtk::MenuItem("_Process", true));
  item->signal_activate().connect(
    sigc::mem_fun(*this, &TreeView_WithPopup::on_menu_file_popup_generic) );
  m_Menu_Popup.append(*item);
    
  item = Gtk::manage(new Gtk::MenuItem("_Remove", true));
  item->signal_activate().connect(
    sigc::mem_fun(*this, &TreeView_WithPopup::on_menu_file_popup_generic) );
  m_Menu_Popup.append(*item);

  m_Menu_Popup.accelerate(*this);
  m_Menu_Popup.show_all(); //Show all menu items when the menu pops up

#ifndef GLIBMM_DEFAULT_SIGNAL_HANDLERS_ENABLED
  signal_button_press_event()
    .connect(sigc::mem_fun(*this, &TreeView_WithPopup::on_button_press_event), false);
#endif
}

TreeView_WithPopup::~TreeView_WithPopup()
{
}

bool TreeView_WithPopup::on_button_press_event(GdkEventButton* event)
{
  bool return_value = false;

  //Call base class, to allow normal handling,
  //such as allowing the row to be selected by the right-click:
  return_value = TreeView::on_button_press_event(event);

  //Then do our custom stuff:
  if( (event->type == GDK_BUTTON_PRESS) && (event->button == 3) )
  {
    m_Menu_Popup.popup(event->button, event->time);
  }

  return return_value;
}

void TreeView_WithPopup::on_menu_file_popup_generic()
{
  std::cout << "A popup menu item was selected." << std::endl;

  Glib::RefPtr<Gtk::TreeView::Selection> refSelection = get_selection();
  if(refSelection)
  {
    Gtk::TreeModel::iterator iter = refSelection->get_selected();
    if(iter)
    {
      int id = (*iter)[m_Columns.m_col_id];
      std::cout << "  Selected ID=" << id << std::endl;
    }
  }
}