C# 画面キャプチャ範囲をユーザーが選択するためのFORMを使ったフレーム

#画面キャプチャ時のフレーム
デスクトップ画面のキャプチャをするときに、
ユーザーが選択範囲を選んで位置を確認するためのフレームをC#のFormにて作成

![B.JPG](https://qiita-image-store.s3.amazonaws.com/0/174938/47c4fae4-effe-5ed7-704f-3e4d5420fb91.jpeg)

###--- ユーザー操作 ---
+ ドラッグで移動
+ ドラッグで拡大縮小（呼び出し側で切り替え可能）

#サンプルコード（呼び出し側）
```
using System;
using System.Drawing;
using System.Windows.Forms;
using CaptureAreaSelector;

namespace testForm
{
    public partial class Form1 : Form
    {

        FrameForm frame;

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            frame = new FrameForm();

            frame.ShowInTaskbar = false; //タスクバーに表示させない

            frame.FrameBorderSize = 10; //線の太さ

            frame.FrameColor = Color.Blue; //線の色

            frame.AllowedTransform = true; //サイズ変更の可否

            frame.Show();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            if (frame!=null)
                MessageBox.Show(frame.SelectedWindow.ToString());
        }
    }
}

```

#サンプルコード
```
using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;

namespace CaptureAreaSelector
{

    public partial class FrameForm : Form
    {
        private static readonly Color PrimaryTransparencyKey = Color.White;
        private static readonly Color SecondaryTransparencyKey = Color.Black;

        /// <summary>
        /// 選択範囲の位置と範囲です。
        /// </summary>
        [Category("配置"), Description("選択範囲の位置と範囲です")]
        public Rectangle SelectedWindow
        {
            get
            {
                var rect = this.Bounds;
                rect.Inflate(_FrameBorderSize * -1, _FrameBorderSize * -1);
                return rect;
            }
        }

        private Color _FrameColor = Color.Red;
        /// <summary>
        /// フレームの色です
        /// </summary>
        [Category("表示"), Description("フレームの色です")]
        [DefaultValue(typeof(Color), "Red")]
        public Color FrameColor {
            get { return _FrameColor; }
            set
            {
                _FrameColor = value;
                if (_FrameColor == PrimaryTransparencyKey)
                    this.TransparencyKey = SecondaryTransparencyKey;
                else
                    this.TransparencyKey = PrimaryTransparencyKey;
                this.Refresh();
            }
        }

        private int _FrameBorderSize = 5;
        /// <summary>
        /// フレームの線の太さです
        /// </summary>
        [Category("表示"), Description("フレームの線の太さです")]
        [DefaultValue(5)]
        public int FrameBorderSize
        {
            get { return _FrameBorderSize; }
            set
            {
                _FrameBorderSize = value;
                this.Refresh();
            }
        }

        /// <summary>
        /// フレームの変形を許可します
        /// </summary>
        [Category("動作"), Description("フレームの変形を許可します")]
        [DefaultValue(true)]
        public bool AllowedTransform { get; set; } = true;

        Point mousePoint; //マウス位置の一時記憶

        public FrameForm()
        {
            InitializeComponent();
            FormBorderStyle = FormBorderStyle.None;
            this.TransparencyKey = PrimaryTransparencyKey;
        }

        /// <summary>
        /// 枠だけ残して透明にする
        /// </summary>
        /// <param name="g"></param>
        void draw(Graphics g)
        {
            var rct = this.ClientRectangle;
            g.FillRectangle(new SolidBrush(FrameColor), rct);
            rct.Inflate(FrameBorderSize * -1, FrameBorderSize * -1);
            g.FillRectangle(new SolidBrush(TransparencyKey), rct);            
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);

            if ((e.Button & MouseButtons.Left) == MouseButtons.Left)
            {
                //位置を記憶する
                mousePoint = new Point(e.X, e.Y);
            }
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            if (AllowedTransform)
            {
                if (e.X > Width * 2 / 3 && e.Y > Height * 1 / 2) //カーソルが右下なら
                    frameTransform(e);
                else
                    frameMove(e);
            }
            else
                frameMove(e);

            base.OnMouseMove(e);
        }

        void frameMove(MouseEventArgs e)
        {
            Cursor = Cursors.SizeAll;

            if ((e.Button & MouseButtons.Left) == MouseButtons.Left)
            {
                this.Left += e.X - mousePoint.X;
                this.Top += e.Y - mousePoint.Y;
            }
        }

        void frameTransform(MouseEventArgs e)
        {
            if (AllowedTransform)
            {
                Cursor = Cursors.SizeNWSE;

                if ((e.Button & MouseButtons.Left) == MouseButtons.Left)
                {

                    this.Width += e.X - mousePoint.X;
                    mousePoint.X += e.X - mousePoint.X;

                    this.Height += e.Y - mousePoint.Y;
                    mousePoint.Y += e.Y - mousePoint.Y;
                }
            }
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            draw(e.Graphics);
            base.OnPaint(e);
        }

        protected override void OnSizeChanged(EventArgs e)
        {
            base.OnSizeChanged(e);
            this.Refresh();
        }
    }
}
```
※呼び出し側と呼び出され側で別プロジェクトになっています


#課題
1.サイズを拡大するときに残像が残る
　→解決しました、o2kazuma様ありがとうございます。
2.サイズ変更可否の切り替えに無理やり感
　→サイズ変更可否のプロパティは派生クラスに乗せて、OnMouseMoveメソッドをオーバーライドした方がよい？？
3.ドラッグで移動させるためのMouseイベントがいくつか並んでいるが、どうもすっきりしない（Controlに対してたまに使う機能群なので）
　→すっきり一まとめにする良い方法はないものか

#作ってみて
このフレームは、キャプチャのための枠取りとして何となく作ったが、他にもいろいろと応用が利きそう。呼び出し側FormのPictureBoxに投影させてみたり。
そこまで考えると、実はcontrolクラスから継承させたほうがよい？わからん。
とりあえず、コントロールを作るのは楽しい。