GTK# 中的自定义小部件
最后修改于 2023 年 10 月 18 日
工具包通常只提供最常见的小部件,如按钮、文本小部件、滑块等。没有哪个工具包能提供所有可能的小部件。
更专业的小部件由客户端程序员创建。他们使用工具包提供的绘图工具来完成。有两种可能性:程序员可以修改或增强现有的部件,或者可以从头开始创建自定义部件。
刻录部件
这是一个我们从头开始创建的小部件的例子。 可以在各种媒体刻录应用程序中找到此小部件,例如 Nero Burning ROM。
burning.cs
using Gtk; using Cairo; using System; class Burning : DrawingArea { string[] num = new string[] { "75", "150", "225", "300", "375", "450", "525", "600", "675" }; public Burning() : base() { SetSizeRequest(-1, 30); } protected override bool OnExposeEvent(Gdk.EventExpose args) { Cairo.Context cr = Gdk.CairoHelper.Create(args.Window); cr.LineWidth = 0.8; cr.SelectFontFace("Courier 10 Pitch", FontSlant.Normal, FontWeight.Normal); cr.SetFontSize(11); int width = Allocation.Width; SharpApp parent = (SharpApp) GetAncestor (Gtk.Window.GType); int cur_width = parent.CurValue; int step = (int) Math.Round(width / 10.0); int till = (int) ((width / 750.0) * cur_width); int full = (int) ((width / 750.0) * 700); if (cur_width >= 700) { cr.SetSourceRGB(1.0, 1.0, 0.72); cr.Rectangle(0, 0, full, 30); cr.Clip(); cr.Paint(); cr.ResetClip(); cr.SetSourceRGB(1.0, 0.68, 0.68); cr.Rectangle(full, 0, till-full, 30); cr.Clip(); cr.Paint(); cr.ResetClip(); } else { cr.SetSourceRGB(1.0, 1.0, 0.72); cr.Rectangle(0, 0, till, 30); cr.Clip(); cr.Paint(); cr.ResetClip(); } cr.SetSourceRGB(0.35, 0.31, 0.24); for (int i=1; i<=num.Length; i++) { cr.MoveTo(i*step, 0); cr.LineTo(i*step, 5); cr.Stroke(); TextExtents extents = cr.TextExtents(num[i-1]); cr.MoveTo(i*step-extents.Width/2, 15); cr.TextPath(num[i-1]); cr.Stroke(); } ((IDisposable) cr.Target).Dispose(); ((IDisposable) cr).Dispose(); return true; } } class SharpApp : Window { int cur_value = 0; Burning burning; public SharpApp() : base("Burning") { SetDefaultSize(350, 200); SetPosition(WindowPosition.Center); DeleteEvent += delegate { Application.Quit(); }; VBox vbox = new VBox(false, 2); HScale scale = new HScale(0, 750, 1); scale.SetSizeRequest(160, 35); scale.ValueChanged += OnChanged; Fixed fix = new Fixed(); fix.Put(scale, 50, 50); vbox.PackStart(fix); burning = new Burning(); vbox.PackStart(burning, false, false, 0); Add(vbox); ShowAll(); } void OnChanged(object sender, EventArgs args) { Scale scale = (Scale) sender; cur_value = (int) scale.Value; burning.QueueDraw(); } public int CurValue { get { return cur_value; } } public static void Main() { Application.Init(); new SharpApp(); Application.Run(); } }
我们将一个DrawingArea
放在窗口的底部,并手动绘制整个小部件。所有重要的代码都位于 Burning 类的 OnExposeEvent
方法中。此小部件以图形方式显示介质的总容量和可供我们使用的可用空间。该小部件由一个 scale 小部件控制。我们自定义小部件的最小值为 0,最大值为 750。如果我们达到 700 的值,我们开始用红色绘制。 这通常表示过度刻录。
string[] num = new string[] { "75", "150", "225", "300", "375", "450", "525", "600", "675" };
这些数字显示在刻录小部件上。它们显示了介质的容量。
SharpApp parent = (SharpApp) GetAncestor (Gtk.Window.GType); int cur_width = parent.CurValue;
这两行从 scale 小部件获取当前数字。我们获取父小部件,并从父小部件获取当前值。
int till = (int) ((width / 750.0) * cur_width); int full = (int) ((width / 750.0) * 700);
till
参数确定要绘制的总大小。 此值来自滑块小部件。它是整个区域的一部分。 full
参数确定我们开始用红色绘制的点。
cr.SetSourceRGB(1.0, 1.0, 0.72); cr.Rectangle(0, 0, full, 30); cr.Clip(); cr.Paint(); cr.ResetClip();
这里的这段代码绘制一个黄色矩形,直到介质已满。
TextExtents extents = cr.TextExtents(num[i-1]); cr.MoveTo(i*step-extents.Width/2, 15); cr.TextPath(num[i-1]); cr.Stroke();
这里的这段代码在刻录小部件上绘制数字。我们计算 TextExtents
以正确地定位文本。
void OnChanged(object sender, EventArgs args) { Scale scale = (Scale) sender; cur_value = (int) scale.Value; burning.QueueDraw(); }
我们从 scale 小部件获取值,将其存储在 cur_value
变量中供以后使用。我们重新绘制刻录小部件。

在本章中,我们在 GTK# 中创建了一个自定义小部件。