65.9K
CodeProject 正在变化。 阅读更多。
Home

[教程 3] 使用 C# 进行图形拖放和删除对象的程序

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.50/5 (3投票s)

2016年3月8日

CPOL

5分钟阅读

viewsIcon

21917

downloadIcon

1902

教程第三部分,介绍使用 C# 和 GDI 及 SVG 进行图形拖放和删除对象的程序

引言

这是本系列教程的第三部分,介绍如何使用 C# 在 Windows 窗体中构建图形程序,并将其导出为矢量格式。

......此外,您还将了解如何移动、删除、**Ctrl+Z** 您的矢量图形,并将其保存为一种特殊的格式,以便您的程序可以再次读取。

此外,我们还将学习如何保存 XML 文件……如何以 Verilog 格式导出……如何使用星形算法……如何使用手形工具……如何手动创建 **Ctrl+Z** 技术。

您将能够构建什么

在本教程中,我们将从介绍如何让您的 C# 程序绘制特定的 SVG 路径开始。

接续上次的进度……今天,我们将讨论如何拖放对象。

  1. 首先进行一些修改
  2. 添加鼠标移动功能

背景

Using the Code

1. 那么,让我们开始对我们之前的代码进行一些修改

由于我们要移动对象,因此需要删除任何类型的缓冲,因此让我们在我们的 `main function` 中编写以下代码行:

this.DoubleBuffered = true;  //will remove any buffering

此外,让我们添加一些 `private data members` 来指示哪个对象被选中,以及是否已经选中任何元素。

int selected=0;
bool is_selected = false;

添加一个新的键盘监听器来监听移动命令,所以在 `Form1_KeyDown` 中:

  else if (e.KeyData == Keys.M)
            {
                action = "move";
            }

我们还将需要一个成员来保存左键单击的值。

另一个成员用于保存一个 `bool`,该 `bool` 标识您是否是第一次拖动一个对象以创建循环,并知道您选择了哪个对象……否则,每次移动鼠标都会循环一次,并创建一个用作选择边界框的矩形。

私有成员

 MouseButtons m = MouseButtons.Left;
 bool first = true;
 RectangleF selected_recatngle = new RectangleF();

此外,我还修改了心形的 `sData`,使其变为:

private string sData = "M97.5,181.5c-4.5-5-15.6-14.8-24.8-21.7c-27.2-20.5-30.9-23.
5-41.9-33.7c-20.4-18.7-29-37.6-29-63.1c0-12.5,0.9-17.3,4.4-24.6C12,25.9,20.8,16.7,
31.9,11c7.9-4,11.8-5.8,25-5.9c13.8-0.1,16.7,1.5,24.8,6c9.9,5.4,20.1,17,22.2,25.3l1.3,
5.1l3.2-7c18.1-39.6,75.9-39,96,1c6.4,12.7,7.1,39.8,1.4,55.1c-7.4,19.9-21.2,35.1-53.3,
58.4c-21,15.3-44.8,38.3-46.4,41.6C104.2,194.2,106,191,97.5,181.5z";

现在,让我们通过转到 `designer` 并将属性>>背景颜色>>更改为白色,使我们的应用程序更像一个图形应用程序。

2. 现在,让我们来处理我们的拖放功能。

首先,转到窗体设计器并添加一个 `mouse move action`。

然后,如果我们转到后台代码,我们将看到已创建 `private void Form1_MouseMove(object sender, MouseEventArgs e)`。

我们需要在用户使用此方法后开始拖动……因此,选择技术仅在用户第一次使用此函数时发生……一旦此技术完成,无论何时用户继续选择对象,程序都无需重新计算已选择的对象……所以总结一下……程序仅在用户第一次使用该函数时循环遍历列表……当他继续移动该对象时,程序无需重新计算哪个对象……这将通过用户第一次使用该函数时的条件来实现。

  private void Form1_MouseMove(object sender, MouseEventArgs e)
        {
		 if (first == true && e.Button == m && action.Equals("move"))
		{
		}
	}

这将检查这是您第一次进入此函数,以及您是否使用左鼠标按钮进入此函数,以及该操作是否为移动操作。

现在我们需要遍历我们的列表以了解选择了哪个对象……所以先编写 `foreach` 循环进行迭代……然后,我们将设置条件以查看选择点是否在任何形状内。

if (sh.draw_svg().Path(render).GetBounds().Contains(e.Location))

然后在此函数中,让我们定义将用于定义矩形边界的矩形,并将选定的 `int` 设置为计数器以了解选择了哪个对象,并将选择的 `bool` 设置为 `true`,如果发生这种情况,则打破 `foreach` 循环,否则将 `is_selected` 设置为 `false`,当然永远不要忘记增加你的计数器,如果你进入了这个条件,那么这是你的第一次……那么下次你移动对象时,你就不需要进入这个条件了,所以只需将 `first` 设置为 `false`。

 private void Form1_MouseMove(object sender, MouseEventArgs e)
        {
            if (first == true && e.Button == m && action.Equals("move"))
            {
                int counter = 0;
                foreach (shapes sh in shape_list)
                {
                    if (sh.draw_svg().Path(render).GetBounds().Contains(e.Location))
                    {
                        selected_recatngle = sh.draw_svg().Path(render).GetBounds();
                        selected = counter;
                        is_selected = true;
                        break;
                    }

                    else
                    {
                        is_selected = false;
                    }
                    counter++;
                }
                first = false;
            }

以上所有只是为了定义所选的形状……我们确实需要移动它……只需先更新边界矩形……然后我们只需修改 `shape` 类中的 `translateX` 和 `translateY` 属性……并调用 `paint` 函数。

如果对象未被选中,它将进入下一个 `else` 条件,将 `first` 布尔值重置为 `false`……这样,当您下次选择另一个形状时,您将知道您选择了哪个对象。

 if (is_selected == true && e.Button == m && action.Equals("move"))
            {
                selected_recatngle.Location = e.Location;
                shape_list.ElementAt<shapes>(selected).translateX = e.X;
                shape_list.ElementAt<shapes>(selected).translateY = e.Y;
                Invalidate();
            }
            else
            {
                first = true;
            }

所以最终的 `mouse move` 将是这样的:

   private void Form1_MouseMove(object sender, MouseEventArgs e)
        {
          
 	 if (first == true && e.Button == m && action.Equals("move"))
            {
                int counter = 0;
                foreach (shapes sh in shape_list)
                {
                    // if (sh.draw_svg().Path(render).GetBounds().Contains(e.Location))
                    //inefficient
                    if (sh.draw_svg().Path(render).IsVisible(e.Location))//more efficient
                    {
                        selected_recatngle = sh.draw_svg().Path(render).GetBounds();
                        selected = counter;
                        is_selected = true;
                        break;
                    }

                    else
                    {
                        is_selected = false;
                    }
                    counter++;
                }
                first = false;
            }

            if (is_selected == true && e.Button == m && action.Equals("move"))
            {
                selected_recatngle.Location = e.Location;
                shape_list.ElementAt<shapes>(selected).translateX = e.X;
                shape_list.ElementAt<shapes>(selected).translateY = e.Y;
                Invalidate();
            }
            else
            {
                first = true;
            }
        }

现在让我们处理在更新的形状成员中的 `translateX` 和 `translateY`……只需在 `heart` 和 `star` 的 `draw svg` 中添加此矩阵……以便在返回之前移动返回值,并应用此 `transform`。

 Matrix m = new Matrix();
            m.Translate(translateX, translateY, MatrixOrder.Append);
            alu.Transform(m);

因此,`heart` 和 `star` 中的最终 `draw_svg` 将是:

 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);
            //modification
            Matrix m = new Matrix();
            m.Translate(translateX, translateY, MatrixOrder.Append);
            alu.Transform(m);
            //modification
            region = new Region(pa.Path(render));

            return pa;
        }

现在让我们绘制选定的矩形……让它成为一个虚线红色矩形……在 `onpaint` 函数中,添加此代码:

Point p_temp = new Point((int)selected_recatngle.Location.X, (int)selected_recatngle.Location.Y);
            Size sssize = new System.Drawing.Size((int)selected_recatngle.Size.Width, 
                                                  (int)selected_recatngle.Size.Height);
            Rectangle rrrr = new Rectangle(p_temp, sssize);
            Pen selection_pen = new Pen(Brushes.Red, 2);
            selection_pen.DashStyle = DashStyle.Dot;
            e.Graphics.DrawRectangle(selection_pen, rrrr);

以上行的目的是简单地将 `RectangleF` 转换为一个普通的矩形进行绘制,然后使用红色虚线笔进行绘制。

现在要删除特定的对象,只需修改 `Form1_KeyDown` 处理程序:

 else if (e.KeyData == Keys.Delete && is_selected==true)
            {
                shape_list.RemoveAt(selected);
                RectangleF null_rectangle = new RectangleF();
                selected_recatngle = null_rectangle;
                is_selected = false;
                Invalidate();
            }

正如我们在鼠标移动中之前确定的选定项一样,只需将其从列表中删除,并将选择矩形点设置为 `null`。

 
我非常想知道您对这些教程的看法……它们是否达到了您的期望??……我非常想听到您的反馈。

历史

  • 2016年3月8日:初始版本
© . All rights reserved.