【C#】PictureBoxに描画した図形(四角形)を選択する方法

WindowsフォームでPictureBoxに描画した図形を選択したかのように見せる方法を紹介します。

サンプル

出来上がりのイメージがこちらです。
図形を選択したことがわかりやすいように図形の枠を太くみせるように工夫しています。

次にサンプルコードになります。

        // 図形の幅
        private const int RECT_WIDTH = 100;
        // 図形の高さ
        private const int RECT_HEIGHT = 100;
        // 図形の配列(ディクショナリ)
        private Dictionary<Rectangle, string> RectList = new Dictionary<Rectangle, string>();
        // 選択中の図形
        private Rectangle? SelectedRect = null;
        // 選択ラベル
        private Label lblRect = null;

        /// <summary>
        /// フォームロード
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Form1_Load(object sender, EventArgs e)
        {
            Bitmap canvas = new Bitmap(pictureBox1.Width, pictureBox1.Height);
            Graphics g = Graphics.FromImage(canvas);
            Pen p = new Pen(Color.Black, 1);
            Rectangle rect;

            // 図形1を描画
            rect = new Rectangle(50, 50, RECT_WIDTH, RECT_HEIGHT);
            g.DrawRectangle(p, rect); // DrawRectangle(Pen, Rectangle)
            RectList.Add(rect,"図形1");

            // 図形2を描画
            rect = new Rectangle(300, 50, RECT_WIDTH, RECT_HEIGHT);
            g.DrawRectangle(p, rect); // DrawRectangle(Pen, Rectangle)
            RectList.Add(rect,"図形2");

            //リソースを解放
            p.Dispose();
            g.Dispose();

            // PictureBoxに表示
            pictureBox1.Image = canvas;
        }

        /// <summary>
        /// マウスダウンイベント
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
        {
            if (lblRect != null)
            {
                // 選択中ステータスの場合
                // 選択中の図形をクリア
                pictureBox1.Controls.Remove(lblRect);
                lblRect = null;
                SelectedRect = null;
            }

            // 図形の配列分繰り返し
            foreach (Rectangle rect in RectList.Keys)
            {
                if (rect.Contains(e.X, e.Y))
                {
                    // 図形の範囲内をクリックした場合
                    // 選択ラベル生成
                    lblRect = new Label();
                    lblRect.AutoSize = false;
                    lblRect.BackColor = Color.Transparent;
                    lblRect.BorderStyle = BorderStyle.FixedSingle;
                    lblRect.Width = RECT_WIDTH;
                    lblRect.Height = RECT_HEIGHT;
                    lblRect.Left = rect.X;
                    lblRect.Top = rect.Y;
                    pictureBox1.Controls.Add(lblRect);

                    // 選択中の図形を設定
                    SelectedRect = rect;
                    break;
                }
            }

        }

        /// <summary>
        /// マウスアップイベント
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
        {
            if(SelectedRect.HasValue == true)
            {
                // 図形を選択中の場合
                // 選択中の図形情報を取得
                string data = RectList[SelectedRect.Value];

                // 選択中の図形情報を表示
                MessageBox.Show(data + "を選択しました");

                // 選択中の図形をクリア
                pictureBox1.Controls.Remove(lblRect);
                lblRect = null;
                SelectedRect = null;
            }
        }

解説

まず、ロードイベントで図形の描画と同時にRectangle型をKeyに持つDictionaryに情報を格納させておきます。
DictionaryのValue値はお好きなデータ型を指定してください。今回はString型で図形の名前を入れています。
次に、PictureBoxにマウスダウンとマウスアップのイベントを用意します。
マウスダウンイベントでは、ロードイベントで作ったDictionaryのKey配列をループしてクリック位置に図形がないか判定します。
(図形の座標内をクリックしたかはRectangleのContainsメソッドで判定できます)
そして、ここからがこのサンプルのミソなのですがクリック位置に図形があった場合、枠線のみのラベルコントロールを動的に生成して図形の全面に配置してやることで、まるで図形を選択したかのような太い枠線になります。
この状態でマウスアップを行うと選択を確定したと判断して選択中の図形情報をメッセージに表示するという仕組みです。
作ってみると意外と少ないコードで書けるものですね。
四角形ならそもそも描画せずにラベルコントロールを代用することもできますが、コントロールの数が膨大になると表示自体が遅くなるため描画してやる方がシンプルに速いです。
描画系のシステムを作る際はどのような方法で描画するのか事前によく検討した上で決めましょう。

コメントを残す

メールアドレスが公開されることはありません。