c# GDI + simple drawing

Keywords: C# Back-end

Author: stg609

The copyright of this article belongs to the author and the blog park. Reprint is welcome, but this statement must be retained without the consent of the author, and the original text connection must be given in an obvious position on the article page, otherwise the right to investigate legal responsibility is reserved

In the previous articles, I have introduced you how to use GDI + to draw, and made an example of screenshot. In this article, I will introduce you how to make a drawing tool similar to windows
Personally, if you want to be a powerful drawing tool, it is not enough to simply master GDI. At present, I can only be a relatively simple drawing tool. Welcome to discuss the shortcomings!
Let's take a look at the final effect first:
  
Main functions: draw lines, rectangles, erasers, circles, switch colors, open pictures, save pictures, clear pictures, and manually adjust the size of canvas; When the software is just started, it is a blank canvas. We can draw directly on the canvas, or import a picture through "open" in the menu, and then we can draw on this picture.
Platform: VS2005 WINFORM

Because there are too many codes, here we only briefly introduce the production steps and provide you with project download
1. Layout the whole interface
2. Realize the function of drawing tool
3. Realize the function of color picking. Here we directly use the custom control written last time
4. Realize menu function
5. Realize the function of manually adjusting the canvas size
6. Test

Realize the function of drawing tool

In order to reduce the coupling degree of the code, some design patterns are used. Because they are not very good at it, the code is still a little messy. Hey hey! All these function blocks about drawing tools are written in the DrawTools class. Then in the main form, you only need to call this class to complete drawing without too much involving specific drawing code. The main tools provided by this class are pencil, eraser, straight line, rectangle, circle, solid rectangle and solid circle. The code of these function blocks is not difficult. As long as you have carefully read the previous articles, you should understand them.
Here we should pay attention to the following points:
1. How to prevent unnecessary traces in the drawing process from being recorded?
This question was mentioned in the third article. You might as well go and have a look at that one first. In order to make the code look readable, I set two Image variables. Finishing img is used to save the traces in the drawing process, and orginalImg is used to save the completed drawing process and the initial background picture.
2. How does this class communicate with the main form?
Of course, if you write these function blocks directly in the main form, there is naturally no problem. But then the code will appear very mixed. If there is only a problem with the tool code, the whole project needs to be changed. Here, I define methods and properties, let the main form pass the information of Sketchpad canvas and color to this tool class by assigning values to the properties, and then use these tools by calling the corresponding tool methods.
3. Key attributes
In order for these tools to work properly, he must be passed the following things: the target palette (that is, the picturebox), the drawing color, and the original canvas.

Realize menu function
  
Here we need to have a little understanding of the operation of the document. You can check the relevant information.
The main difficulty is the implementation of the menu item "open"
If we want to save the opened picture again after modification, we must make the file close after opening, otherwise the original file cannot be overwritten because the file is opened. "GDI general error" will pop up at compile time. Therefore, according to the practice of other friends on the Internet, first draw the open picture to another canvas through GDI +, and then close the open picture and the drawing board used to draw the picture in time. See details http://www.wanxin.org/redirect.php?tid=3&goto=lastpost

private void openPic_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();//Instantiate file open dialog box
            ofd.Filter = "JPG|*.jpg|Bmp|*.bmp|All documents|*.*";//The setup dialog box opens the filename of the file
            if (ofd.ShowDialog() == DialogResult.OK)
            {
                Bitmap bmpformfile = new Bitmap(ofd.FileName);//Get open file
                panel2.AutoScrollPosition = new Point(0,0);//Reset scroll bar
                pbImg.Size = bmpformfile.Size;//Resize drawing area to picture size

                reSize.Location = new Point(bmpformfile.Width, bmpformfile.Height);//reSize is used by me to manually adjust the canvas size
                //Because the size of the blank canvas is limited initially, the "open" operation may cause the size of the palette to change, so we need to transfer the palette back to the tool class
                dt.DrawTools_Graphics = pbImg.CreateGraphics();

                Bitmap bmp = new Bitmap(pbImg.Width, pbImg.Height);
                Graphics g = Graphics.FromImage(bmp);
                g.FillRectangle(new SolidBrush(pbImg.BackColor), new Rectangle(0, 0, pbImg.Width, pbImg.Height));//Without this sentence, the background of this bmp is transparent
                g.DrawImage(bmpformfile, 0, 0,bmpformfile.Width,bmpformfile.Height);//Draw the picture on the drawing board
                g.Dispose();//Free up resources occupied by Sketchpad
                //pbImg.Image = Image.FormFile(ofd.FileName) is not used directly because it will keep the picture open, so the modified picture cannot be saved
                bmpformfile.Dispose();//Free up resources occupied by pictures
                g = pbImg.CreateGraphics();
                g.DrawImage(bmp, 0, 0);
                g.Dispose();
                dt.OrginalImg = bmp;
                bmp.Dispose();
                sFileName = ofd.FileName;//Save the detailed path of the open picture file, which can be used to overwrite this file later
                ofd.Dispose();

            }
        }

Clearing the image is actually filling the entire canvas with white
Others are relatively simple, which is not specific.

Manually adjust the canvas size

Some people on the Internet say to use API, but I think it's easier to use other controls to help. At least we can understand it.
Idea: place a picturebox1 (size 5 * 5), fix it in the lower right corner of the main drawing board, then change the Cursor when the mouse enters into the arrow shape, set the event when the mouse is pressed and moved, and let the picturebox1 move with the mouse. When the mouse is released, adjust the coordinates of the lower right corner of the main drawing board to the coordinates of picturebox1.
Here's the code:
Among them, reSize is the picturebox control we use to help

private bool bReSize = false;//Change canvas size
        private void reSize_MouseDown(object sender, MouseEventArgs e)
        {
            bReSize = true;//When the mouse is pressed, it indicates that you want to start resizing
        }

        private void reSize_MouseMove(object sender, MouseEventArgs e)
        {
            if (bReSize)
            {
                reSize.Location = new Point(reSize.Location.X + e.X, reSize.Location.Y + e.Y);

            }
        }

        private void reSize_MouseUp(object sender, MouseEventArgs e)
        {
            bReSize = false;//End of size change
            //Adjusting the size may cause the size of the drawing board to exceed the screen area, so set autoScroll to true in advance
            //But the appearance of the scroll bar increases our difficulty, because moving the scroll bar up and down does not automatically help us adjust the coordinates of the picture.
            //This is because there is more than one coordinate system in GDI drawing. It seems that there are three without careful understanding. One is the screen coordinate, one is the client area coordinate, and the other is the document coordinate.
            //Moving the scroll bar up and down changes the coordinates of the document, but the coordinates of the customer area remain unchanged, and the location attribute belongs to the coordinates of the customer area, so we will make an error in direct calculation
            //At this time, we need to know the offset between the document coordinates and the coordinates of the customer area, which is what AutoScrollPostion can provide

            pbImg.Size = new Size(reSize.Location.X - (this.panel2.AutoScrollPosition.X), reSize.Location.Y - (this.panel2.AutoScrollPosition.Y));
            dt.DrawTools_Graphics = pbImg.CreateGraphics();//Because the size of the sketchpad has been changed, it must be re assigned

            //In addition, the canvas has also been changed, so it needs to be re assigned
            Bitmap bmp = new Bitmap(pbImg.Width, pbImg.Height);
            Graphics g = Graphics.FromImage(bmp);
            g.FillRectangle(new SolidBrush(Color.White), 0, 0, pbImg.Width, pbImg.Height);
            g.DrawImage(dt.OrginalImg, 0, 0);
            g.Dispose();
            g = pbImg.CreateGraphics();
            g.DrawImage(bmp, 0, 0);
            g.Dispose();
            dt.OrginalImg = bmp;

            bmp.Dispose();
        }

The effect is shown in the figure below (look carefully at the lower right corner of the white area):

At this point, you can adjust the size of the picture by dragging the small square.
  
In this way, the main problems have almost been solved, but there are still deficiencies. You are welcome to put forward your valuable opinions.
Drawing engineering

Posted by qing on Wed, 20 Oct 2021 17:45:08 -0700