【C#】マウスで画像範囲を選択して、ドラッグで移動する【ペイント】

ペイントツールでよくある範囲選択（切り取り）機能です
前回のプログラムに付け足すような形で追加しています

https://qiita.com/Hnming/items/57a6a67864bebf5d56ae

![Animation.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2666090/edba7f88-1389-6fa2-ed43-209dfec3bb16.gif)

```C#
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Runtime.Remoting;
using System.Windows.Forms;

namespace ImageEditing
{
    public partial class frmPaintTool : Form
    {
        // 変数
        Bitmap _newBitMap;              // ビットマップ画像
        Bitmap _oldBitMap;              // ビットマップ画像
        Bitmap _selectBitMap;           // ビットマップ画像
        bool _mouseDrug;                // マウスクリック中のフラグ
        bool _moveFirst;                // 初回の画像移動のフラグ
        bool _moveImage;                // 画像移動中のフラグ
        int _prevX;                     // 前のマウスカーソルのX座標
        int _prevY;                     // 前のマウスカーソルのY座標
        int _startX;                    // スタートのマウスカーソルのX座標
        int _startY;                    // スタートのマウスカーソルのY座標
        int _selectX;                   // 選択中の範囲のX座標
        int _selectY;                   // 選択中の範囲のY座標
        int _penType;                   // ペンモード
        Rectangle _selectedRect;        // 選択範囲の記憶

        public frmPaintTool()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            // BitMapオブジェクトを生成する
            // 背景色として白で塗りつぶす
            _newBitMap = new Bitmap(DrawingPicBox.Width, DrawingPicBox.Height);
            Graphics objGrp = Graphics.FromImage(_newBitMap);
            SolidBrush objBrush = new SolidBrush(Color.White);
            objGrp.FillRectangle(objBrush, 0, 0, _newBitMap.Width, _newBitMap.Height);
            objGrp.Dispose();
            objBrush.Dispose();

            // ビットマップ画像をコピー
            _oldBitMap = (Bitmap)_newBitMap.Clone();
        }


        private void DrawingPicBox_MouseDown(object sender, MouseEventArgs e)
        {
            // マウスクリック開始判定
            _mouseDrug = true;
            
            if (_penType == 2)
            {
                // 選択ツールが選択されている場合
                // マウスカーソルの位置を取得する
                Point clickPoint = new Point(e.X, e.Y);                 
                if (_selectedRect.Contains(clickPoint) == true)         
                {
                    // マウスカーソルが前回の選択範囲の中でクリックされたとき
                    if (_moveFirst == true)
                    {
                        PixelFormat format = _newBitMap.PixelFormat;
                        // 範囲の画像を切り取る
                        _selectBitMap = _newBitMap.Clone(_selectedRect, format);                  
                    }
                    // 移動モードに移行する
                    _moveImage = true;                                  
                }
                else                                                    
                {
                    // マウスカーソルが範囲の外を選択したとき
                    if (_moveImage == true)                             
                    {
                        // 画像を移動していたとき
                        Graphics objGrp = Graphics.FromImage(_oldBitMap);
                        // 変更を確定する
                        // DrawImage()で、選択された画像_selectBitMapを指定した位置に描画する
                        objGrp.DrawImage(_selectBitMap, _selectedRect);    
                        objGrp.Dispose();
                        _selectBitMap.Dispose();
                    }
                    _newBitMap = (Bitmap)_oldBitMap.Clone();
                    _moveFirst = true;
                    _moveImage = false;
                }
                DrawingPicBox.Image = _newBitMap;
            }

            // マウスカーソル位置記憶の初期化
            _prevX = e.Location.X;
            _prevY = e.Location.Y;
            _startX = e.Location.X;
            _startY = e.Location.Y;
        }

        private void DrawingPicBox_MouseUp(object sender, MouseEventArgs e)
        {
            // マウスクリック終了判定
            _mouseDrug = false;

            if (_penType == 2)
            {
                // 選択ツールが選択されている場合
                if (_moveImage == false)                                
                {
                    // 画像範囲選択中のとき
                    if (_startX <= _prevX)
                    {
                        // 選択した画像範囲を_selectedRectに記憶する
                        if (_startY <= _prevY)
                        {
                            _selectedRect = new Rectangle(_startX, _startY, _prevX - _startX, _prevY - _startY);
                            _selectX = _startX;                           // 選択範囲のスタート位置を記憶する
                            _selectY = _startY;
                        }
                        else
                        {
                            _selectedRect = new Rectangle(_startX, _prevY, _prevX - _startX, _startY - _prevY);
                            _selectX = _startX;                           // 選択範囲のスタート位置を記憶する
                            _selectY = _prevY;
                        }
                    }
                    else
                    {
                        if (_startY <= _prevY)
                        {
                            _selectedRect = new Rectangle(_prevX, _startY, _startX - _prevX, _prevY - _startY);
                            _selectX = _prevX;                            // 選択範囲のスタート位置を記憶する
                            _selectY = _startY;
                        }
                        else
                        {
                            _selectedRect = new Rectangle(_prevX, _prevY, _startX - _prevX, _startY - _prevY);
                            _selectX = _prevX;                            // 選択範囲のスタート位置を記憶する
                            _selectY = _prevY;
                        }
                    }
                }
                else
                {
                    // 画像移動中のとき
                    // 移動した後の画像範囲のスタート位置を記憶する
                    _selectX = _selectX + e.Location.X - _startX;           
                    _selectY = _selectY + e.Location.Y - _startY;
                    // 移動した後の画像範囲を_selectedRectに記憶する
                    _selectedRect = new Rectangle(_selectX, _selectY, _selectBitMap.Width, _selectBitMap.Height);
                }
                return;
            }

            // 変更を保存する
            _oldBitMap = (Bitmap)_newBitMap.Clone();
        }

        private void DrawingPicBox_MouseMove(object sender, MouseEventArgs e)
        {
            // マウスクリック中に、BitMapにGraphicsオブジェクトで描画する
            if (_mouseDrug == true)
            {
                // BitMapからGraphicsオブジェクトを生成
                // 選択されているツールの種類によって処理を分ける
                Graphics objGrp = Graphics.FromImage(_newBitMap);
                Pen objPen = null;
                SolidBrush objBrush = null;
                if (_penType == 0)
                {
                    // ペンが選択されている場合
                    objPen = new Pen(Color.Black, 3);
                    objGrp.DrawLine(objPen, _prevX, _prevY, e.Location.X, e.Location.Y);
                    objPen.Dispose();
                }
                else if (_penType == 1)
                {
                    // 四角形が選択されている場合
                    _newBitMap = (Bitmap)_oldBitMap.Clone();
                    objGrp = Graphics.FromImage(_newBitMap);
                    objBrush = new SolidBrush(Color.Black);
                    if (_startX <= e.Location.X)
                    {
                        if (_startY <= e.Location.Y)
                        {
                            objGrp.FillRectangle(objBrush, _startX, _startY, e.Location.X - _startX, e.Location.Y - _startY);
                        }
                        else
                        {
                            objGrp.FillRectangle(objBrush, _startX, e.Location.Y, e.Location.X - _startX, _startY - e.Location.Y);
                        }
                    }
                    else
                    {
                        if (_startY <= e.Location.Y)
                        {
                            objGrp.FillRectangle(objBrush, e.Location.X, _startY, _startX - e.Location.X, e.Location.Y - _startY);
                        }
                        else
                        {
                            objGrp.FillRectangle(objBrush, e.Location.X, e.Location.Y, _startX - e.Location.X, _startY - e.Location.Y);
                        }
                    }
                    objBrush.Dispose();
                }
                else if (_penType == 2)
                {
                    // 選択ツールが選択されている場合
                    // 画像を途中で上書き保存しない
                    _newBitMap = (Bitmap)_oldBitMap.Clone();
                    objGrp = Graphics.FromImage(_newBitMap);
                    if (_moveImage == false)
                    {
                        // 画像範囲選択中のとき
                        objPen = new Pen(Color.Black, 1);
                        objPen.DashStyle = DashStyle.Dash;
                        if (_startX <= e.Location.X)
                        {
                            if (_startY <= e.Location.Y)
                            {
                                objGrp.DrawRectangle(objPen, _startX - 1, _startY - 1, e.Location.X - _startX + 1, e.Location.Y - _startY + 1);
                            }
                            else
                            {
                                objGrp.DrawRectangle(objPen, _startX - 1, e.Location.Y - 1, e.Location.X - _startX + 1, _startY - e.Location.Y + 1);
                            }
                        }
                        else
                        {
                            if (_startY <= e.Location.Y)
                            {
                                objGrp.DrawRectangle(objPen, e.Location.X - 1, _startY - 1, _startX - e.Location.X + 1, e.Location.Y - _startY + 1);
                            }
                            else
                            {
                                objGrp.DrawRectangle(objPen, e.Location.X - 1, e.Location.Y - 1, _startX - e.Location.X + 1, _startY - e.Location.Y + 1);
                            }
                        }
                    }
                    else
                    {
                        // 画像移動中のとき
                        if (_moveFirst == true)
                        {
                            // 初回の画像移動だった場合
                            // 切り取られた範囲を白で塗り潰す
                            objBrush = new SolidBrush(Color.White);
                            objGrp.FillRectangle(objBrush, _selectedRect);
                            objBrush.Dispose();
                            // 画像を更新する
                            _oldBitMap = (Bitmap)_newBitMap.Clone();        
                            _moveFirst = false;
                        }

                        // DrawImage()で、選択された画像_selectBitMapを指定した位置に描画する
                        // DrawRectangle()で、選択範囲の枠を描画する
                        objPen = new Pen(Color.Black, 1);
                        objPen.DashStyle = DashStyle.Dash;
                        objGrp.DrawImage(_selectBitMap, _selectX + e.Location.X - _startX, _selectY + e.Location.Y - _startY, _selectBitMap.Width, _selectBitMap.Height);
                        objGrp.DrawRectangle(objPen, _selectX + e.Location.X - _startX - 1, _selectY + e.Location.Y - _startY - 1, _selectBitMap.Width + 1, _selectBitMap.Height + 1);
                        objPen.Dispose();
                    }
                }

                // BitMapオブジェクトをPictureBoxに表示する
                objGrp.Dispose();
                _prevX = e.Location.X;
                _prevY = e.Location.Y;
                DrawingPicBox.Image = _newBitMap;
            }
        }

        private void Tools_CheckedChanged(object sender, EventArgs e)
        {
            // 現在の選択されているツールの種類を保存
            if(typePen.Checked == true)
            {
                _penType = 0;
            }
            else if(typeSquare.Checked == true)
            {
                _penType = 1;
            }
            else if (typeSelect.Checked == true)
            {
                _penType = 2;
            }
        }
    }
}

```


マウスドラッグで範囲を指定し、選択された範囲の画像を_selectBitMapへコピーしています


DrawImage()でコピーされた_selectBitMap（ビットマップ画像）を、そのまま描画できます
（Rectangleなどで描画する場所とサイズを指定する必要がある）

https://qiita.com/Hnming/items/fe38e3b3d2d4b677226c