242.md 5.6 KB
Newer Older
W
wizardforcel 已提交
1
# GTK# 中的自定义小部件 
W
wizardforcel 已提交
2 3 4 5 6 7 8 9 10

> 原文: [http://zetcode.com/gui/gtksharp/customwidget/](http://zetcode.com/gui/gtksharp/customwidget/)

工具箱通常仅提供最常见的窗口小部件,例如按钮,文本窗口小部件,滑块等。没有工具箱可以提供所有可能的窗口小部件。

客户端程序员可以创建更多专门的小部件。 他们使用工具箱提供的绘图工具来完成此任务。 有两种可能:程序员可以修改或增强现有的小部件,或者可以从头开始创建自定义小部件。

## 刻录小部件

W
wizardforcel 已提交
11
这是我们从头开始创建的小部件的示例。 可以在各种媒体刻录应用(例如 Nero 刻录 ROM)中找到此小部件。
W
wizardforcel 已提交
12 13 14

`burning.cs`

W
wizardforcel 已提交
15
```cs
W
wizardforcel 已提交
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
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();
    }
}

```

W
wizardforcel 已提交
146
我们在窗口底部放置一个`DrawingArea`并手动绘制整个窗口小部件。 所有重要的代码都位于`Burning`类的`OnExposeEvent()`方法中。 此小部件以图形方式显示了介质的总容量和可供我们使用的可用空间。 该小部件由比例小部件控制。 自定义窗口小部件的最小值为 0,最大值为 750。如果值达到 700,则开始绘制红色。 这通常表示过度燃烧。
W
wizardforcel 已提交
147

W
wizardforcel 已提交
148
```cs
W
wizardforcel 已提交
149 150 151 152 153 154 155
string[] num = new string[] { "75", "150", "225", "300", 
    "375", "450", "525", "600", "675" };

```

这些数字显示在刻录小部件上。 它们显示了介质的容量。

W
wizardforcel 已提交
156
```cs
W
wizardforcel 已提交
157 158 159 160 161
SharpApp parent = (SharpApp) GetAncestor (Gtk.Window.GType);        
int cur_width = parent.CurValue;

```

W
wizardforcel 已提交
162
这两行从刻度小部件获取当前数字。 我们获得父窗口小部件,并从父窗口小部件中获得当前值。
W
wizardforcel 已提交
163

W
wizardforcel 已提交
164
```cs
W
wizardforcel 已提交
165 166 167 168 169 170 171
int till = (int) ((width / 750.0) * cur_width);
int full = (int) ((width / 750.0) * 700);

```

`till`参数确定要绘制的总大小。 该值来自滑块小部件。 它占整个面积的一部分。 `full`参数确定我们开始用红色绘制的点。

W
wizardforcel 已提交
172
```cs
W
wizardforcel 已提交
173 174 175 176 177 178 179 180 181 182
cr.SetSourceRGB(1.0, 1.0, 0.72);
cr.Rectangle(0, 0, full, 30);
cr.Clip();
cr.Paint();
cr.ResetClip();

```

此代码在此处绘制了一个黄色矩形,直到介质充满为止。

W
wizardforcel 已提交
183
```cs
W
wizardforcel 已提交
184 185 186 187 188 189 190 191 192
TextExtents extents = cr.TextExtents(num[i-1]);
cr.MoveTo(i*step-extents.Width/2, 15);
cr.TextPath(num[i-1]);
cr.Stroke();

```

这里的代码在刻录小部件上绘制数字。 我们计算`TextExtents`来正确定位文本。

W
wizardforcel 已提交
193
```cs
W
wizardforcel 已提交
194 195 196 197 198 199 200 201 202 203 204 205 206
void OnChanged(object sender, EventArgs args)
{
    Scale scale = (Scale) sender;
    cur_value = (int) scale.Value;
    burning.QueueDraw();
}

```

我们从小部件中获取值,并将其存储在`cur_value`变量中以备后用。 我们重新绘制刻录的小部件。

![Burning widget](img/e2235d128fceb509a7f7200eb41349a0.jpg)

W
wizardforcel 已提交
207
图:刻录小部件
W
wizardforcel 已提交
208

W
wizardforcel 已提交
209
在本章中,我们在 GTK# 中创建了一个自定义窗口小部件。