Thursday, October 22, 2009

Menus in Gtk# (and Monodevelop)



Unfortunately there is surprisingly little information about Gtk# and gtk itself in Internet. So when I started my first UI project in Monodevelop, I had to spend a lot of time looking for information and experimenting.

My environment: gtk# version: 2.12, Monodevelop: 2.0, OS: Ubuntu-9.04

When you create new Gtk project, everything is very intuitive.
First little surprise is that in Gtk you can not create any element on your window but instead you have to put there a placeholder, usually a Vbox, and only than you can use familiar controls like menu.

Menu editing is intuitive and cause no problems. But I wanted to generate one of the menus programmaticaly: list of last files open. Here is a surprise.
Monodevelop has its own UI builder and most of the controls have appropriate object instances auto-generated. For example:

// Container child MainWindow.Gtk.Container+ContainerChild
this.vbox1 = new Gtk.VBox();
this.vbox1.Name = "vbox1";
this.vbox1.Spacing = 6;


But menu is an exception. Menu items do not have appropriate variables generated. There are two ways you can get your menu item: you can digg through "Children" properties until you find the menu item you want. Not too elegant, if you are looking several levels down.
The other way is to use UIManager:

var menu = (Menu)((ImageMenuItem)this.UIManager.GetWidget("/menubar2/FileAction/LastProjectsAction")).Submenu;

But how do I know what the path to the menu is? You need to open your auto-generated window class and find there a line like this:

this.UIManager.AddUiFromString("<ui><menubar name='menubar2'><menu name='FileAction' action='FileAction'><menuitem name='SaveAction' action='SaveAction'/><menu name='LastProjectsAction' action='LastProjectsAction'/><menuitem name='ManageProjectsAction' action='ManageProjectsAction'/><separator/><menuitem name='ExitAction' action='ExitAction'/></menu></menubar></ui>");

If you traverse nodes, following their "name" attribute, that would be the path you are looking for.

Using "Menu.Append()" you can add new MenuItem to the menu. You run your program, and... there is no result. There is no exception, but no new menu item either. Why is it missing? Gtk again: by default any widget's visibility property is off. Don't ask me why. Just call Gtk.Widget.Show() on newly added menu item. As alternative, you can add menu items and than call "Widget.ShowAll()" on the parent menu.

When those problems were sorted out I got functioning basic UI application which loads last used files as a menu.