如何使用 Gtk Sharp 和 C# 创建用于在代码中绘图的 Cairo Context

How to create Cairo Context for drawing in code with Gtk Sharp and C#

我有几个按钮和一个区域,我想在其中绘制一些东西。我一直在尝试创建一个用 Cairo 绘图的表面,因为 Gtk 的 Drawing Area 小部件似乎不知道如何绘制基本形状(或者我只是找不到它是如何绘制的)。

我找到了一些关于此的基本教程(例如:http://zetcode.com/gui/gtksharp/drawing/ or http://www.mono-project.com/docs/tools+libraries/libraries/Mono.Cairo/tutorial/)但无法弄清楚如何在我的应用程序中制作用于绘图的表面,以及按钮和标签,而不仅仅是一个新的空白window 仅用于开罗绘图。

这是我的应用程序现在的样子:

所以,我想把中间的空白部分作为我的绘图区。

我尝试了这个,我认为它应该使我的应用程序中的绘图区小部件可以使用 Cairo 绘制:

我不太明白我应该在这里做什么。

我只需要在上面画一些基本的圆和线,所以我真的不需要 Cairo,如果能帮助我用 Gtk# 画画,我将不胜感激!

你走在正确的轨道上。不幸的是,GTK# 和 Cairo 有一些怪癖。首先,我强烈建议使用 EventBox 小部件而不是 DrawingArea。与直觉相反,我从来没有运气让 DrawingArea 正常工作。 EventBox 的工作方式大致相同,只是您可以通过将 VisibleWindow 设置为 false 来使背景透明。

public class YourWidget : EventBox
{
    public YourWidget() {
        Visible = true;
        VisibleWindow = false;
        ExposeEvent += OnExpose;
    }
}

接下来,在事件框中绘制时使用的坐标基于父window的原点,即整个应用程序window。每个小部件对象都包含一些属性 Allocation,这些属性指定它在父对象 window 中的位置以及它的大小。为了在自定义小部件 window 的范围内绘制,您必须参考这些点。这仅适用于扩展 EventBox 小部件的自定义小部件。如果没记错的话,使用 DrawingArea 的小部件是从小部件位置引用的。最后,我会将 Cairo 上下文包含在 using 块中,这样您就不会忘记处理该对象。

protected void OnExpose(object sender, ExposeEventArgs args) {
    using (Context cr = Gdk.CairoHelper.Create(this.GdkWindow)) {
          int top = Allocation.Top;
          int left = Allocation.Left;  

          cr.Rectangle(left + 8, top + 8, 10, 10);
          cr.SetSourceRGB(255, 0, 0);
          cr.Fill();
    }
}

此外,自从我启动一个新的 GTK# 应用程序以来已经有一段时间了,但您可能必须将 Mono.Cairo 包添加到项目的引用中。最后,我使用基于 EventBox 的小部件创建了 several custom widgets for a touch application,它也可以作为帮助您入门的参考。希望对大家有所帮助。

我可以在这里添加一些内容。您可以使用 DrawingArea,我从来没有遇到过像 Skyler 一样的麻烦,但 Cairo 几乎可以在任何小部件或基础 window 上绘制。我经常使用 DrawingAreas,我还扩展了基本小部件并使用 cairo 进行渲染。

我能给的关键点:

  • 使用小部件的 Expose 事件获取对 Cairo 需要绘制到的 GdkWindow 的引用。例如:

    protected void OnExpose (object o, Gtk.ExposeEventArgs args)
    {
        Draw (args.Event.Window);
    }
    
  • 在您的绘图操作中,使用 Cairo Helper 创建一个 Cairo 上下文。

    private void Draw (Gdk.Window w)
    {
        double _bg = (55.0 / 255.0);
        using (Cairo.Context _c = Gdk.CairoHelper.Create (w)) {
            _c.LineWidth = 2;
            _c.SetSourceRGB (_bg, _bg, _bg);
            _c.Rectangle (0, 0, this.WidthRequest, this.HeightRequest);
            _c.StrokePreserve ();
            _c.SetSourceRGBA (_bg, _bg, _bg, 0.5);
            _c.Fill ();
            _c.Dispose ();
        }
    }
    
  • 在最后使用 Dispose() 否则你会 运行 进入内存问题。

如果您愿意,我还可以向您展示如何使用 Pango 文本渲染库。它们有点复杂,但比在 cairo 库中使用 SetText() 操作要好得多。

干杯,M

您可以在没有 Cairo 的情况下绘图,但这被认为已过时,至少使用 C# 是这样。

最详尽的信息(虽然不多)可以在这里找到: http://www.mono-project.com/docs/tools+libraries/libraries/Mono.Cairo/

就个人而言,正如#muszeo 所提到的,我使用自定义的 DrawingArea:

public class MyWidget: Gtk.DrawingArea {       
    public MyWidget(int width, int height)
    {
        this.Width = width;
        this.Height = height;
        this.SetSizeRequest( width, height );
        this.ExposeEvent += (o, args)  => this.OnExposeDrawingArea();
    }

    /// <summary>
    /// Redraws the widget
    /// </summary>
    private void OnExposeDrawingArea()
    {
        using (var canvas = Gdk.CairoHelper.Create( this.GdkWindow ))
        {
            // Draw with the canvas
            // /* i.e. */ canvas.LineTo( 100, 100 );
            canvas.Stroke();

            // Clean
            canvas.GetTarget().Dispose();
        }
    }
}

那么您只需要在 window(也许是对话框?)中创建您的小部件:

var dlg = new Gtk.Dialog( "Demo", this, Gtk.DialogFlags.Modal );
var swScroll = new Gtk.ScrolledWindow();

MyWidget widget = new MyWidget( 512, 512 );
swScroll.AddWithViewport( widget );
dlg.VBox.PackStart( swScroll, true, true, 5 );
dlg.AddButton( Gtk.Stock.Close, Gtk.ResponseType.Close );
dlg.ShowAll();
dlg.Run(); 

我保留了一个包含各种 Gtk# 简单演示的 repo,其中之一是使用 Cairo 绘制图形的非常基本的图表 class:

http://github.com/baltasarq/GtkSharpDemo

希望对您有所帮助。