交互式添加多个形状,使用链表 GDI+ SVG





4.00/5 (3投票s)
教程 2 (如何使用链表交互式添加多个 GDI 形状)
这是本教程系列的第二篇,旨在创建一个功能齐全的图形程序,该程序可以导出其艺术作品为svg、png格式。我们还可以将其实现为电路程序,并以verilog格式输出其工作成果,我建议您阅读我的第一篇教程 https://codeproject.org.cn/script/Articles/MemberArticles.aspx?amid=10828711。
您将能够构建什么
- https://drive.google.com/folderview?id=0B739QmcCMLMHVU1DbHhIQlZ3MTA&usp=sharing
- https://www.youtube.com/watch?v=6hXJ1EoAYc0
引言
今天,我们将讨论如何让用户向程序中添加不同的形状。我们将使用C#中的链表来实现这一点,这比普通列表更实用……仅仅因为它不需要限制用户可添加形状的数量。
总体计划
- 添加不同的形状(就像我们在教程1中所做的那样)
- 链表快速概述
- 一种非常简单的方法,让用户添加多个对象
首先:添加不同的形状
就像在tut1
中一样,我们将创建一个新的形状……我们正在使用一个名为SVG渲染库的库。
只需在“管理NuGet程序包”中键入svg
,它就会显示为第一个结果
- 首先,打开 http://editor.method.ac/ 或您喜欢的任何图形程序
- 从形状库中……绘制任何形状(我画了一颗心!!)
- 然后保存您的艺术作品(在此网站上,文件>>保存图像将导出为svg格式)
- 在记事本中打开您下载的svg文件。
- 注意
path
标签,它将包含一个d
属性……复制里面的内容。 - 创建一个继承自基类形状的类。
- 添加
draw_svg 虚函数
,如教程1中所述。 - 将从记事本中复制的
d
属性放入一个string
变量中
最终的类应该是这样的
class heart : shapes
{
private string sData = "m114.071045,61.254959c40.765015,-116.94968 200.484436,
0 0,150.36232c-200.484055,-150.36232 -40.763611,-267.312 0,-150.36232z";
private Region region;
public override Svg.SvgPath draw_svg()
{
Svg.SvgPath pa = new Svg.SvgPath();
Svg.Pathing.SvgPathSegmentList svgSvgPathSegmentList = new Svg.Pathing.SvgPathSegmentList();
var converter = TypeDescriptor.GetConverter(typeof(Svg.Pathing.SvgPathSegmentList));
pa.PathData = (Svg.Pathing.SvgPathSegmentList)converter.ConvertFrom(sData);
Svg.ISvgRenderer render = null;
region = new Region(pa.Path(render));
return pa;
}
}
第二部分:链表快速概述
链表就像一串连接在一起的对象。链中的每个对象(称为节点)都指向其他对象……通过这种方式,可以向列表中添加多个对象,而无需从一开始就定义列表的大小……这将让您的用户绘制大量对象。
定义列表
List<int> list = new List<int>();
or for an object of a class
List<class> list = new List<class>();
在末尾添加新项
list.Add(class_object);
在指定索引处插入
list.Insert(index, class_object);
删除
list.RemoveAt(index);
选择特定索引处的特定对象
class_name a= list.ElementAt<class_name>(index);
第三部分:处理我们的项目
首先,我们正在处理form.cs文件
- 让我们定义一个名为shape_list的私有数据成员
List<shapes> shape_list = new List<shapes>();
现在,为了使我们的程序具有交互性,我们需要捕获按键,例如“
s
”代表星星(star)和“h
”代表心(heart)。这将修改一个动作字符串,我们需要它来识别要绘制的对象。然后,我们将需要捕获鼠标单击到所需位置以绘制所需的形状。
- 所以,让我们开始定义动作字符串作为一个私有数据成员
string action = "";
- 定义捕获鼠标单击和键盘的方法
从窗体的设计器>>>属性 >>>事件 >>> 在“操作”(action)下>>双击鼠标单击区域。
这将在后端代码中创建
private void Form1_MouseClick(object sender, MouseEventArgs e)
。从窗体的设计器>>>属性 >>> 事件 >>> 在“键”(key)下>>双击“键按下”(key down)。
这将在后端代码中创建
private void Form1_KeyDown(object sender, KeyEventArgs e)
。在
keydown
方法中,输入:private void Form1_KeyDown(object sender, KeyEventArgs e) { if (e.KeyData == Keys.S) { action = "star"; } else if (e.KeyData == Keys.H) { action = "heart"; } }
现在,在鼠标单击方法中,写入两个条件来处理两种操作
private void Form1_MouseClick(object sender, MouseEventArgs e) { if (action.Equals("star")) { shapes sh = new shapes(); //define base class star st = new star(); //define derived class sh = st; //put derived inside base sh.translateX = e.X;//capture location of mouse in x axis sh.translateY = e.Y;//capture location of mouse in y axis //and put them into translateX and translateY variables //in shape variable //in shapes base class shape_list.Add(st);// add this shape to the shape_list Invalidate();//call the on paint function } else if (action.Equals("heart")) { shapes sh = new shapes(); //define base class heart h = new heart(); //define derived class sh = h; //put derived inside base sh.translateX = e.X; //capture location of mouse in x axis sh.translateY = e.Y; //capture location of mouse in y axis //and put them into translateX and translateY variables //in shape variable // in shapes base class shape_list.Add(h); // add this shape to the shape_list Invalidate(); //call the on paint function } }
- 但是派生类尚未理解translateX和translateY……因为
drawSvg
取决于派生类,所以我们必须修改派生类中的draw_svg
(不要忘记draw svg是一种方法,可以将svg文件中定义的路径数据用于GraphicsPath
,然后绘制到您的Windows窗体中)。所以,我们将不得不修改我们旧的
star 类
,使其变为class star :shapes { private string sData = "m1.212486,47.649818l47.846649,0l14.78479, -45.453926l14.784615,45.453926l47.846909,0l-38.708832, 28.091873l14.785934,45.454201l-38.708626,-28.09272l-38.708832, 28.09272l14.785782,-45.454201l-38.708389,-28.091873l0,0z"; // i just modified the sData to make the star smaller and to be more precise private Region region; public override Svg.SvgPath draw_svg() { Svg.SvgPath pa = new Svg.SvgPath(); Svg.Pathing.SvgPathSegmentList svgSvgPathSegmentList = new Svg.Pathing.SvgPathSegmentList(); var converter = TypeDescriptor.GetConverter(typeof(Svg.Pathing.SvgPathSegmentList)); pa.PathData = (Svg.Pathing.SvgPathSegmentList)converter.ConvertFrom(sData); Svg.ISvgRenderer render = null; //---------modification GraphicsPath alu = new GraphicsPath();//define a Graphics path alu = pa.Path(render);// way to connect between graphics path and svg path // as this code can only apply translate on graphics path // and we need to draw the defined svg // so we need to make a connection between // GraphicsPath and svgPath Matrix m = new Matrix();//define matrix to apply translation //apply translation using the translateX and translateY defined in //base class on the MATRIX //we still need to apply this translation on graphics path itself m.Translate(translateX, translateY, MatrixOrder.Append); alu.Transform(m);//apply translation on graphics path itself region = new Region(pa.Path(render));//svg library needs a Renderer to convert vectors //---------modification return pa; } }
同样的操作也会在
heart 派生类
上完成class heart : shapes { private string sData = "m114.071045,61.254959c40.765015,-116.94968 200.484436, 0 0,150.36232c-200.484055,-150.36232 -40.763611, -267.312 0,-150.36232z"; private Region region; public override Svg.SvgPath draw_svg() { Svg.SvgPath pa = new Svg.SvgPath(); Svg.Pathing.SvgPathSegmentList svgSvgPathSegmentList = new Svg.Pathing.SvgPathSegmentList(); var converter = TypeDescriptor.GetConverter(typeof(Svg.Pathing.SvgPathSegmentList)); pa.PathData = (Svg.Pathing.SvgPathSegmentList)converter.ConvertFrom(sData); Svg.ISvgRenderer render = null; GraphicsPath alu = new GraphicsPath(); alu = pa.Path(render); Matrix m = new Matrix(); m.Translate(translateX, translateY, MatrixOrder.Append); alu.Transform(m); region = new Region(pa.Path(render)); return pa; } }
- 现在,我们只需要更改form.cs中的
onpaint
函数只需进行一个简单的
foreach
循环(就像一个普通的for
循环,但不需要索引或停止条件……它只是遍历列表中的项)。protected override void OnPaint(PaintEventArgs e) { Graphics g = e.Graphics; foreach (shapes sh in shape_list)//just loop on each item of the list //and name this item sh { e.Graphics.DrawPath(new Pen(Brushes.Black, 2), sh.draw_svg().Path(render)); e.Graphics.FillPath(Brushes.Khaki, sh.draw_svg().Path(render)); } }
现在,当您运行程序时,您将能够
- 如果您想绘制星星,请按键盘上的s键绘制星星,然后单击鼠标以绘制星星的位置
- 如果您想绘制心,请按键盘上的h键绘制心,然后单击鼠标以绘制心
对我来说,了解您对这些教程的看法非常重要……它们是否达到了您的预期??……您的反馈对我来说真的非常重要。