Glade# for Rapid Development: Simple Web Browser

  • Introduction
  • Project Setup
  • Building the Interface
  • Start Coding
  • Internationalization
  • Further Exercises
  • Update [16/11/2005]

    I had to relocate this page from Novell’s server to my own server since I can no longer host files at my previous employment. I will update this tutorial soon to reflect any change due to newer version of Mono and Gtk#.

    Update [29/09/2004]

    A couple of things have come up since the last version of this tutorial:

    1. Mozilla: because of the way Mozilla is installed in some distributions (I haven’t been able to trace what it exactly is yet), running the application in MonoDevelop causes the following failure: Unhandled Exception: System.NullReferenceException: Object reference not
      set to an instance of an object
      . Running the application from a terminal (the exe is in the project bin/Debug directory) solves that issue. Also make sure MOZILLA_FIVE_HOME is set.
    2. Glade 2.6+: If you are using a very recent version of Glade which by default adds an alignement to a new frame (Glade 2.6 and newer, I believe) you can either remove the alignment from the frame or or add the WebControl to the allignment instead of the frame with something like
      [Widget] Alignment alignment1;
      

      and later in the code

      //instead of frame1.Add(web);
      alignment1.Add(web);
      

    Introduction

    In a previous example we built a very
    simple glade application using a simple editor and the Glade Interface
    Builder. In this example, we’ll build a slightly more complex
    application, taking advantage of more tools: a web browser.

    Many applications display rich content with an HTML rendering
    engine. Evolution and Monodoc, for example, use
    gtkhtml to render and edit messages, Monodevelop uses the Mozilla
    project’s Gecko, and Konqueror and KMail use the khtml widget.

    Mono includes bindings for both gtkhtml and Gecko, but in this
    example we’ll use Gecko. Gtkhtml is more suited to simple displays,
    such as email messages or static documentation pages, but Gecko is
    more full-featured and therefore better suited to use in a
    full-fledged web browser.

    We’ll use the following tools in this example:

    • Mono
    • GTK# (gtk-sharp)
    • Gecko# (gecko-sharp, gtkmozembed#)
    • Monodevelop, an IDE for Mono

    Monodevelop isn’t absolutely necessary here, but we’ll
    be using it in the examples and screenshots.

    Project Setup

    First, launch Monodevelop and then select File -> New
    Solution
    . A dialog appears, offering you several choices for
    template and project type. Select C# for your template and Glade#
    Project for the project type. This will automatically fill in some
    starter code for you: a window and a button, similar to the “Hello
    World” code we wrote earlier.

    Note: Monodevelop uses the same project terminology as Visual
    Studio.NET: a “Solution” contains multiple “Projects,” each of which
    contains one or more source files. Other platforms describe this
    arrangement as one of “Projects” and “Sub-Projects.”

    There’s one more step as we get set up: adding the gecko-sharp
    assembly to the list of references we’ll be using in
    Monodevelop. To do that, right click on References in the
    Solutions tab on the left hand side, click Edit Reference
    and check the
    box marked gecko-sharp. Adding the assembly means that
    Monodevelop will offer autocompletion and syntax validation on Gecko
    objects as well as those in its default set of assemblies.

    Name your project MyBrowser and save it in a new directory. Now
    we’re ready to start.

    Building the Interface

    The first step is building the user interface. If you’re using
    Monodevelop, you already have a glade XML file started for you, in the
    MyBrowser/Resource/ directory. Double-click on that file in
    the list of project files Monodevelop provides, or open it using
    Glade. If you are not using Monodevelop, start Glade and create a new
    GNOME project, then create a new window for it.

    Double click on the window1 Glade object, so that its
    properties are displayed in the Properties dialog. Then, change its
    title to say “My Browser” instead of “Glade Window.

    Next, click the vertical box button ( src=”http://www.frenchguys.com/Documents/glade2_img/vbox.png” align=”baseline”/>) and click in the window.
    When prompted, select 2 rows. You now have a top and bottom half of
    your window. The top half will consist of buttons and toolbars, and
    the bottom half will be the HTML display area.

    To add spaces for your buttons, create a horizontal box ( src=”http://www.frenchguys.com/Documents/glade2_img/hbox.png” align=”baseline” />) with 3 colums in the
    top row of the vertical box you created earlier.

    In the three colums of the top half of the window, we’ll add two
    buttons and a text entry. In the Properties dialogs for the buttons
    you added, go to the Widget tab and select the stock Backward and
    Forward icons. Using stock icons provides application consistency
    across the desktop. In addition, stock labels are already
    translated, so you get free localization.

    Right-click the top area and choose hbox->Select to select
    the hbox. Change its Packing property so that Expand and
    Fill are “No.” Now the box will take up only as much space as
    it needs, rather than filling all available space.

    In the bottom half of the UI, add a frame. Then, right-click
    on its label and select Delete so that it is blank and
    unlabeled. Later, we’ll fill the frame with the Gecko rendering
    widget, which is not available in Glade.

    Your UI should now look like this:

    Now that looks more like a web browser! With the UI more or less
    complete, save your work and quit Glade.

    Start Coding

    The code that the project wizard generated for us should look
    pretty familiar by now:

    // project created on 4/16/04 at 2:17 P
    using System;
    using Gtk;
    using Glade;
    
    public class GladeApp
    {
            public static void Main (string[] args)
            {
                    new GladeApp (args);
            }
    
            public GladeApp (string[] args)
            {
                    Application.Init();
    
                    Glade.XML gxml = new Glade.XML (null, "gui.glade", "window1", null);
                    gxml.Autoconnect (this);
                    Application.Run();
            }
    
            /* Connect the Signals defined in Glade */
            public void OnWindowDeleteEvent (object o, DeleteEventArgs args)
            {
                    Application.Quit ();
                    args.RetVal = true;
            }
    }
    


    Now, we’ll add to the code. Our changes here are marked in red:

    ...
    using Glade;
      // Earlier, we added a reference to the Gecko.dll assembly. Noting
      // here that we are using it lets the compiler know we're using classes
      // from that namespace, so we can type WebControl directly and not
      // have to specify Gecko.WebControl everywhere.
    using Gecko;
    
    public class GladeApp
    {
    ...

    As we saw earlier, Glade can automatically bind UI controls to
    references, provided that indicate that the object is a
    Glade.Widget, like so:

    ...
    public class GladeApp
    {
     // Because frame1 and entry1 are tagged as Widgets
     // the Glade runtime will bind them to UI controls with the same name
    [Widget] Frame frame1;
    [Widget] Entry entry1;
    ...

    Because Glade only includes standard widgets, we couldn’t put the
    browser control into our UI when we created it earlier. Instead,
    we’ll write it in code, starting with a variable to refer to
    it. Let’s call it “web.”

    ...
    public class GladeApp
    {
    [Widget] Frame frame1;
    [Widget] Entry entry1;
    
     // Define a variable for our web control 
    WebControl web;
    ...

    Now that we’ve defined a variable to refer to it, we can actually
    create the control:

    ...
    Glade.XML gxml = new Glade.XML (null, "gui.glade", "window1", null);
    gxml.Autoconnect (this);
    
     // First we create a WebControl, using its default constructor
    web = new WebControl();
    
     // Then we ask it to show itself.
     // This is required because of a Gecko bug, and doesn't actually show the control yet. 
    web.Show();
    
     // Next, we'll add the web control to our existing frame:
    frame1.Add(web);
    ...

    Now things begin to get really interesting. Type “entry1.” and
    you’ll get a list of possible completions. Select “Activated,” the
    event called when someone types into the text entry and presses
    Return. We want the entered text to be loaded as a URL, so we’ll
    need to delegate a new method, load_url, to entry1.Activated.

    C# supports operator overloading like C++ does, so we can do that
    with a simple shortcut, literally adding the method load_url to the
    list of delegates that will be fired when
    entry1.Activated occurs. To do that, we’ll just add the
    following line, right before “Application.Run().”

    
                     // We can define load_url later
                     entry1.Activated += load_url;
     

    Of course, now we have to actually implement the load_url method we
    delegated to the entry1.Activated event. We’ll get the URL from the
    text entry control. In other languages, such as Java, we’d do that
    by using a “getter” method, something like
    entry1.getText(). In C#, we have a shortcut, again through
    operator overloads. “Getter” and “setter” methods still exist, but
    you can also just get or set data as a property of its parent
    object: entry1.Text, instead of creating a string, setting the
    string to the value of entry1.getText(), and then using the data.
    That seems trivial at first, but in more complicated situations it
    can clarify syntax to a great degree.

    ...
    // Connect the Signals defined in Glade 
    public void OnWindowDeleteEvent (object o, DeleteEventArgs args)
    {
            Application.Quit ();
            args.RetVal = true;
    }
    
    // A delegate signature has two parameters.
    
    // The first one is the source object that the event was invoked on
    // and the second is the list of arguments for that event.
    
    // We have it easy here, because we already know where the event comes
    // from, and don't care much about the arguments either.
    
    
    void load_url( object source, EventArgs args) {
        web.LoadUrl(entry1.Text);
    }
    ...

    Save your work and select Run->Compile All. Since the
    gui.glade file is listed in the project’s resource file list, it
    will be bundled into the executable automatically.

    After compilation, you should be able to run the application by
    selecting Run->Run. Test it by entering a URL such as
    http://go-mono.com, and pressing Enter to trigger the entry1.Activated
    event.

    N.B. If you’d like to run your application from the command line, you’ll need to add MOZILLA_HOME to your LD_LIBRARY_PATH

    Internationalization

    Because we’ve used some stock components in our UI, Gnome will
    know how to automatically localize them for us. For example, in a
    terminal, go to the MyBrowser/bin/Debug/ directory, where the browser
    executable was built earlier, and enter the command:

    LANG=fr_FR mono MyBrowser.exe
    

    Setting the LANG environment variable to fr_FR (French as spoken
    in France, as opposed to, say, fr_CA, the Canadian (Quebecois)
    dialect.)

    You should see this:

    For a better example, try this one:

    LANG=he_IL mono MyBrowser.exe
    

    he_IL designates Hebrew (he) as used in Israel (IL). Notice how the
    button and general UI order has been inverted to reflect the
    right-to-left writing direction found in Hebrew.

    Further Exercises

    To build on what you’ve learned so far, try the following exercises:

    • Attach actions to the Forward and Back buttons. You’ll need to implement a history queue, and decide how far back it should go.
    • Implement a Refresh button.
    • Implement a Go button, so that entry1.Activated can be triggered without pressing Return.
    • Implement a Home button.
    • Add a status bar.
    • Add exception handling for missing pages (404) and so forth.
    • Add an application menu with an About box.