Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / WPF

Custom ComboBox Control in Gtk# using MonoDevelop and Gtk3

0.00/5 (No votes)
25 Mar 2020GPL34 min read 7.4K  
ComboBox control written in C#
In this article, I present a ComboBox control written in C# which wraps a Gtk 3 ComboBoxText control, so as to simplify its usage.

Goals

  • Encourage other programmers to use the Mono / MonoDevelop / Gtk# combination by sharing this example.
  • Make it as simple to use as a basic Gtk widget, while adding as little overhead as possible.
  • Make it more accessible to Windows / Forms / WPF programmers by hiding the Gtk-specific implementation details.

Credits

Thanks are due to the Mono and MonoDevelop teams for making it possible to run C# programs on Linux. Thanks also to the Gtk# team for their framework.

Background

I am in the process of porting a bunch of my Windows C# WPF / MVVM applications to Linux / Mono / Gtk3. I am learning the Gtk toolkit as I go along, and I have been trying to craft tools that will allow me to use as much of my existing code as possible. ComboBoxes are used *everywhere* so this was one of the first items I tackled.

Concept

First of all, let me say here that I sorely miss the convenience of the WPF Bindings.

They make it trivial to hook up a list of any type of class to a ComboBox. You set the ItemsSource, DisplayMemberPath, SelectedValuePath and SelectedValue properties and WPF works its magic for you.

Here, we have to do this by ourselves, so the less we have to do, the better. Consequently, to quote the famous aircraft designer Ed Heinemann [1], we have to “simplificate and add lightness”.

If you reduce the function of the ComboBox to its simplest form, you come to the conclusion that essentially, for it to do its job, it needs just:

  • an integer “key” or index to be able to keep track of each item,
  • and an associated string to display as a description for that item.

So if we were to create a small class with an int Key and String Description, e.g., something like this:

C#
public class ComboItem
{
   public int Key { get; set; }
   public String Description { get; set; }
 }

and supply the ComboBox with a list of these, it should work just fine. Turns out we don’t even have to do that, because C# very conveniently supplies us with the KeyValuePair class. And so we come to the HComboBox control, which manipulates a list of KeyValuePair<int,String> items, which is as simple and generic as you can get.

Solution

The HComboBox control derives from HBox and contains a ComboBoxText, hence the name.

Properties

  • Get / Set the Height / Width
  • Get the ComboBoxText member so that it can be manipulated directly by the clients if need be.

Methods

  • C#
    void LoadItems( Ilist<KeyValuePair<int,String>> lst )

    Loads the list of items into the ComboBox. WPF uses ItemsSource, but this does the same job quite nicely, while also hiding the Gtk-specific code.

  • C#
    KeyValuePair<int,String> GetSelection()

    and:

  • C#
    void SetSelection( int nKey )

    are self-explanatory. They too serve to “hide” the Gtk-specific details from the user.

Events

It exposes the Changed event which the clients can use to know when the selected item has changed.

Sample Usage

See the MainWindow class of the attached sample program as an example of how to use this control.

To demonstrate the packing / unpacking to and from the KeyValuePair<int,String>, let us define a simple class to act as our domain object:

C#
public class DayOfWeek
{
  public int Day { get; set; }
  public String Name { get; set; }
}

and let us assume that the client has a list of these:

C#
IList<DayOfWeek> lst = new List<DayOfWeek>()
{
  new DayOfWeek() { Day = 0, Name = "" },
  new DayOfWeek() { Day = 1, Name = "Sunday" },
  new DayOfWeek() { Day = 2, Name = "Monday" },
  new DayOfWeek() { Day = 3, Name = "Tuesday" },
  new DayOfWeek() { Day = 4, Name = "Wednesday" },
  new DayOfWeek() { Day = 5, Name = "Thursday" },
  new DayOfWeek() { Day = 6, Name = "Friday" },
  new DayOfWeek() { Day = 7, Name = "Saturday" },
};

We then need a function to convert the above list to a list of KeyValuePairs which we can pass to the ComboBox.

In our example, it looks something like this (expanded for readability):

C#
private IList<KeyValuePair<int,String>> ConvertToComboList(IList<DayOfWeek>lst)
{
IList<KeyValuePair<int,String>> lstRet= new List<KeyValuePair<int,String>>();

    if( lst != null && lst.Count > 0 )
    {
        foreach( DayOfWeek dow in lst )
        {
            lstRet.Add( new KeyValuePair<int, string>( dow.Day, dow.Name ) );
        }
    }
    return lstRet;
} 

Finally, we can load the list into the ComboBox:

C#
m_hcbx.LoadItems( ConvertToComboList( lst ) );

Once the ComboBox has been initialized, we can set the selection thus:

C#
// Example usage :
// Set selected value to today
DateTime dt = DateTime.Now;

m_hcbx.SetSelection( (( int )dt.DayOfWeek ) + 1 );  // Sunday is 0, so +1

and to retrieve the currently selected item:

C#
KeyValuePair<int,String> day = m_hcbx.GetSelection();

// use day.Key and day.Value here...

and that’s all there is to it.

Remarks

The downside of this particular approach is that for every legacy class you wish to display in the HComboBox, you need to write a dedicated ConvertToComboList() function.

In cases where the legacy code is available and able to be modified / enhanced, as happens in my case, one solution is to define an interface similar to this:

C#
public interface IListable
{
    int Key { get; }
    String Description { get; }
} 

and have the legacy classes implement it.

You then need only one templated ConvertToComboList() function, thus:

  • C#
    IList<KeyValuePair<int,String>> ConvertComboList<T>(IList<T> lst) where T : IListable

which will work with all classes that implement the IListable interface. In my experience, classes that appear in ComboBoxes already have an integer property which can used as a key and a String property which can be used as a Description, so the implementation of the interface is trivial.

Exercises for the Reader

This control derives from HBox and contains a ComboBoxText. Another approach would be to derive from ComboBoxText directly and extend it with the added functionality. As it stands, I took the coward’s way out and exposed the ComboBoxText member via a getter property.

Requirements

  • Mono (currently I have version 6.6.0.166)
  • MonoDevelop (current version 7.8.4)
  • Gtk3 libraries (currently at version 3.22.25.56)

The supplied sample application is a MonoDevelop Solution. It requires the Gtk 3 packages.

Since these are DLLs, they are not included in the sample project, but the packages.config file is included. After opening the solution in MonoDevelop, select restore / update Packages and NuGet will fetch them.

History

  • 25th March, 2020: Initial version

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)