自作したスクロールバー付きのピクチャーボックスに矩形だけでなく円や直線を描画する機能を追加します。それから矩形や円、直線を描画する処理は別のクラスに分離することを考えます。クラスが変に肥大化すると管理が難しくなります(もうすでになっているかも)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public partial class ScrollPictureBox : UserControl { public ScrollPictureBox() { InitializeComponent(); pictureBox1.Location = new Point(0,0); pictureBox1.SizeMode = PictureBoxSizeMode.AutoSize; this.AutoScroll = true; this.BorderStyle = BorderStyle.Fixed3D; // やめる //pictureBox1.MouseDown += PictureBox1_MouseDown; //pictureBox1.MouseUp += PictureBox1_MouseUp; //pictureBox1.MouseMove += PictureBox1_MouseMove; } } |
ドラッグされた部分に図形を描画するクラスを示します。
Shapeでドラッグされた部分に何を描画するのかを設定できるようにします。
1 2 3 4 5 6 7 8 9 |
public enum Shape { None, // なにも描画しない DrawRectangle, // 矩形 FillRectangle, // 塗りつぶされた矩形 DrawEllipse, // 楕円 FillEllipse, // 塗りつぶされた楕円 DrawLine, // 直線 } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 |
public class DrawByDrag { PictureBox _pictureBox = null; public DrawByDrag(PictureBox pictureBox) { _pictureBox = pictureBox; } public Shape Shape = Shape.None; // ドラッグが完了したので描画される図形の大きさを固定する public bool IsFixed = true; // ドラッグされたときに図形を表示するか public bool _doesShow = true; public bool DoesShow { get { return _doesShow; } set { _doesShow = value; _pictureBox.Invalidate(); } } // ドラッグされたときに表示される図形や直線の色 public Color LineColor = Color.Empty; // ドラッグされたときに表示される線の太さ public int LineWidth = 1; // PictureBox上のドラッグが開始された点 Point _startPoint = new Point(); public Point StartPoint { get { return _startPoint; } set { DoesShow = true; _startPoint = value; _endPoint = value; // 前のEndPointによって描画されるのを防ぐ IsFixed = false; } } // PictureBox上の現在のマウスの座標 public Point _endPoint = new Point(); public Point EndPoint { get { return _endPoint; } set { // 固定されている場合はなにもしない if (IsFixed) return; int x = value.X; int y = value.Y; // 座標が0未満のときは0とする if (x < 0) x = 0; if (y < 0) y = 0; // 座標がpictureBoxの幅や高さより大きいときは幅や高さにあわせる if (_pictureBox != null) { if (x > _pictureBox.Width) x = _pictureBox.Width; if (y > _pictureBox.Height) y = _pictureBox.Height; } _endPoint = new Point(x, y); } } // StartPointとEndPointからこれを囲む矩形をもとめる public Rectangle GetRectangle() { int x = StartPoint.X; if (StartPoint.X > EndPoint.X) x = EndPoint.X; int y = StartPoint.Y; if (StartPoint.Y > EndPoint.Y) y = EndPoint.Y; int width = Math.Abs(StartPoint.X - EndPoint.X); int height = Math.Abs(StartPoint.Y - EndPoint.Y); if (width == 0 || height == 0) return Rectangle.Empty; return new Rectangle(x, y, width, height); } // Shapeを調べて適切な図形を描画する public void Draw(Graphics g) { if (DoesShow == false) return; if (Shape == Shape.DrawRectangle) DrawRectangle(g); if (Shape == Shape.FillRectangle) FillRectangle(g); if (Shape == Shape.DrawEllipse) DrawEllipse(g); if (Shape == Shape.FillEllipse) FillEllipse(g); if (Shape == Shape.DrawLine) DrawLine(g); } // 境界線の太さも考慮してGetRectangle()により得られた範囲から外に出ないように矩形を描画する public void DrawRectangle(Graphics g) { Rectangle rect = GetRectangle(); if (LineWidth * 2 >= rect.Width) { g.FillRectangle( new SolidBrush(LineColor), new Rectangle(rect.X, rect.Y, rect.Width, rect.Height) ); } else { g.DrawRectangle( new Pen(LineColor, LineWidth), new Rectangle(rect.X + LineWidth / 2, rect.Y + LineWidth / 2, rect.Width - LineWidth, rect.Height - LineWidth) ); } } public void FillRectangle(Graphics g) { Rectangle rect = GetRectangle(); g.FillRectangle(new SolidBrush(LineColor), new Rectangle(rect.X, rect.Y, rect.Width, rect.Height)); } // 境界線の太さも考慮してGetRectangle()により得られた範囲から外に出ないように楕円を描画する public void DrawEllipse(Graphics g) { Rectangle rect = GetRectangle(); if (LineWidth * 2 >= rect.Width) { g.FillEllipse( new SolidBrush(LineColor), new Rectangle(rect.X, rect.Y, rect.Width, rect.Height) ); } else { g.DrawEllipse( new Pen(LineColor, LineWidth), new Rectangle(rect.X + LineWidth / 2, rect.Y + LineWidth / 2, rect.Width - LineWidth, rect.Height - LineWidth) ); } } public void FillEllipse(Graphics g) { Rectangle rect = GetRectangle(); g.FillEllipse(new SolidBrush(LineColor), new Rectangle(rect.X, rect.Y, rect.Width, rect.Height)); } public void DrawLine(Graphics g) { g.DrawLine(new Pen(LineColor, LineWidth), StartPoint.X, StartPoint.Y, EndPoint.X, EndPoint.Y); } } |
ScrollPictureBoxクラスにおける処理を示すまえに、新しいイベントハンドラの引数になるPictureBoxMouseEventArgsクラスを示します。これはマウスボタンが押された座標と離された座標、移動した座標(それぞれについてPictureBox上の座標とBitmap上のPixsel座標)を知らせるためのものです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
public class PictureBoxMouseEventArgs { public PictureBoxMouseEventArgs(MouseEventArgs e, double expansionrate, Point startPoint) { PositionX = e.X; PositionY = e.Y; PositionMoveX = e.X - startPoint.X; PositionMoveY = e.Y - startPoint.Y; PixelX = (int)Math.Round(e.X / expansionrate); PixelY = (int)Math.Round(e.Y / expansionrate); PixelMoveX = (int)Math.Round(PositionMoveX / expansionrate); PixelMoveY = (int)Math.Round(PositionMoveY / expansionrate); } public int PositionX { get; protected set; } = 0; public int PositionY { get; protected set; } = 0; public int PositionMoveX { get; protected set; } = 0; public int PositionMoveY { get; protected set; } = 0; public int PixelX { get; protected set; } = 0; public int PixelY { get; protected set; } = 0; public int PixelMoveX { get; protected set; } = 0; public int PixelMoveY { get; protected set; } = 0; } |
ScrollPictureBoxクラスを示します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
public partial class ScrollPictureBox : UserControl { public ScrollPictureBox() { InitializeComponent(); pictureBox1.Location = new Point(0,0); pictureBox1.SizeMode = PictureBoxSizeMode.AutoSize; this.AutoScroll = true; this.BorderStyle = BorderStyle.Fixed3D; // 以下の3つはやめる //pictureBox1.MouseDown += PictureBox1_MouseDown; //pictureBox1.MouseUp += PictureBox1_MouseUp; //pictureBox1.MouseMove += PictureBox1_MouseMove; // 代わりに・・・・ pictureBox1.MouseDown += PictureBox1_MouseDown1; pictureBox1.MouseUp += PictureBox1_MouseUp1; pictureBox1.MouseMove += PictureBox1_MouseMove1; pictureBox1.Paint += PictureBox1_Paint; this.AllowDrop = true; // DrawByDragのインスタンスを生成 DrawByDrag = new DrawByDrag(pictureBox1); DrawByDrag.LineColor = Color.Black; DrawByDrag.LineWidth = 1; // 最初はなにも描画させない DrawByDrag.Shape = Shape.None; } public DrawByDrag DrawByDrag = null; // 新しいイベントとイベントハンドラ public delegate void PictureBoxMouseDownHandler1(object sender, PictureBoxMouseEventArgs e); public event PictureBoxMouseDownHandler1 PictureBoxMouseDown1; public event PictureBoxMouseDownHandler1 PictureBoxMouseMove1; public event PictureBoxMouseDownHandler1 PictureBoxMouseUp1; // マウスがクリックされたらDrawByDrag.StartPointをセット // それと同時にクリックされた位置(PictureBox上の座標とPixel座標)をイベントで通知する private void PictureBox1_MouseDown1(object sender, MouseEventArgs e) { DrawByDrag.StartPoint = e.Location; PictureBoxMouseDown1?.Invoke(this, new PictureBoxMouseEventArgs(e, Expansionrate, DrawByDrag.StartPoint)); } // マウスが移動したらDrawByDrag.EndPointをセット // それと同時にマウスの位置(PictureBox上の座標とPixel座標)をイベントで通知する private void PictureBox1_MouseMove1(object sender, MouseEventArgs e) { DrawByDrag.EndPoint = e.Location; // マウスがドラッグされた範囲を表示させるためにInvalidate()を実行する pictureBox1.Invalidate(); PictureBoxMouseMove1?.Invoke(this, new PictureBoxMouseEventArgs(e, Expansionrate, DrawByDrag.StartPoint)); } // マウスボタンが離されたらDrawByDrag.EndPointをセット // マウスボタンが離されたので描画される矩形等の位置を固定する // それと同時にマウスの位置(PictureBox上の座標とPixel座標)をイベントで通知する private void PictureBox1_MouseUp1(object sender, MouseEventArgs e) { DrawByDrag.EndPoint = e.Location; DrawByDrag.IsFixed = true; // マウスがドラッグされた範囲を表示させるためにInvalidate()を実行する pictureBox1.Invalidate(); if (FullScaleBitmap != null) { // FullScaleBitmap上の座標を取得する // FinishedDragRectangleイベントでFullScaleBitmapのトリミングされた部分を通知する Rectangle rect = DrawByDrag.GetRectangle(); int x = (int)Math.Round(rect.X / Expansionrate); int y = (int)Math.Round(rect.Y / Expansionrate); int width = (int)Math.Round(rect.Width / Expansionrate); int height = (int)Math.Round(rect.Height / Expansionrate); Rectangle rectangle = new Rectangle(x, y, width, height); if (rectangle.Width > 0 && rectangle.Height > 0) FinishedDragRectangle?.Invoke(this, new TrimmedBitmapArgs(FullScaleBitmap, rectangle)); } PictureBoxMouseUp1?.Invoke(this, new PictureBoxMouseEventArgs(e, Expansionrate, DrawByDrag.StartPoint)); } public delegate void PaintEventHandler(object sender, PaintEventArgs e); public event PaintEventHandler PictureBoxPaint; private void PictureBox1_Paint(object sender, PaintEventArgs e) { // Shape.None以外のときはマウスドラッグされているのであればその部分を描画する DrawByDrag.Draw(e.Graphics); PictureBoxPaint?.Invoke(this, e); } } |
これで試運転をしてみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
public partial class Form1 : Form { public Form1() { InitializeComponent(); numericUpDownLineWidth.Value = 1; radioButtonNone.Checked = true; } private void numericUpDownLineWidth_ValueChanged(object sender, EventArgs e) { scrollPictureBox1.DrawByDrag.DoesShow = false; scrollPictureBox1.DrawByDrag.LineWidth = (int)numericUpDownLineWidth.Value; } private void radioButtonDrawRectangle_CheckedChanged(object sender, EventArgs e) { scrollPictureBox1.DrawByDrag.DoesShow = false; scrollPictureBox1.DrawByDrag.Shape = ScrollPictureBoxLib.Shape.DrawRectangle; } private void radioButtonFillRectangle_CheckedChanged(object sender, EventArgs e) { scrollPictureBox1.DrawByDrag.DoesShow = false; scrollPictureBox1.DrawByDrag.Shape = ScrollPictureBoxLib.Shape.FillRectangle; } private void radioButtonDrawEllipse_CheckedChanged(object sender, EventArgs e) { scrollPictureBox1.DrawByDrag.DoesShow = false; scrollPictureBox1.DrawByDrag.Shape = ScrollPictureBoxLib.Shape.DrawEllipse; } private void radioButtonFillEllipse_CheckedChanged(object sender, EventArgs e) { scrollPictureBox1.DrawByDrag.DoesShow = false; scrollPictureBox1.DrawByDrag.Shape = ScrollPictureBoxLib.Shape.FillEllipse; } private void radioButtonDrawLine_CheckedChanged(object sender, EventArgs e) { scrollPictureBox1.DrawByDrag.DoesShow = false; scrollPictureBox1.DrawByDrag.Shape = ScrollPictureBoxLib.Shape.DrawLine; } private void radioButtonNone_CheckedChanged(object sender, EventArgs e) { scrollPictureBox1.DrawByDrag.DoesShow = false; scrollPictureBox1.DrawByDrag.Shape = ScrollPictureBoxLib.Shape.None; } private void numericUpDown1_ValueChanged(object sender, EventArgs e) { scrollPictureBox1.Expansionrate = (double)numericUpDown1.Value / 100; } } |