65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 MonoDevelop 和 Gtk3 在 Gtk# 中创建自定义组合框控件

2020 年 3 月 25 日

GPL3

4分钟阅读

viewsIcon

7963

用 C# 编写的 ComboBox 控件

目标

  • 通过分享这个例子,鼓励其他程序员使用 Mono / MonoDevelop / Gtk# 组合。
  • 使其易于使用,就像基本的 Gtk 小部件一样,同时增加尽可能少的开销。
  • 通过隐藏 Gtk 特定实现细节,使其对 Windows / Forms / WPF 程序员更易于访问。

致谢

感谢 Mono 和 MonoDevelop 团队使在 Linux 上运行 C# 程序成为可能。也感谢 Gtk# 团队提供的框架。

背景

我正在将我的一些 Windows C# WPF / MVVM 应用程序移植到 Linux / Mono / Gtk3。我一边学习 Gtk 工具包,一边尝试创建工具,以便尽可能多地利用我现有的代码。ComboBox 无处不在,所以这是我最先处理的项目之一。

概念

首先,我想说我非常怀念 WPF 绑定带来的便利。

它们可以轻松地将任何类型的类的列表连接到 ComboBox。您只需设置 ItemsSourceDisplayMemberPathSelectedValuePathSelectedValue 属性,WPF 就会为您实现神奇的效果。

在这里,我们必须自己完成这些工作,所以我们做的越少越好。因此,引用著名飞机设计师 Ed Heinemann [1] 的话,我们必须“简化并增加轻量级”。

如果您将 ComboBox 的功能简化到最基本的形式,您就会得出结论,为了完成其工作,它本质上只需要

  • 一个整数“key”或索引来跟踪每个项目,
  • 以及一个相关的 string 来显示为该项目的描述。

所以,如果我们创建一个带有 int KeyString Description 的小型类,例如类似这样的东西

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

并将 ComboBox 填充此类列表,它应该可以正常工作。事实证明,我们甚至不必这样做,因为 C# 非常方便地提供了 KeyValuePair 类。于是我们得到了 HComboBox 控件,它操作 KeyValuePair<int,String> 项目的列表,这是你能得到的尽可能简单和通用的。

解决方案

HComboBox 控件派生自 HBox 并包含一个 ComboBoxText,因此得名。

属性

  • Get / Set Height / Width
  • 获取 ComboBoxText 成员,以便客户端需要时可以直接操作它。

方法

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

    将项目列表加载到 ComboBox 中。WPF 使用 ItemsSource,但这个方法做得很好,同时隐藏了 Gtk 特定的代码。

  • KeyValuePair<int,String> GetSelection()

  • void SetSelection( int nKey )

    是自解释的。它们也用于“隐藏”用户不应看到的 Gtk 特定细节。

事件

它公开了 Changed 事件,客户端可以使用该事件来知道何时已更改所选项目。

示例用法

请参阅所附示例程序中的 MainWindow 类,了解如何使用此控件。

为了演示 KeyValuePair<int,String> 的打包/解包,让我们定义一个简单的类来充当我们的域对象

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

并假设客户端有一个此类列表

      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" },
      }; 

然后我们需要一个函数将上面的列表转换为 KeyValuePair 列表,我们可以将其传递给 ComboBox

在我们的例子中,它看起来大致像这样(为方便阅读而展开)

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;
} 

最后,我们可以将列表加载到 ComboBox

 m_hcbx.LoadItems( ConvertToComboList( lst ) );

初始化 ComboBox 后,我们可以这样设置选择

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

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

以及检索当前选定的项目

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

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

就这样,一切都完成了。

备注

这种特定方法的缺点是,对于您希望在 HComboBox 中显示的每个旧类,都需要编写一个专用的 ConvertToComboList() 函数。

在旧代码可用并且可以修改/增强的情况下,就像我的情况一样,一种解决方案是定义一个类似的接口

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

并让旧类实现它。

然后您只需要一个模板化的 ConvertToComboList() 函数,如下所示

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

这将适用于实现 IListable 接口的所有类。根据我的经验,出现在 ComboBox 中的类通常已经有一个可以作为 key 的整数属性和一个可以作为 Description 的字符串属性,因此实现接口非常简单。

给读者的练习

此控件派生自 HBox 并包含一个 ComboBoxText。另一种方法是直接派生自 ComboBoxText 并用附加功能扩展它。目前,我采取了胆小的做法,通过 getter 属性公开了 ComboBoxText 成员。

要求

  • Mono (目前我有 6.6.0.166 版本)
  • MonoDevelop (当前版本 7.8.4)
  • Gtk3 库(目前版本为 3.22.25.56)

提供的示例应用程序是一个 MonoDevelop 解决方案。它需要 Gtk 3 包。

由于这些是 DLL,因此不包含在示例项目中,但包含了 packages.config 文件。在 MonoDevelop 中打开解决方案后,选择还原/更新包,NuGet 将会获取它们。

历史

  • 2020 年 3 月 25 日:初始版本
© . All rights reserved.