DEV Community

Kashif Soofi
Kashif Soofi

Posted on

GTK 4 ListView with .NET 8

GtkListView

GTK 4 has added new widgets for to display lists namely GtkListView, GtkGridView and GtkColumnView. Here is an excellent article by ToshioCP about GtkListView and an article by Matthias Clasen on why the new widgets were added to GTK 4.

In my search I could not find any guide or getting started with GtkListView in .NET. This post is an attempt to do that and also a reference to what I did :).

Project Setup

Let's start by creating a directory and run following command to create a new solution, project and add project to solution.

dotnet new sln --name ListView
dotnet new console -o ListView.App
dotnet sln add ListView.App/ListView.App.csproj
Enter fullscreen mode Exit fullscreen mode

Next let's add a reference to GirCore.Gtk-4.0 nuget package.

cd ListView.App
dotnet add package GirCore.Gtk-4.0 --version 0.5.0-preview.4
Enter fullscreen mode Exit fullscreen mode

Application

Let's start by adding an empty GtkApplicationWindow. Add following code in Program.cs

var application = Gtk.Application.New("org.kashif-code-samples.listview.sample", Gio.ApplicationFlags.FlagsNone);
application.OnActivate += (sender, args) =>
{
    var window = Gtk.ApplicationWindow.New((Gtk.Application) sender);
    window.Title = "ListView Sample";
    window.SetDefaultSize(300, 300);
    window.Show();
};
return application.RunWithSynchronizationContext(null);
Enter fullscreen mode Exit fullscreen mode

Running the application, will display an empty window.

Empty Window

No Selection ListView

Next lets enhance our sample application to add a window that has a simple ListView with no selection.
First lets add a new class CodeListViewWindow inherting from Gtk.Window to our project.
ListView will need 2 objects,

  • SelctionModel - is an interface to support selection of items
  • ListItemFactory - creates or recycles GtkListItem and connects it with an item of the list model. We are going to use NoSelection, SelectionModel needs a ListStore and we are going to use StringList to display a list of strings. For ListItemFactory we are going to use SignalListItemFactory and setup OnSetup and OnBind signal handlers to create Label and bind data to that Label.

You might get a warning on ListView is a namespace but being used as a type, trick is to add using Gtk; statement after namespace or fully qualify ListView.

Here is the code for the class.

public class CodeListViewWindow : Window
{
    public CodeListViewWindow()
        : base()
    {
        Title = "No Selection ListView";
        SetDefaultSize(300, 300);

        var stringList = StringList.New(["One", "Two", "Three", "Four"]);
        var selectionModel = NoSelection.New(stringList);
        var listItemFactory = SignalListItemFactory.New();
        listItemFactory.OnSetup += SetupSignalHandler;
        listItemFactory.OnBind += BindSignalHandler;

        var listView = ListView.New(selectionModel, listItemFactory);

        var scrolledWindow = ScrolledWindow.New();
        scrolledWindow.Child = listView;
        Child = scrolledWindow;
    }

    private void SetupSignalHandler(SignalListItemFactory sender, SetupSignalArgs args)
    {
        var listItem = args.Object as ListItem;
        if (listItem is null)
        {
            return;
        }

        var label = Label.New(null);
        listItem.Child = label;
    }

    private void BindSignalHandler(SignalListItemFactory sender, BindSignalArgs args)
    {
        var listItem = args.Object as ListItem;
        if (listItem is null)
        {
            return;
        }

        var label = listItem.Child as Label;
        if (label is null)
        {
            return;
        }

        var item = listItem.Item as StringObject;
        label.SetText(item?.String ?? "NOT FOUND");
    }
}
Enter fullscreen mode Exit fullscreen mode

Next step is to add a button on our application window, then we will add a click handler to open a new window that has a no selection list view.
Update the OnActivate handler as follows.

application.OnActivate += (sender, args) =>
{
    var buttonShowCodeListView = CreateButton("Show Code ListView Window");
    buttonShowCodeListView.OnClicked += (_, _) => new CodeListViewWindow().Show();

    var gtkBox = Gtk.Box.New(Gtk.Orientation.Vertical, 0);
    gtkBox.Append(buttonShowCodeListView);

    var window = Gtk.ApplicationWindow.New((Gtk.Application) sender);
    window.Title = "ListView Sample";
    window.SetDefaultSize(300, 300);
    window.Child = gtkBox;
    window.Show();
};
Enter fullscreen mode Exit fullscreen mode

And helper method to create the button.

static Gtk.Button CreateButton(string label)
{
    var button = Gtk.Button.New();
    button.Label = label;
    button.SetMarginTop(12);
    button.SetMarginBottom(12);
    button.SetMarginStart(12);
    button.SetMarginEnd(12);
    return button;
}
Enter fullscreen mode Exit fullscreen mode

Running the application will show our application window with a button.

Application Window

And clicking on that button will open a new window with a simple list as follows.

Code List View Window

BuilderListItemFactory

We can also use the BuilderListItemFactory instead of SignalListItemFactory, this allows us the define the behaviour in ui file instead of writing the signal handlers in code.
I have added the following ui file copied from article by TochioCP.

<interface>
  <template class="GtkListItem">
    <property name="child">
      <object class="GtkLabel">
        <binding name="label">
          <lookup name="string" type="GtkStringObject">
            <lookup name="item">GtkListItem</lookup>
          </lookup>
        </binding>
      </object>
    </property>
  </template>
</interface>
Enter fullscreen mode Exit fullscreen mode

Next I have added another class TemplateListViewWindow with following content. ListItemTemplate.ui file is set as Embedded Resource and for ReadResourceAsByteArray we would need to add using GObject;.

using System.Reflection;
using Gtk;
using GObject;
using GLib;

public class TemplateListViewWindow : Window
{
    public TemplateListViewWindow()
        : base()
    {
        Title = "Template ListView";
        SetDefaultSize(300, 300);

        var stringList = StringList.New(["One", "Two", "Three", "Four"]);
        var selectionModel = SingleSelection.New(stringList);
        var bytes = Assembly.GetExecutingAssembly()
            .ReadResourceAsByteArray("ListItemTemplate.ui");
        var listItemFactory = BuilderListItemFactory.NewFromBytes(null, Bytes.New(bytes));
        var listView = ListView.New(selectionModel, listItemFactory);

        var scrolledWindow = ScrolledWindow.New();
        scrolledWindow.Child = listView;
        Child = scrolledWindow;
    }
}
Enter fullscreen mode Exit fullscreen mode

And I have added a button to show this new window on our application window in Program.cs.

    var buttonShowTemplateListView = CreateButton("Show Template ListView Window");
    buttonShowTemplateListView.OnClicked += (_, _) => new TemplateListViewWindow().Show();
    ...
    gtkBox.Append(buttonShowTemplateListView);
Enter fullscreen mode Exit fullscreen mode

Window with ListItem template looks like following

Window with Template ListItem

Wrapup

Here is how you can use Gtk ListView in your application, hopefully it would be a good starting point for anyone looking to start using ListView in C# with code or template.

Source

Source code for the sample application is available on GitHub in gtk4-dotnet8-list-view repository.

References

In no particular order

Top comments (0)