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





0/5 (0投票)
用 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
。您只需设置 ItemsSource
、DisplayMemberPath
、SelectedValuePath
和 SelectedValue
属性,WPF 就会为您实现神奇的效果。
在这里,我们必须自己完成这些工作,所以我们做的越少越好。因此,引用著名飞机设计师 Ed Heinemann [1] 的话,我们必须“简化并增加轻量级”。
如果您将 ComboBox
的功能简化到最基本的形式,您就会得出结论,为了完成其工作,它本质上只需要
- 一个整数“
key
”或索引来跟踪每个项目, - 以及一个相关的
string
来显示为该项目的描述。
所以,如果我们创建一个带有 int Key
和 String 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 日:初始版本