【C#】【Form】PictureBox [7] ~ 画像をコピーする・その2 ~

■ はじめに

https://blogs.yahoo.co.jp/dk521123/37857445.html
で、画像をクリップボードにコピーしたが、
Graphicsで描画した図形も含めてコピーはできない。

今回は、Graphicsで描画した図形も含めてコピーする方法について記載する。

■ 解決案

 * コピー直前の対象インスタンス(下記でいうと「selectedImage」)に対して、
   画面に描画している内容(下記でいう「this.DeawText()」の内部)を再度行う。

処理イメージ

using (var image = new Bitmap(pictureBox.Image))
using (var selectedImage = image.Clone(copyArea, image.PixelFormat))
using (var graphics = Graphics.FromImage(selectedImage)) // ★注目★
{
  // ★ここでPaintイベントで描画している内容と同じ内容を処理を行う
  // ただし、切り取っているため、描画位置を補正する必要がある。

  // 画像をクリップボードにコピー
  Clipboard.SetImage(selectedImage);
}

■ サンプル

 以下の関連記事のサンプル「【2-3】DrawString()とInvalidate()を使う(【1-1】の発展版)」のサンプル
https://blogs.yahoo.co.jp/dk521123/37890831.html
と 以下の関連記事のサンプル「例2:矩形で囲ってリリースしたタイミングでコピー」を組み合わせて
Graphics.DrawString() で描画した文字列もクリップボードのコピー対象にするサンプルを作ってみた。
https://blogs.yahoo.co.jp/dk521123/37857445.html

コンポーネント構成

 * PictureBox x 1
 * GroupBox x 1
  + radioButton x 2 (コピー選択モード切り替え)

コード

using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;

namespace SampleForm
{
  public partial class Form1 : Form
  {
    private Point targetPoint;
    private bool isDragging = false;
    private SizeF textSize;
    private int? dx;
    private int? dy;

    // コピー選択モードかどうか
    bool isSelectMode = false;
    // コピー用変数
    private Point selectingStartPoint = Point.Empty;
    private Rectangle selectingCopyArea = Rectangle.Empty;

    public Form1()
    {
      InitializeComponent();

      this.targetPoint = Point.Empty;
      this.isDragging = false;
    }

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

      var clickPoint = e.Location;
      if (this.isSelectMode)
      {
        // 選択モードの場合
        this.selectingStartPoint = clickPoint;
        return;
      }

      var mouseDownPoint = clickPoint;
      this.isDragging = true;
      if (this.targetPoint.IsEmpty ||
        !this.IsOnTextArea(mouseDownPoint, this.targetPoint, this.textSize))
      {
        this.targetPoint = mouseDownPoint;
      }
      else
      {
        this.dx = this.targetPoint.X - mouseDownPoint.X;
        this.dy = this.targetPoint.Y - mouseDownPoint.Y;

        var x = mouseDownPoint.X + this.dx.Value;
        var y = mouseDownPoint.Y + this.dy.Value;
        this.targetPoint = new Point(x, y);
      }

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

    private void pictureBox1_Paint(object sender, PaintEventArgs e)
    {
      if (!this.selectingStartPoint.IsEmpty && !this.selectingCopyArea.IsEmpty)
      {
        var pen = new Pen(Color.Red, 2)
        {
          DashStyle = DashStyle.Dot
        };
        var graphics = e.Graphics;
        graphics.DrawRectangle(pen, this.selectingCopyArea);
      }

      if (this.targetPoint.IsEmpty)
      {
        return;
      }

      var pictureBox = sender as PictureBox;
      this.DrawText(e.Graphics, this.targetPoint.X, this.targetPoint.Y);
    }

    private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
    {
      if (this.isSelectMode)
      {
        var pictureBox = sender as PictureBox;

        var start = this.selectingStartPoint;
        var end = e.Location;
        var copyArea = Rectangle.FromLTRB(
            Math.Min(start.X, end.X), Math.Min(start.Y, end.Y),
            Math.Max(start.X, end.X), Math.Max(start.Y, end.Y));
        if (copyArea.Width == 0 || copyArea.Height == 0)
        {
          return;
        }

        using (var image = new Bitmap(pictureBox.Image))
        using (var selectedImage = image.Clone(copyArea, image.PixelFormat))
        using (var graphics = Graphics.FromImage(selectedImage))
        {
          // ★ここで描画する★
          this.DrawText(graphics, this.targetPoint.X - start.X, this.targetPoint.Y - start.Y);

          // 画像をクリップボードにコピー
          Clipboard.SetImage(selectedImage);
        }

        pictureBox.Invalidate();
        this.selectingStartPoint = Point.Empty;
        this.selectingCopyArea = Rectangle.Empty;

        return;
      }

      this.isDragging = false;
      this.dx = null;
      this.dy = null;
    }

    private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
    {
      var pictureBox = sender as PictureBox;

      if (this.isSelectMode)
      {
        if (this.selectingStartPoint.IsEmpty)
        {
          return;
        }

        var start = this.selectingStartPoint;
        var end = e.Location;
        this.selectingCopyArea = Rectangle.FromLTRB(
            Math.Min(start.X, end.X), Math.Min(start.Y, end.Y),
            Math.Max(start.X, end.X), Math.Max(start.Y, end.Y));

        pictureBox.Invalidate();

        return;
      }

      if (this.targetPoint.IsEmpty)
      {
        return;
      }

      var mousePoint = e.Location;
      bool isOnTextArea = this.IsOnTextArea(mousePoint, this.targetPoint, this.textSize);
      if (isOnTextArea)
      {
        pictureBox.Cursor = Cursors.SizeAll;
      }
      else
      {
        pictureBox.Cursor = Cursors.Default;
      }

      if (!this.isDragging)
      {
        return;
      }

      if (this.dx == null || this.dy == null)
      {
        this.targetPoint = mousePoint;
      }
      else
      {
        var x = mousePoint.X + this.dx.Value;
        var y = mousePoint.Y + this.dy.Value;
        this.targetPoint = new Point(x, y);
      }
      pictureBox.Invalidate();
    }

    // コピー選択モードをオフ
    private void radioButton1_CheckedChanged(object sender, EventArgs e)
    {
      this.isSelectMode = false;

      // カーソルを変える
      this.pictureBox1.Cursor = Cursors.Default;
      this.selectingStartPoint = Point.Empty;
      this.selectingCopyArea = Rectangle.Empty;
    }

    // コピー選択モードをオン
    private void radioButton2_CheckedChanged(object sender, EventArgs e)
    {
      this.isSelectMode = true;

      // カーソルを変える
      this.pictureBox1.Cursor = Cursors.Cross;
      this.selectingStartPoint = Point.Empty;
      this.selectingCopyArea = Rectangle.Empty;
    }

    private void DrawText(Graphics graphics, int x, int y)
    {
      var text = "Hello World!";
      graphics.DrawString(text, this.Font, Brushes.Black, x, y);
      this.textSize = graphics.MeasureString(text, this.Font);
    }

    private bool IsOnTextArea(Point targetPoint, Point textPoint, SizeF textSize)
    {
      const int Margin = 5;
      var point = new Point(textPoint.X - Margin, textPoint.Y - Margin);
      var textArea = new Rectangle(point, textSize.ToSize());
      return textArea.Contains(targetPoint);
    }
  }
}

関連記事

PictureBox

PictureBox [1] ~ 画像を表示する ~
https://blogs.yahoo.co.jp/dk521123/23504075.html
PictureBox [2] ~ PictureBox を マウスで移動する ~
https://blogs.yahoo.co.jp/dk521123/37861699.html
PictureBox [3] ~ マウスホイール で画像の拡大・縮小する ~
https://blogs.yahoo.co.jp/dk521123/37866101.html
PictureBox [4] ~ PictureBox 内に文字列を描画する ~
https://blogs.yahoo.co.jp/dk521123/37890831.html
PictureBox [5] ~ PictureBox 内に画像を描画する ~
https://blogs.yahoo.co.jp/dk521123/37890873.html
PictureBox [6] ~ PictureBox 内でMouseMoveイベントにより線を描画する ~
https://blogs.yahoo.co.jp/dk521123/37905014.html
PictureBox [7] ~ 画像をコピーする・その1 ~
https://blogs.yahoo.co.jp/dk521123/37857445.html

その他

Windows Form ~ 目次 ~
https://blogs.yahoo.co.jp/dk521123/8054245.html
クリップボード
https://blogs.yahoo.co.jp/dk521123/7518189.html