【C#】Graphics ~ さまざまな描画 ~

■ 描画

画像の描画

 * Graphics.DrawImage() を使用
https://docs.microsoft.com/ja-jp/dotnet/api/system.drawing.graphics.drawimage?view=netframework-4.8
  => 描画したいImageオブジェクトと、描画する位置、サイズを指定
  => 詳細は、以下の関連記事を参照
PictureBox [5] ~ PictureBox 内に画像を描画する ~
https://blogs.yahoo.co.jp/dk521123/37890873.html

線の描画

 * 方法がいくかある

【1】DrawLine() で描画する
【2】ControlPaint.DrawReversibleLine() で描画する

四角の描画

 * FillRectangle() で描画する
 * 余計な枠を塗りつぶすのに、以下の関連記事で使用した。
https://blogs.yahoo.co.jp/dk521123/38010582.html
サンプル
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
  e.Graphics.FillRectangle(
    new SolidBrush(Color.Gray),
    new Rectangle(x, y, width, height));
}

円の描画

 * FillEllipse() で描画する
注意
 * 引数は「半径」ではなく「幅、高さ」なので使いにくい
 * 以下のサイトによい解決方法あり。
https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q11173767351
より

/// <summary>
/// x,yを中心とした半径radiusの円を書く
/// </summary>
/// <param name="graphics">graphicsオブジェクト</param>
/// <param name="brush">brushオブジェクト</param>
/// <param name="x">x座標</param>
/// <param name="y">y座標</param>
/// <param name="radius">半径</param>
/// <see cref="">https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q11173767351"/>
private void FillCircle(Graphics graphics, Brush brush, float x, float y, float radius)
{
  graphics.FillEllipse(brush, x - radius, y - radius, radius * 2, radius * 2);
}

■ サンプル

例1:Hello World

Form1.cs
public partial class Form1 : Form
{
    Graphics graphics;
    int s1 = 0, s2 = 0;

    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Paint(object sender, PaintEventArgs e)
    {
        this.graphics.FillPie(Brushes.Brown, this.ClientRectangle, this.s1, this.s2);
        this.s1++;
        this.s2++;
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        this.DoubleBuffered = true;
        this.graphics = this.CreateGraphics();
    }

    private void timer1_Tick(object sender, EventArgs e)
    {
        this.OnPaint(null);
    }
}

例2:基本形(円を描画)

 * PictureBox x 1
 * Button x 1
 を配置

 * 以下を参考。
http://www.ect.niihama-nct.ac.jp/~coji/doc/VCS2010_02p.pdf
Form1.cs
using System;
using System.Drawing;
using System.Windows.Forms;

namespace ImageViewer
{
  public partial class Form1 : Form
  {
    private Bitmap bitmap;
    private Graphics graphics;
    private Pen pen;

    public Form1()
    {
      InitializeComponent();

      this.bitmap = new Bitmap(this.pictureBox1.Width, this.pictureBox1.Height);
      this.pictureBox1.Image = this.bitmap;
      this.graphics = Graphics.FromImage(this.pictureBox1.Image);
      this.pen = new Pen(Color.Black, 3);
    }

    private void button1_Click(object sender, EventArgs e)
    {
      this.graphics.Clear(Color.White);
      this.graphics.DrawEllipse(this.pen, 50, 100, 150, 200);
      this.pictureBox1.Refresh();
    }
  }
}

例3:画像にクリックしたらその場所に二重丸を描画する

 * PictureBox x 1 を配置
Form2.cs
using System;
using System.Drawing;
using System.Windows.Forms;

namespace SampleForm
{
  public partial class Form2 : Form
  {
    public Form2()
    {
      InitializeComponent();
    }

    private void pictureBox1_MouseDown(object sender, MouseEventArgs mouseEventArgs)
    {
      var targetPoint = new Point(mouseEventArgs.X, mouseEventArgs.Y);
      using (Graphics graphics = this.pictureBox1.CreateGraphics())
      {
        this.FillCircle(graphics, Brushes.White, targetPoint.X, targetPoint.Y, 10);
        this.FillCircle(graphics, Brushes.SkyBlue, targetPoint.X, targetPoint.Y, 8);
      }
    }

    /// <summary>
    /// x,yを中心とした半径radiusの円を書く
    /// </summary>
    /// <param name="graphics">graphicsオブジェクト</param>
    /// <param name="brush">brushオブジェクト</param>
    /// <param name="x">x座標</param>
    /// <param name="y">y座標</param>
    /// <param name="radius">半径</param>
    /// <see cref="">https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q11173767351"/>
    private void FillCircle(Graphics graphics, Brush brush, float x, float y, float radius)
    {
      graphics.FillEllipse(brush, x - radius, y - radius, radius * 2, radius * 2);
    }
  }
}

例4:マウスを追いかける

 * 例3の発展版。
注意
 * このサンプルは、VS2017/Windows10で実行したら、うまくうごかなかった。
   改良したサンプルは、以下の関連記事を参照。
https://blogs.yahoo.co.jp/dk521123/37905014.html
Form1.cs
using System;
using System.Drawing;
using System.Windows.Forms;

namespace SampleForm
{
  public partial class Form1 : Form
  {
    private Image sourceImage;

    private Point startPoint = Point.Empty;
    private Point endPoint = Point.Empty;
    private Point previousPoint = Point.Empty;

    public Form1()
    {
      InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
      this.sourceImage = this.pictureBox1.Image;
    }

    private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
    {
      if (e.Button != MouseButtons.Left)
      {
        // 右クリックは無視
        return;
      }

      var targetPoint = e.Location;

      if (!this.startPoint.IsEmpty && !this.endPoint.IsEmpty)
      {
        // Clear
        this.Clear();
      }
      else if (!this.startPoint.IsEmpty && this.endPoint.IsEmpty)
      {
        this.endPoint = targetPoint;
      }
      else
      {
        this.startPoint = targetPoint;
        this.endPoint = Point.Empty;
      }

      var pictureBox = sender as PictureBox;
      pictureBox.Invalidate();
    }

    private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
    {
      if (!this.startPoint.IsEmpty && this.endPoint.IsEmpty)
      {
        var pictureBox = sender as PictureBox;

        var targetPoint = e.Location;
        if (!this.previousPoint.IsEmpty)
        {
          ControlPaint.DrawReversibleLine(
            pictureBox.PointToScreen(this.startPoint),
            pictureBox.PointToScreen(this.previousPoint),
            Color.White);
        }

        ControlPaint.DrawReversibleLine(
          pictureBox1.PointToScreen(this.startPoint),
          pictureBox.PointToScreen(targetPoint),
          Color.White);
        this.previousPoint = targetPoint;
      }
      else
      {
        this.previousPoint = Point.Empty;
      }
    }

    private void pictureBox1_Paint(object sender, PaintEventArgs e)
    {
      var graphics = e.Graphics;
      if (!this.startPoint.IsEmpty)
      {
        this.FillCircle(graphics, Brushes.White, this.startPoint.X, this.startPoint.Y, 10);
        this.FillCircle(graphics, Brushes.SkyBlue, this.startPoint.X, this.startPoint.Y, 8);
      }

      if (!this.endPoint.IsEmpty)
      {
        this.FillCircle(graphics, Brushes.White, this.endPoint.X, this.endPoint.Y, 10);
        this.FillCircle(graphics, Brushes.LightGreen, this.endPoint.X, this.endPoint.Y, 8);

        graphics.DrawLine(new Pen(Color.LightGreen, 3), this.startPoint, this.endPoint);
      }
    }

    private void Clear()
    {
      this.startPoint = Point.Empty;
      this.endPoint = Point.Empty;
      this.previousPoint = Point.Empty;

      this.pictureBox1.Image = this.sourceImage;
    }

    /// <summary>
    /// x,yを中心とした半径radiusの円を書く
    /// </summary>
    /// <param name="graphics">graphicsオブジェクト</param>
    /// <param name="brush">brushオブジェクト</param>
    /// <param name="x">x座標</param>
    /// <param name="y">y座標</param>
    /// <param name="radius">半径</param>
    /// <see cref="">https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q11173767351"/>
    private void FillCircle(Graphics graphics, Brush brush, float x, float y, float radius)
    {
      graphics.FillEllipse(brush, x - radius, y - radius, radius * 2, radius * 2);
    }
  }
}


関連記事

PictureBox

PictureBox [1] ~ 画像を表示する ~
https://blogs.yahoo.co.jp/dk521123/23504075.html
PictureBox [5] ~ PictureBox 内に画像を描画する ~
https://blogs.yahoo.co.jp/dk521123/37890873.html
PictureBox [6] ~ PictureBox 内でMouseMoveイベントにより線を描画する ~
https://blogs.yahoo.co.jp/dk521123/37905014.html