编程技术 · 2018 年 12 月 06 日 0

C#实现画布功能

在C#中设计和实现canvas

canvas_bin canvas_src

介绍

在许多不同的应用中,需要图形输入。除了创建新的图形对象(如线条,矩形和多边形)外,还需要以不同比例编辑和查看对象。在某些情况下,图形事件(如形状创建,选择和移动)应绑定到某些应用程序逻辑。

画布或图形编辑器是为应用程序提供此类功能的组件。

背景

Canvas控件或窗口小部件是一个包含负责查看和编辑的不同类型图形对象的组件。

大多数GUI工具包都有自己的canvas组件。例如,Qt工具包,Borland C ++ Builder和Tcl / Tk中有画布。不知何故,它在.NET中缺失了。

对于canvas实现和功能,我使用Qt和Tcl / Tk工具包的经验。在这两个工具包中,canvas提供了丰富的功能来创建和操作图形对象。

在两个工具包中,画布的缩放和平移会影响存储在其中的图形对象的几何形状。当您在画布中存储“真实世界”测量值时不方便,因此每次在画布中编辑它时,都应该跟踪“真实世界”坐标系的变化。

为了避免这种不便,这种实现在逻辑上划分为几何模型和视图。当您在画布内编辑对象时,几何模型(以“真实世界”坐标显示)正在变化,视图(客户端坐标)保持不变,当您重新缩放和滚动画布时,几何模型保持不变。

Canvas几何模型可以有几个视图显示模型的不同部分。模型更改时,所有视图都会自动更新,这意味着如果您在其中一个视图中移动或编辑形状,您将看到其他视图中的更改。

使用画布

Canvas在命名空间中的Canvas.cs文件中实现CanvasControl

示例如何使用它在CanvasDemo.cs文件中。

要创建新的Canvas并连接到其事件:

// create new canvas
canvas1 = new CanvasControl.Canvas();
// connect listeners to canvas events
canvas1.CoordChangeEvent += new
  CanvasControl.Canvas.CoordChangeEventHandler(OnCoordChainged);
canvas1.ItemNewEvent += new
  CanvasControl.Canvas.ItemEventHandler(OnNewCanvasItem);
canvas1.ItemEnterEvent += new
  CanvasControl.Canvas.ItemEventHandler(OnEnterCanvasItem);

where:

CoordChangeEvent event is generated when cursor moves inside the Canvas. A second parameter of the event handler CoordChangeEventHandler contains coordinates of the cursor and R,G,B values of the background image.

public void OnCoordChainged(object sender, 
CanvasControl.CoordEventArgs e)
{
    textBox1.Text = e.X.ToString() + "," + e.Y.ToString();
}

ItemNewEvent and ItemEnterEvent – events are generated when cursor enters and leaves graphical object. The second parameter contains ID (index) of the object. Graphical object could be accessed by its ID.

public void OnEnterCanvasItem(object sender, 
CanvasControl.ItemEventArgs e)
{
    textBox3.Text = e.indx.ToString();
}

In order to create additional view of existing Canvas (its geometrical model) we should create second Canvas, say canvas2 and attach it to previous one:

canvas2 = new CanvasControl.Canvas();
canvas2.setGeomModel(canvas1.getGeomModel());

Canvas itself does not contain other graphical controls, it’s controlled by its API. But there is an option to connect Scrollbar controls to update scrollbars controls during canvas navigation.

canvas1.connectScrolls(hScrollBar1,vScrollBar1);

To create, move or select graphical objects, we need to change Canvas state as follows:

// create graphical objects
canvas1.CreateRectangle();

canvas1.CreateLine();

canvas1.CreatePolygon();

// move objects
canvas1.StartMoving();
// select objects
canvas1.StartSelection();

Graphical object could be accessed by its ID and geometrical and other properties could be retrieved or updated. In the function bellow, ID-s of selected items are retrieved and updated with user defined color.

private void btnColor_Click(object sender, System.EventArgs 
e)
{
        ColorDialog colorDialog = new ColorDialog();
        if(colorDialog.ShowDialog() != DialogResult.OK)
                 return;

        Color color = colorDialog.Color;
        int [] selected = canvas1.GetSelected();
        foreach(int i in selected)
        {
                 CanvasControl.CanvasItem item = canvas1.GetItem(i);
                 item.Icolor = color;
                 item.select(false);
        }

        canvas1.Invalidate();
        canvas1.Update();
}

您可以创建自己的几何对象,为此,您应该实现CanvasItem接口(请参阅Canvas.cs文件)并使用Canvas.AddShape方法将其放入画布中。

设计问题

在画布的设计中使用了几种设计模式。

模板模式在CanvasItem类中用于在基类中实现某些形状的通用功能。

Observer模式用于通知订阅者有关画布更改的信息,它是使用C#事件实现的。

Facade模式用于隐藏用户的内部画布对象。

在上面的类图中,提供了创建canvas控件的类

  • Canvas – 封装画布控件的视图(模型/视图模式)。Canvas CanvasGeomModel通过C#事件与几何模型同步。每次模型更改时,模型都会生成ItemChangedEventModelChangedEvent强制画布更新视图。每次视图Canvas需要显示模型,它调用draw的方法CanvasGeomModel指定的图形上下文(或画家对象)Graphics和一个Matrix“客户”之间transformaion坐标的视图系统和“真实世界”坐标的模型的系统。最终,CanvasGeomModeldraw请求被委托给concreat shape,它们实现CanvasItem draw了在视图上绘制自己的接口。
  • CanvasGeomModel – 封装画布控件的几何模型。它负责存储,编辑和显示从CanvasItem课堂派生的不同形状。几何对象在模型中以“真实世界”坐标表示,独立于不同视图的“客户”坐标。
  • CanvasItem– 封装存储在几何模型中的几何对象或形状的常见行为CanvasGeomModel。它表示使用by Canvas来操作和显示不同类型的形状。为了创建新的形状,应该实现CanvasItem接口中定义的方法。
public class LineCanvasItem : CanvasItem
{
  public float x1;
  public float y1;
  public float x2;
  public float y2;
  private bool is_selected = false;

  // construct from 2 points coordinates
  public LineCanvasItem(float x_1,float y_1,float x_2,float y_2)
  {
    x1 = x_1;
    y1 = y_1;
    x2 = x_2;
    y2 = y_2;
  }
  // returns true if item is selected
  public override bool isSelected()
  {
    return is_selected;
  }
  // select item
  public override void select(bool m)
  {
    is_selected = m;
  }
  // returns true if the item is within the distanse
  public override bool isCloseToPoint(PointF pnt,float dist)
  {
    double curr_dist = Geom.segmentToPointSqrDist(
      new PointF(x1,y1),new PointF(x2,y2),pnt);

    return Math.Sqrt(curr_dist) < dist;
  }
  // return bounding box of the item
  public override RectangleF boundingBox()
  {
    return new RectangleF(x1,y1,x2-x1,y2-y1);
  }
  // start point X coordinate of the shape
  public override float X()
  {
    return x1;
  }
  // start point Y coordinate of the shape
  public override float Y()
  {
    return y1;
  }
  // move shape to specified location
  public override void move(PointF p)
  {
    float dx = p.X-x1;
    float dy = p.Y-y1;

    x1 += dx;
    y1 += dy;

    x2 += dx;
    y2 += dy;
  }
  // move shape by specified shift
  public override void moveBy(float xs,float ys)
  {
    x1+=xs;
    y1+=ys;

    x2+=xs;
    y2+=ys;
  }
  // draw shape on the view
  public override void draw(Graphics graph,
      System.Drawing.Drawing2D.Matrix trans)
  {
    // transform points to "client" view coordinate system
    PointF [] points = {new PointF(x1,y1), new PointF(x2,y2)};
    trans.TransformPoints(points);

    // draw line in "client" coordinate system
    if(is_selected)
      graph.DrawLine(new Pen(Color.Cyan),
        (int)points[0].X,(int)points[0].Y,
        (int)points[1].X,(int)points[1].Y);
    else
      graph.DrawLine(new Pen(Icolor),(int)points[0].X,
        (int)points[0].Y,
        (int)points[1].X,(int)points[1].Y);
  }
}

然后Canvas.AddShape应该使用方法为画布添加新形状:

canvas1.AddShape(new LineCanvasItem(10,10,100,100));
  • Geom– 使用的几何程序Canvas

历史

  • 28/02/2004初始版本。
  • 17/03/2004几何模型与画布视图分离+设计说明。

原文链接:https://www.codeproject.com/Articles/6238/Canvas-implementation-for-C

参考https://answers.unity.com/questions/849176/how-to-create-a-canvas-and-text-ui-46-object-using.html

https://stackoverflow.com/questions/3300412/generate-c-sharp-image-object-with-canvas-todataurl

https://docs.microsoft.com/en-us/dotnet/framework/wpf/controls/how-to-create-and-use-a-canvas

https://www.c-sharpcorner.com/Resources/630/how-to-create-a-canvas.aspx