Winform 动态 画图 不闪
一、问题:解决winform动态画图闪的问题,网上搜的方法,大部分都是:
- “this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);”,甚至直接“this.DoubleBuffered = true;”。
- 先 new 个Bitmap,画在Bitmap上,然后再把Bitmap画在界面上。
凡是直接这么给人解答问题的,基本都是属于道听途说,自己没试过的。或者根本就没注意要解决的是“动态”的问题。
二、解决方法:动态画图不闪的方法如下,先上效果图(请忽略鼠标样式,是gif录制软件的效果):
三、代码:简单封了个自定义控件,用Action传入画图方法:
1 // -------------------------------------------------------------------------------------------------------------------- 2 // <copyright file="PictureBoxEx.cs" company="hyl"> 3 // hyl 4 // </copyright> 5 // <summary> 6 // 用Action传画图方法。不闪。 7 // </summary> 8 // -------------------------------------------------------------------------------------------------------------------- 9 10 namespace HYL 11 { 12 using System; 13 using System.Drawing; 14 using System.Windows.Forms; 15 16 public partial class PictureBoxEx : PictureBox 17 { 18 /// <summary> 19 /// 画图方法 20 /// </summary> 21 private Action<Graphics> draw; 22 23 public PictureBoxEx() 24 { 26 this.InitializeComponent(); 27 28 // 开双缓存(用这种方法,画图不太复杂的话,甚至不开也不闪。。。) 29 this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true); 30 } 31 32 public void Rander(Action<Graphics> Draw) 33 { 34 this.Invalidate(); 35 this.draw = Draw; 36 } 37 38 protected override void OnPaint(PaintEventArgs pe) 39 { 40 base.OnPaint(pe); 41 42 // 画图 43 this.draw?.Invoke(pe.Graphics); 44 } 45 } 46 }
重点在于要在 “OnPaint” 执行画图代码,也就是说要用 “OnPaint” 里的 “pe.Graphics” 来画。
四、调用的方式如下:
1 namespace HYL 2 { 3 using System; 4 using System.Collections.Generic; 5 using System.Drawing; 6 using System.Linq; 7 using System.Windows.Forms; 8 9 public partial class Form1 : Form 10 { 11 List<Line> lines = new List<Line>(); 12 13 private bool painting; 14 15 public Form1() 16 { 17 this.InitializeComponent(); 18 } 19 20 private void panel1_MouseDown(object sender, MouseEventArgs e) 21 { 22 if (e.Button == MouseButtons.Left) 23 { 24 // 左键确定点 25 if (this.btnLine.Checked) 26 { 27 this.lines.Last().Points.Add(new LinePoint { IsTemp = false, Point = e.Location }); 28 this.painting = true; 29 } 30 } 31 32 if (e.Button == MouseButtons.Right) 33 { 34 // 右键停止画图 35 if (this.btnLine.Checked) 36 { 37 this.ClearEmptyLines(); 38 } 39 40 this.painting = false; 41 this.btnLine.Checked = false; 42 } 43 } 44 45 private void ClearEmptyLines() 46 { 47 this.lines = this.lines.Where(l => l.Points.Count > 1).ToList(); 48 if (this.lines.Count > 0) 49 { 50 var lastLine = this.lines.Last(); 51 lastLine.ClearTempPoints(); 52 } 53 } 54 55 private void panel1_MouseMove(object sender, MouseEventArgs e) 56 { 57 if (this.painting) 58 { 59 if (this.btnLine.Checked) 60 { 61 this.PaintingLine(e); 62 } 63 64 this.Draw(); 65 } 66 } 67 68 private void PaintingLine(MouseEventArgs e) 69 { 70 var lastLine = this.lines.Last(); 71 var lastPoint = lastLine.Points.Last(); 72 73 if (lastPoint.IsTemp) 74 { 75 lastLine.Points.Remove(lastPoint); 76 } 77 78 LinePoint newPoint = new LinePoint { IsTemp = true, Point = e.Location }; 79 lastLine.Points.Add(newPoint); 80 } 81 82 /// <summary> 83 /// 画图 84 /// </summary> 85 private void Draw() 86 { 87 Action<Graphics> draw = g => 88 { 89 Pen pen = new Pen(Color.Black, 2); 90 g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; 91 g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality; 92 93 if (this.lines != null) 94 { 95 foreach (Line line in this.lines) 96 { 97 g.DrawLines(pen, line.GetPoints().ToArray()); 98 } 99 } 100 }; 101 102 this.pictureBoxEx1.Rander(draw); 103 } 104 105 private void btnLine_CheckedChanged(object sender, EventArgs e) 106 { 107 if (this.btnLine.Checked) 108 { 109 this.lines.Add(new Line()); 110 } 111 else 112 { 113 this.ClearEmptyLines(); 114 this.painting = false; 115 this.Draw(); 116 } 117 } 118 } 119 120 public class Line : ShapeElement 121 { 122 public Line() 123 { 124 this.Points = new List<LinePoint>(); 125 } 126 127 // 线里的点 128 public IList<LinePoint> Points { get; set; } 129 130 // 获取Point的集合 131 public IList<Point> GetPoints() 132 { 133 return this.Points.Select(p => p.Point).ToList(); 134 } 135 136 // 清理临时点 137 public void ClearTempPoints() 138 { 139 this.Points = this.Points.Where(p => !p.IsTemp).ToList(); 140 } 141 } 142 143 public class LinePoint 144 { 145 public Point Point { get; set; } 146 147 // 是否临时点 148 public bool IsTemp { get; set; } 149 } 150 }