《C# GDI+ 破境之道》:第一境 GDI+基础 —— 第一节:画直线

今天正式开一本新书,《C# GDI+ 破镜之道》,一样是破镜之道系列丛书的一分子。html

关于GDI+呢,官方的解释是这样的:编程

GDI+ 是 Microsoft Windows 操做系统的窗体子系统应用程序编程接口 (API)。 GDI+ 是负责在屏幕和打印机上显示的信息。 顾名思义,GDI+ 是包含 GDI 与早期版本的 Windows 图形设备接口的后续版本。

 好,两个关键信息:app

  1. 窗体子系统应用的编程接口
  2. 图形设备接口

充分说明了GDI+的应用场景与用途。须要了解更多呢,就去查阅一下吧。dom

本书的开始,不打算去解释一些枯燥的概念,好比什么是Graphics、Brush、Pen甚至是Color;第一境毕竟是基础,我打算先带你们玩儿,等玩儿开了、玩儿嗨了,我们再来总结这些概念,就会至关好理解了。我们就先从最基本的画元素开始吧:)ide

本节,主要是说道一下如何使用GDI+画直线。体育老师说了,两点肯定一条直线,那么,画直线的关键呢,就是肯定两个点了。音乐老师也说了,直线呢,是向两边无限延长的,木有尽头。那咱们仍是别挑战无极限了,因此,我们在这里说的画直线呢,实际上是画线段。函数

这是我创建的一个简单的WinForm窗体(FormDrawLines)。 摆了几个按钮,用来绘制各类不一样的线条以及展现不一样线条的特性。this

两个辅助按钮,用来切换线条的颜色和窗体是否使用双缓冲。spa

 1 using System;  2 using System.Collections.Generic;  3 using System.Drawing;  4 using System.Drawing.Drawing2D;  5 using System.Windows.Forms;  6 
 7 public partial class FormDrawLines : Form  8 {  9     private Random random = null; 10     private Color penColor = Color.Transparent; 11     private Point lastMouseDownLocation = Point.Empty; 12     private bool startDrawPointToPointLine = false; 13     private bool startDrawFollowMouseLine = false; 14 
15     public FormDrawLines() 16  { 17  InitializeComponent(); 18         random = new Random(DateTime.Now.Millisecond); 19         penColor = Color.White; 20  } 21 
22 …… 23 
24 }
命名空间引用、私有变量及构造函数

几个辅助方法,不是本节重点,这里简单说明一下用途,一笔带过:P操作系统

一、获取画布中的一个随机点code

1 private Point GetRandomPoint() 2 { 3     return new Point(random.Next(0, ClientRectangle.Width), random.Next(0, ClientRectangle.Height - pnlToolbox.Height)); 4 }
获取随机点 —— GetRandomPoint

二、显示信息,其中,lblInformation为一个Label控件。

1 private void ShowInformation(string message) 2 { 3     lblInformation.Text = message; 4 }
显示信息 —— ShowInformation

三、切换线条颜色,其中,colors为ColorDialog组件。

1 private void btnChangePenColor_Click(object sender, EventArgs e) 2 { 3     if (colors.ShowDialog(this) == DialogResult.OK) 4  { 5         penColor = colors.Color; 6  } 7 }
切换线条颜色 —— btnChangePenColor_Click

四、切换是否使用双缓冲

1 private void btnSwitchDoubleBuffered_Click(object sender, EventArgs e) 2 { 3     DoubleBuffered = !DoubleBuffered; 4 
5     ShowInformation($"二级缓冲:{DoubleBuffered}。"); 6 }
切换是否使用双缓冲 —— btnSwitchDoubleBuffered_Click

下面是本节的重点:

一、随机画线

 1 private void btnDrawRandomLine_Click(object sender, EventArgs e)  2 {  3     var pointA = GetRandomPoint();  4     var pointB = GetRandomPoint();  5 
 6     using (var g = CreateGraphics())  7     using (var pen = new Pen(penColor, 2f))  8  {  9  g.Clear(SystemColors.AppWorkspace); 10  g.DrawLine(pen, pointA, pointB); 11  } 12 
13     ShowInformation($"画随机线,{pointA}->{pointB}。"); 14 }
随机画线 —— btnDrawRandomLine_Click

g.Clear(SystemColors.AppWorkspace); 是用来清屏的。

关键方法是

//
// Summary: // Draws a line connecting two System.Drawing.Point structures. //
// Parameters: // pen: // System.Drawing.Pen that determines the color, width, and style of the line. //
// pt1: // System.Drawing.Point structure that represents the first point to connect. //
// pt2: // System.Drawing.Point structure that represents the second point to connect. //
// Exceptions: // T:System.ArgumentNullException: // pen is null.
public void DrawLine(Pen pen, Point pt1, Point pt2);
Graphics.DrawLine 方法原型

这是画线的最基础方法,给一根笔、两个点,就能够在画布上做画了:)

  • 笔,决定了线的颜色及粗细;
  • 两点,决定了线的位置及长度;

应该不难理解。

二、消除锯齿

经过“随机画线”,咱们发现,画出的线边缘锯齿状严重,垂直和水平线还好,带点角度就惨不忍睹了。还好,GDI+为咱们提供了一系列消除锯齿的选项,虽然有时也很难差强人意,不过总的来讲仍是能够接受的。

 1 private void btnDrawSmoothLine_Click(object sender, EventArgs e)  2 {  3     var pointA = GetRandomPoint();  4     var pointB = GetRandomPoint();  5     var mode = (SmoothingMode)(random.Next(0, 5));  6 
 7     using (var g = CreateGraphics())  8     using (var pen = new Pen(penColor, 2f))  9  { 10  g.Clear(SystemColors.AppWorkspace); 11         g.SmoothingMode = mode; 12  g.DrawLine(pen, pointA, pointB); 13  } 14 
15     ShowInformation($"消除锯齿,{pointA}->{pointB},模式:{mode.ToString()}。"); 16 }
消除锯齿 —— btnDrawSmoothLine_Click

关键点在于g.SmoothingMode = mode;为了尽可能多的展现平滑模式带来的效果,mode来自于System.Drawing.Drawing2D.SmoothingMode的随机取值;

 1 //
 2 // Summary:  3 // Specifies whether smoothing (antialiasing) is applied to lines and curves and  4 // the edges of filled areas.
 5 public enum SmoothingMode  6 {  7     //
 8     // Summary:  9     // Specifies an invalid mode.
10     Invalid = -1, 11     //
12     // Summary: 13     // Specifies no antialiasing.
14     Default = 0, 15     //
16     // Summary: 17     // Specifies no antialiasing.
18     HighSpeed = 1, 19     //
20     // Summary: 21     // Specifies antialiased rendering.
22     HighQuality = 2, 23     //
24     // Summary: 25     // Specifies no antialiasing.
26     None = 3, 27     //
28     // Summary: 29     // Specifies antialiased rendering.
30     AntiAlias = 4
31 }
System.Drawing.Drawing2D.SmoothingMode

严格来说,消除锯齿并不属于画线的范畴,在画其余图形元素时一样有效,他归属于GDI+的2D渲染质量,指定是否将平滑(抗锯齿)应用于直线和曲线以及填充区域的边缘。这一点,须要明确。

 三、画虚线

 1 private void btnDrawDashLine_Click(object sender, EventArgs e)  2 {  3     var pointA = GetRandomPoint();  4     var pointB = GetRandomPoint();  5     var style = (DashStyle)(random.Next(1, 5));  6 
 7     using (var g = CreateGraphics())  8     using (var pen = new Pen(penColor, 2f))  9  { 10  g.Clear(SystemColors.AppWorkspace); 11         g.SmoothingMode = SmoothingMode.HighQuality; 12         pen.DashStyle = style; 13  g.DrawLine(pen, pointA, pointB); 14  } 15 
16     ShowInformation($"画虚线,{pointA}->{pointB},样式:{style.ToString()}。"); 17 }
画虚线 —— btnDrawDashLine_Click

画虚线的关键点在于制定笔的样式:pen.DashStyle = style;一样,为了多展现集中样式,style取了枚举的随机值;

//
// Summary: // Specifies the style of dashed lines drawn with a System.Drawing.Pen object.
public enum DashStyle { //
    // Summary: // Specifies a solid line.
    Solid = 0, //
    // Summary: // Specifies a line consisting of dashes.
    Dash = 1, //
    // Summary: // Specifies a line consisting of dots.
    Dot = 2, //
    // Summary: // Specifies a line consisting of a repeating pattern of dash-dot.
    DashDot = 3, //
    // Summary: // Specifies a line consisting of a repeating pattern of dash-dot-dot.
    DashDotDot = 4, //
    // Summary: // Specifies a user-defined custom dash style.
    Custom = 5 }
System.Drawing.Drawing2D.DashStyle

没有取0和5,Solid = 0为实线,Custom = 5为自定义样式,咱们在第一境里先不介绍这类自定义的用法,容易玩儿不嗨……

四、画线冒

这是一个很朴实的需求,好比我想画一个链接线,一头带箭头,或者两头都带箭头,又或者一头是圆点另外一头是箭头等。常常被问到,其实在GDI+中,很是容易实现,甚至还能够指定虚线的线冒,可爱:)

 1 private void btnDrawLineCap_Click(object sender, EventArgs e)  2 {  3     var pointA = GetRandomPoint();  4     var pointB = GetRandomPoint();  5     var style = (DashStyle)(random.Next(0, 6));  6     var lineCaps = new List<int> { 0, 1, 2, 3, 16, 17, 18, 19, 20, 240 };  7     var dashCaps = new List<int> { 0, 2, 3 };  8     var startCap = (LineCap)lineCaps[random.Next(0, 10)];  9     var endCap = (LineCap)lineCaps[random.Next(0, 10)]; 10     var dashCap = (DashCap)dashCaps[random.Next(0, 3)]; 11 
12     using (var g = CreateGraphics()) 13     using (var pen = new Pen(penColor, 4f)) 14  { 15  g.Clear(SystemColors.AppWorkspace); 16         g.SmoothingMode = SmoothingMode.HighQuality; 17         pen.DashStyle = style; 18  pen.SetLineCap(startCap, endCap, dashCap); 19  g.DrawLine(pen, pointA, pointB); 20  } 21 
22     ShowInformation($"画线冒,{pointA}->{pointB},起点线冒:{startCap.ToString()},终点线冒:{endCap.ToString()},虚线冒:{dashCap.ToString()},线条样式:{style.ToString()}。"); 23 }
画线冒 —— btnDrawLineCap_Click

关键点在于pen.SetLineCap(startCap, endCap, dashCap);一样,startCap, endCap分别取了System.Drawing.Drawing2D.LineCap的随机值;dashCap取了System.Drawing.Drawing2D.DashCap的随机值;

//
// Summary: // Specifies the available cap styles with which a System.Drawing.Pen object can // end a line.
public enum LineCap { //
    // Summary: // Specifies a flat line cap.
    Flat = 0, //
    // Summary: // Specifies a square line cap.
    Square = 1, //
    // Summary: // Specifies a round line cap.
    Round = 2, //
    // Summary: // Specifies a triangular line cap.
    Triangle = 3, //
    // Summary: // Specifies no anchor.
    NoAnchor = 16, //
    // Summary: // Specifies a square anchor line cap.
    SquareAnchor = 17, //
    // Summary: // Specifies a round anchor cap.
    RoundAnchor = 18, //
    // Summary: // Specifies a diamond anchor cap.
    DiamondAnchor = 19, //
    // Summary: // Specifies an arrow-shaped anchor cap.
    ArrowAnchor = 20, //
    // Summary: // Specifies a mask used to check whether a line cap is an anchor cap.
    AnchorMask = 240, //
    // Summary: // Specifies a custom line cap.
    Custom = 255 }
System.Drawing.Drawing2D.LineCap
//
// Summary: // Specifies the type of graphic shape to use on both ends of each dash in a dashed // line.
public enum DashCap { //
    // Summary: // Specifies a square cap that squares off both ends of each dash.
    Flat = 0, //
    // Summary: // Specifies a circular cap that rounds off both ends of each dash.
    Round = 2, //
    // Summary: // Specifies a triangular cap that points both ends of each dash.
    Triangle = 3 }
System.Drawing.Drawing2D.DashCap

一样,咱们也能够经过分别设置pen的StartCap、EndCap、DashCap属性来达到相同目的;

 

好了,到这里呢,关于线的基本画法就已经所有介绍完了,感受有点EZ? BORED?那么咱们就来利用现有的知识,耍个花活?

五、点点连线

这里比简单的画线,稍微复杂一点点,须要两个事件配合:

 1 private void btnDrawPointToPointLine_Click(object sender, EventArgs e)  2 {  3     startDrawPointToPointLine = true;  4     lastMouseDownLocation = Point.Empty;  5 
 6     using (var g = CreateGraphics())  7  {  8  g.Clear(SystemColors.AppWorkspace);  9  } 10 
11     ShowInformation($"点点连线,等待起点(鼠标单击画布内任意位置)。"); 12 }
点点连线 —— btnDrawPointToPointLine_Click
 1 private void FormDrawLines_MouseDown(object sender, MouseEventArgs e)  2 {  3     if (startDrawPointToPointLine)  4  {  5         if (Point.Empty.Equals(lastMouseDownLocation))  6  {  7             lastMouseDownLocation = e.Location;  8             ShowInformation($"点点连线,起点:{lastMouseDownLocation},等待终点(鼠标单击画布内任意位置)。");  9  } 10         else
11  { 12             using (var g = CreateGraphics()) 13             using (var pen = new Pen(penColor, 2f)) 14  { 15  g.Clear(SystemColors.AppWorkspace); 16                 g.SmoothingMode = SmoothingMode.HighQuality; 17  g.DrawLine(pen, lastMouseDownLocation, e.Location); 18  } 19 
20             ShowInformation($"点点连线,{lastMouseDownLocation}->{e.Location}。"); 21 
22             startDrawPointToPointLine = false; 23             lastMouseDownLocation = Point.Empty; 24  } 25  } 26 }
点点连线 —— FormDrawLines_MouseDown

原理很简单,当咱们点击“点点连线”按钮的时候,激活标记位startDrawPointToPointLine、归位lastMouseDownLocation,并提示须要鼠标操做,选择一个起始点;

 

当咱们在画布区域内单击一个下,就触发了FormDrawLines_MouseDown事件, 它会判断,当startDrawPointToPointLine处于激活状态而且lastMouseDownLocation处于原位时,它就把鼠标的当前位置赋值给lastMouseDownLocation,做为线段的起始点位置,并提示须要鼠标操做,选择一个终点;

当咱们再次在画布区域内单击一个下,就又触发了FormDrawLines_MouseDown事件, 它会判断,当startDrawPointToPointLine处于激活状态而且lastMouseDownLocation不处于原位时,它就把鼠标的当前位置做为线段的终点位置,并画出线段;而后就是恢复startDrawPointToPointLine为未激活状态,并归位 lastMouseDownLocation;

恐怕要很是适应这种多事件配合的方式了,由于鼠标跟随也是多事件配合一块儿玩儿的:P

六、鼠标跟随

在点点连线的基础上,咱们把标记位换成了startDrawFollowMouseLine;同时,增长了FormDrawLines_MouseMove事件;

 1 private void FormDrawLines_MouseMove(object sender, MouseEventArgs e)  2 {  3     if (startDrawFollowMouseLine && !Point.Empty.Equals(lastMouseDownLocation))  4  {  5         using (var g = CreateGraphics())  6         using (var pen = new Pen(penColor, 2f))  7  {  8  g.Clear(SystemColors.AppWorkspace);  9             g.SmoothingMode = SmoothingMode.HighQuality; 10  g.DrawLine(pen, lastMouseDownLocation, e.Location); 11  } 12 
13         ShowInformation($"鼠标跟随,{lastMouseDownLocation}->{e.Location}。"); 14  } 15 }
鼠标跟随 —— FormDrawLines_MouseMove

原理也不难,就是在选了起点之后,鼠标的移动事件会把鼠标的当前位置做为终点,重绘线段,以达到跟随的效果;因为截图也看不出动态效果,就不上图了,有兴趣的童鞋能够Run代码看看效果:)

 

Okay,关于GDI+画线的部分,咱们就到此告一段落了。

 

篇外话

这里涉及了坐标系,美术老师说:

横坐标,坐标原点左为负,坐标原点右为正,从左到右愈来愈大;

纵坐标,坐标原点下为负,坐标原点上为正,从下到上愈来愈大;

可是在GDI+的世界坐标系里,纵坐标的描述正好相反;而且坐标原点初始时在画布的左上角,而不是画布的中央; 用心体会一下:)

 

 

喜欢本系列丛书的朋友,能够点击连接加入QQ交流群(994761602)【C# 破境之道】
方便各位在有疑问的时候能够及时给我个反馈。同时,也算是给各位志同道合的朋友提供一个交流的平台。
须要源码的童鞋,也能够在群文件中获取最新源代码。

原文出处:https://www.cnblogs.com/mikecheers/p/12329610.html

相关文章
相关标签/搜索