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

[教程 8] 手形工具

starIconstarIconstarIconstarIconstarIcon

5.00/5 (9投票s)

2016年6月27日

CPOL

7分钟阅读

viewsIcon

13698

downloadIcon

501

我们将创建手形工具以平移绘图窗体,并使用打开和关闭手形的自定义光标

引言

这是如何使用 C# 在 Windows 窗体中构建图形程序并以矢量格式导出作品的系列教程中的第八个教程

.... 此外,您还将了解如何移动、删除、撤销您的矢量艺术,并将其保存为特殊格式以便您的程序再次读取。

此外,我们将学习如何保存 XML 文件... 如何导出为 Verilog 格式... 如何使用星形算法... 如何使用手形工具... 如何手动创建撤销技术。

您将能够构建什么

教程地图

  1. 编辑super_form以添加手形工具图标,为此新工具添加新操作,为单击此新工具创建函数,并为此新工具使用新的自定义光标。
  2. Form1中,定义一个比用户可见的实际窗体大小更大的尺寸,称为AutoScrollMinSize
  3. Form1中,编辑Form1_MouseMove并为新工具添加一个新部分,这将是该工具的核心。
  4. Form1中,创建Form1_MouseDown,以完成该工具的功能。
  5. Form1中,编辑OnPaint以创建绘制形状的偏移量。
  6. 当用户选择手形工具时按下鼠标时更改光标,这将通过Form1super_form之间的连接实现。
  7. Form1中,平移后释放鼠标时,将鼠标重置为打开的手形。
  8. Form1中,编辑之前教程中讨论过的所有先前部分,以使其与新工具良好配合。
  9. 启用快捷键(空格键)以使用新的手形工具。
  10. Form1中,使用OnPaint来显示平移窗体时点周围的圆圈和线条轮廓。

 


背景


 

更新

添加了两个部分

9-启用快捷键(空格键)以使用新的手形工具。

10-在Form1中,使用OnPaint来显示平移窗体时点周围的圆圈和线条轮廓。

并更新了关于使用偏移量选择和移动线条和对象重要性的解释图片,因为之前的图片中发现了一个错误,但现在已修复。

 

 


1-编辑super_form以添加手形工具图标,为此新工具添加新操作,为单击此新工具创建函数,并为此新工具使用新的自定义光标。

使用super_form[design],添加一个按钮。

将按钮图标设置为此图像

只需将名称更改为Hand_tool

结果将是

现在我们将使用super_form.cs

首先编辑 action 枚举以添加一个新动作

 public enum action
    {
        star, heart, line, move,point,hand, none
    }

然后我们将为新“手形”工具的新按钮添加一个单击函数。

        private void Hand_tool_Click(object sender, EventArgs e)
        {
            super_action = action.hand;
            toolStrip2.Visible = false;//make the toolbar of the line proprieties invisible when 
                                       //clicking this tool
        }

现在我们需要为Hand_tool添加一个新的自定义光标

所以像之前在这个教程中讨论的那样,将此图标添加为资源

open_hand.zip

然后编辑之前编写的Hand_tool_Click函数,使其成为

        private void Hand_tool_Click(object sender, EventArgs e)
        {
            super_action = action.hand;

//new code
            cursor_super = new Cursor(drag_and_drop_and_delete.Properties.Resources.open_hand.Handle);
            this.Cursor = cursor_super;
//new code 

            toolStrip2.Visible = false;
        }

 


2-在Form1中,定义一个比用户可见的实际窗体大小更大的尺寸,称为AutoScrollMinSize

  public Form1()
        {
            InitializeComponent();


            this.Size = new Size(1000, 700);

//new code
            this.AutoScrollMinSize = new Size(2000, 1000);
             // you must define an AutoScrollMinSize with a size bigger than that of the size of the
             // Form, as it tells the actual size of the form , but not all of this space is really
             // visible to the user at once, as the size(1000, 700) is the actual size visible to the
             // user without need to use the hand tool, but the AutoScrollMinSize is the actual size
             // the user can draw but with use of the hand tool
//new code
                  


            this.DoubleBuffered = true;
            Invalidate();
        }

 


3-在Form1中,编辑Form1_MouseMove并为新工具添加一个新部分,这将是该工具的核心。

我们将使用Form1来处理手形工具本身的功能,但首先我们需要定义一些变量。

        public Point prevScrollPoint;
        public int pointX, pointY;
        public int offsetX, offsetY;

然后让我们使用Form1_MouseMove

添加一个新条件

   private void Form1_MouseMove(object sender, MouseEventArgs e)
        {

                 .

                 .//old code

                 .

            //new code
            else if (super_form.super_action.Equals(action.hand) )
            {
                if (e.Button == MouseButtons.Left)
                //only occurs when this user clicks the left mouse click
                {
                }

            }
            //new code

       }

然后在此条件中,您将使用之前编写的变量

   private void Form1_MouseMove(object sender, MouseEventArgs e)
        {

                 .

                 .//old code

                 .

            else if (super_form.super_action.Equals(action.hand) )
            {
                if (e.Button == MouseButtons.Left)
                {
                    //new code
                    offsetX = e.X - prevScrollPoint.X;
                    offsetY = e.Y - prevScrollPoint.Y;

                    pointX = -(this.AutoScrollPosition.X);
                    pointY = -(this.AutoScrollPosition.Y);

                    pointX += -offsetX;
                    pointY += -offsetY;

                    this.AutoScrollPosition = new Point(pointX, pointY);

                    prevScrollPoint = e.Location;

                    Invalidate();
                    //new code
                }

            }

       }

 


4-在Form1中,创建一个Form1_MouseDown,以完成该工具的功能。

手形工具的功能尚未完成,您必须创建一个MouseDown函数。

        private void Form1_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left && super_form.super_action.Equals(action.hand))
            {
                prevScrollPoint = e.Location;
            }
        }

 


5-在Form1中,编辑OnPaint以创建绘制形状的偏移量。

我们必须编辑OnPaint以真正使用AutoScrollPosition的值作为偏移量来转换对象。

        protected override void OnPaint(PaintEventArgs e)
        {
            Graphics g = e.Graphics;

            //new code

            g.TranslateTransform(this.AutoScrollPosition.X, this.AutoScrollPosition.Y);

            //new code

        .

        .//old code

        .

      }

 

 


6-当用户选择手形工具时按下鼠标时更改光标,这将通过Form1super_form之间的连接实现。

.

当鼠标被点击时,我们需要将光标更改为闭合的手形。

要做到这一点,我们必须在super_form内部更改光标,但super_form的光标不是静态变量,所以你必须将super_form传递给所有Form1元素,这会非常耗费内存,所以还有另一种我使用的方法。

即在super_form内部使用一个静态按钮,并为该按钮分配一个点击函数,这个点击函数将简单地将super_form的光标更改为与静态super_cursor相同。

我们将在super_form中工作。

根据之前的教程,我们将拥有

        public static Cursor cursor_super;

今天我们将创建一个静态按钮

        public  static Button change_cursor_static_button;

super_form的构造函数中

  public super_form()
        {
            InitializeComponent();

          .

          .//old code


          .

            //new code
            change_cursor_static_button = new Button();
            change_cursor_static_button.Click += change_cursor_static_button_function;
                                            // assign a function to this button
            //new code


        }

那么这个函数将很简单,只是将cursor_super分配给super_form的光标

        private void change_cursor_static_button_function(object sender, EventArgs e)
        {
            this.Cursor = cursor_super;
        }

现在我们将与Form1一起工作

我们将编辑Form1_MouseDown函数

我们将添加一个新的闭合手形光标

closed_hand.zip

        private void Form1_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left && super_form.super_action.Equals(action.hand))
            {
                prevScrollPoint = e.Location;

                Cursor new_cursor
                = new Cursor(drag_and_drop_and_delete.Properties.Resources.closed_hand.Handle);

                super_form.cursor_super = new_cursor;
                 //load this new cursor to the cursor_super of the super_cursor

                super_form.change_cursor_static_button.PerformClick();
            }
        }

 


7-在Form1中。平移后释放鼠标时,将鼠标重置为打开的手形。

简单地说,在Form1中,我们将创建一个MouseUp函数

我们将使用打开的手形光标

        private void Form1_MouseUp(object sender, MouseEventArgs e)
        {
            if (super_form.super_action.Equals(action.hand))
            {
                Cursor new_cursor =
                new Cursor(drag_and_drop_and_delete.Properties.Resources.open_hand.Handle);

                super_form.cursor_super = new_cursor;
                super_form.change_cursor_static_button.PerformClick();
            }
         }

 

 


 

现在手形工具的功能已经完成,但我们必须使旧教程中的旧代码与这个新工具良好配合。

8-在Form1中,编辑我们之前在教程中讨论过的所有先前部分,以使其与新工具良好配合。

 

 

所以必须有一个偏移量

  1. 移动和选择线内的点,
  2. 这个概念也适用于点击整条线,
  3. 也适用于移动和选择对象
  4. 以及绘制对象(星形、心形)
  5. 以及绘制线条

1-点击线内的点

使用Form1_MouseMove

  private void Form1_MouseMove(object sender, MouseEventArgs e)
        {

               .

               .//old code

               .

            else if (super_form.super_action.Equals(action.point))
            {

                   if (e.Button == m)
                   {
//new code

                    Point with_offset = new Point(e.X - 10 - AutoScrollPosition.X,
                                                  e.Y - 10 - AutoScrollPosition.Y);
//new code

                       .

                       .//old code
  
                       .

                   }

               .

               .//old code

               .

          }

}

并在线中选择点

使用Form1_MouseClick

        private void Form1_MouseClick(object sender, MouseEventArgs e)
        {

          .

          .//old code

          .

            else if (super_form.super_action.Equals(action.point))
            {

                if (e.Button == m)
                {

                    Point with_offset = new Point(e.X - 10 - AutoScrollPosition.X,
                                                  e.Y - 10 - AutoScrollPosition.Y);

          .

          .//old code

          .

               }

          .

          .//old code

          .

            }

          .

          .//old code

          .

}

2-点击整条线,

&

3-移动和选择对象

使用Form1_MouseMove

 private void Form1_MouseMove(object sender, MouseEventArgs e)
        {

 if (super_form.super_action.Equals(action.move))
            {
                //new code
                Point with_offset = new Point(e.X - 10 - AutoScrollPosition.X, 
                                              e.Y - 10 - AutoScrollPosition.Y);
                //new code

                    .

                    .//old code

                    .
                if (is_selected && e.Button == m)
                {
                    selected_recatngle.Location = e.Location;
                    if (selected_type.Equals("shape"))
                    {
                        shape_list.ElementAt<shapes>(selected).translateX = e.X- AutoScrollPosition.X;
                        shape_list.ElementAt<shapes>(selected).translateY = e.Y- AutoScrollPosition.Y;
                    }
                }


            }

}

要选择对象,我们将使用Form1_MouseClick

        private void Form1_MouseClick(object sender, MouseEventArgs e)
        {

               .

               .//old code

               .
            else if (super_form.super_action.Equals(action.move))
            {

               .

               .//old code

               .

                //new hand
                Point with_offset = new Point(e.X - 10 - AutoScrollPosition.X, 
                                              e.Y - 10 - AutoScrollPosition.Y);
                //new hand

               .

               .//old code

               .

           }

               .

               .//old code

               .

}

4-绘制对象(星形、心形)

使用Form1_MouseClick

        private void Form1_MouseClick(object sender, MouseEventArgs e)
        { 
            if (super_form.super_action.Equals(action.star))
            {
                shapes sh = new shapes();
                star st = new star();
                sh = st;

                //new code
                sh.translateX = e.X- AutoScrollPosition.X;
                sh.translateY = e.Y - AutoScrollPosition.Y;
                //new code

                shape_list.Add(st);

                Invalidate();
            }

            else if (super_form.super_action.Equals(action.heart))
            {
                shapes sh = new shapes();
                heart h = new heart();
                sh = h;

                //new code
                sh.translateX = e.X- AutoScrollPosition.X;
                sh.translateY = e.Y- AutoScrollPosition.Y;
                //new code

                shape_list.Add(h);

                Invalidate();
            }

              .

              .//old code

              .

}

5-绘制线条

使用line_action

private void line_action(MouseEventArgs e)
        {

             .

             .//old code

             .

            //new hand
            Point_line.X = e.Location.X - AutoScrollPosition.X;
            Point_line.Y = e.Location.Y - AutoScrollPosition.Y;
            //new hand

             .

             .//old code

             .

      }

 


9-启用快捷键(空格键)以使用新的手形工具。

我们将使用 Adobe Reader、Adobe Illustrator、Adobe Photoshop 中的手形工具快捷键,即仅使用空格键来平移屏幕,按住空格键不会锁定在手形工具上,而只会平移屏幕,当用户完成平移(松开空格键)后,旧工具将返回给用户。

所以这个快捷键会很特殊,因为按住空格键后,旧工具会返回给用户。

 

因此在super_form中,我们将使用一个公共静态变量,我们将其命名为old_action

       public static action old_action;

然后,在tabControl1_KeyDown函数内部,我们将为我们的新快捷键空格按钮添加一个条件。

        private void tabControl1_KeyDown(object sender, KeyEventArgs e)
        {

             .

             .//old code

             .
//new code
            else if (e.KeyData == Keys.Space)
            {

// we only need this code to run once, as we need a hold action not just click,so we use an if 
condition , if no if condition this code would continuously run as the user holds the space bar which
we don't need

                if (super_action != action.hand)
                {
                    old_action = super_action;//save the current action

                    super_action = action.hand;//change the current action to be hand

                    Cursor new_cursor= 
                    new Cursor(drag_and_drop_and_delete.Properties.Resources.open_hand.Handle);
                    //load the open_hand_cursor to a new cursor variable

                    this.Cursor = new_cursor;
                    //and make the current cursor the open_hand_cursor 

                    toolStrip2.Visible = false;
                    //make the line proprieties menu discussed in previous tut invisible

/////previous tut
////    https://codeproject.org.cn/Tips/1105143/tut-Draw-Lines-with-Circle-and-Rectangle-End

 

                }
            }
//new code

             .

             .//old code

             .

     }

现在我们将在用户停止按住空格键时选择旧工具,因此我们将在之前在教程中讨论过的tabcontrol中创建一个KeyUp函数。

所以,在super_form中的tabControl1Properties

那么函数将是

 private void tabControl1_KeyUp(object sender, KeyEventArgs e)
        {

            super_action = old_action; 
            //simply load the old tool that we have just saved it before to
            be the current tool

        }

但光标仍然是手形工具,所以我们必须进一步编辑tabControl1_KeyUp以使其也能重置光标为旧操作的旧光标。

所以我们将使用old_action来确定要加载的光标(旧工具的光标)

        private void tabControl1_KeyUp(object sender, KeyEventArgs e)
        {

            super_action = old_action;

            if(super_action.ToString()=="heart")
            {
                Cursor new_cursor =
                new Cursor(drag_and_drop_and_delete.Properties.Resources.heart_cursor.Handle);
                //load the cursor of the tool that corresponds to the action

                this.Cursor = new_cursor;//load this cusor to be the cursor of the super_form
            }

            //same concept would work on other actions

            else if (super_action.ToString() == "star")
            {
                Cursor new_cursor = 
                new Cursor(drag_and_drop_and_delete.Properties.Resources.star_cursor.Handle);

                this.Cursor = new_cursor;
            }

            else if (super_action.ToString() == "line")
            {
                Cursor new_cursor = 
                new Cursor(drag_and_drop_and_delete.Properties.Resources.Line_cursor.Handle);

                this.Cursor = new_cursor;
            }

            else if (super_action.ToString() == "move")
            {
                Cursor new_cursor = 
                new Cursor(drag_and_drop_and_delete.Properties.Resources.move__cursor.Handle);

                this.Cursor = new_cursor;
            }

            else if (super_action.ToString() == "point")
            {
                Cursor new_cursor = 
                new Cursor(drag_and_drop_and_delete.Properties.Resources.point__cursor.Handle);

                this.Cursor = new_cursor;
            }
        }

 


10-在Form1中,使用OnPaint来显示平移窗体时点周围的圆圈和线条轮廓。

我们将在Form1中使用OnPaint函数,以便在用户平移窗体时使用移动工具时,保留线条的轮廓。

因此在Form1OnPaint函数中

之前我们只在super_formsuper_action = move时才绘制轮廓。

但现在我们也会(所以我们会使用“或”逻辑)在old_action = move时绘制,因为当手形工具被选中时,平移窗体时,旧动作会保存之前的工具,所以如果它是移动工具,当用户使用手形工具时,轮廓也会可见。

  protected override void OnPaint(PaintEventArgs e)
        {
            Graphics g = e.Graphics;

            g.TranslateTransform(this.AutoScrollPosition.X, this.AutoScrollPosition.Y);

            int counter_line = 0;
            foreach (lines l in lines_list)
            {
                if (      
                      super_form.super_action.Equals(action.move) ||
                      super_form.old_action.Equals(action.move)
                    )
                {
                    Pen selection_pen_line = new Pen(Brushes.Blue, 2);
                    g.DrawPath(selection_pen_line, l.outline);
                }

           .

           .//old code

           .

       }

同样的概念也适用于当用户同时使用point_mover_toolhand工具时查看线条的点。

之前我们只在super_action = point时绘制线条的点,但现在我们增加了一个新条件(使用“或”逻辑),即使用old_action = point

        protected override void OnPaint(PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            g.TranslateTransform(this.AutoScrollPosition.X, this.AutoScrollPosition.Y);
            int counter_line = 0;

            foreach (lines l in lines_list)
            {
              if (      
                      super_form.super_action.Equals(action.move) ||
                      super_form.old_action.Equals(action.move)
                    )
                {
                    Pen selection_pen_line = new Pen(Brushes.Blue, 2);
                    g.DrawPath(selection_pen_line, l.outline);
                }

               
                g.DrawPath(l.pen, l.path_line);
                g.FillPath(Brushes.Black, l.arrow_path_line);

                if (
                    super_form.super_action.Equals(action.point) ||
                    super_form.old_action.Equals(action.point)
                    )
                {
                    int count = 0;

                    foreach (GraphicsPath circle in l.circle_points_list)
                    {

                           .

                           .//old code

                           .

           }

 

如果你喜欢这个教程,请点赞 微笑 | :) 

© . All rights reserved.