質問: hitRects; // PictureBox更新 pictureBox1.Refresh(); } } } } private void pictureBox1_Paint(object sender, PaintEventArgs e) { } } }
回答: マウスドラッグによる移動は概ね以下のような方法になると思います。 * PictureBox内でのMouseDown 押された位置と異動対象の図形を覚える。このとき図形の色を赤に変える * PictureBox内でのMouseMove 直前のマウス位置から現在のマウス位置の差分を移動距離とし、その分だけ各図形を移動。単にマウスを移動させた場合でもドラッグの場合でも両方このイベントが発生してしまうのでハンドラーの中でマウスボタンの状態をチェックしてドラッグであることの確認が必要のようですね(すみません自分も詳しくないので) なおもちろんこのイベントで通知されるマウス位置は時間のために覚えておきます。 * PictureBox内でのMouseUp 異動対象の図形の色を青に戻すなど必要な後始末をする。自分の場合は最後に起こったMoveMoveとMouseUpでマウスの位置が微妙に違うこともあるかもしれないと考えるので一応MouseUpでもMouseMoveと同様の移動処理もやっておきますが、そこまでは必要ないのかも知れません。 これらの処理に必要な情報は「直前のマウスの位置」「異動対象の図形がどれか」といったものになるのでフォームのフィールドとして用意しておけばよいと思います。 * アニメーションの基本的な(やさしい)方法(既にご存知ならスルーしてください) 描画する対象の図形の状態(位置や色)を変えたらその都度その図形を直接書き直すというのはややこしい処理になります。元の位置に書かれていた四角形を消して新たな場所に書くだけだと元の位置と重なる位置に他の四角形があった場合どうするか等々複雑なことを考えなければならないからです。こうした場合の一般的方法として位置や色などを必要な情報を更新したらPictureBox.Invalidate()を呼び出して、「もう一回書き直してね」といってやるのがよいと思います。 なお、図形の移動の実装には直接関係ないのですが、四角形に色々な操作を付け加えるためにオブジェクトとしてまとめた方がよいと思います。機能を追加していくにつれ必要な情報があちこちに散らばってきてわかりにくくなるので、「四角形」を単なるRectangleではなく「PictureBox上に存在するある位置・大きさ・色をもった操作可能な図形」と捉えそれをクラスとして定義すれば、そうしない場合に比べてわかりやすくなるでしょう。 やりたいことによってクラスの定義はいろいろ考えられますが素朴な例を以下に挙げてみます。なお、ドラッグの詳細な実装は含んでいません。また各メンバーのアクセス修飾子やイベントの設定は適宜ご自分で補ってください。 ``` // あるPixcureBoxのImageの中に存在する、移動可能な四角形を表現するクラス public class SquareShape { PictureBox Owner; // 描画する先のPictureBox Rectangle Bounds; // 位置と大きさ Color FillColor; // 色 // 初期位置や色を与えてインスタンスを作る。 public SquareShape(PictureBox owner, int x, int y, Color fillColor) { ... } public bool Contains(Point p) { ... } // 点がこの四角形の内側にあるかテスト public void Draw(Graphics g) { ... } // 描画するpictureBox1_Paint用 // 図形の状態を変えるメソッド群 // 状態を変えるとPictureBoxの再描画が必要になるので各メソッドの中で // owner.Invalidated()を呼び出す。実際の描画は全てpictureBox1_Paintにまかせる public void Move(int dx, int dy) { // 現在の位置からdx,dyだけ移動 bounds = new Rectangle(bounds.x + dx, bounds.y + dy, bounds.Width, bounds.Height); owner.Invalidate(); } public void SetColor(Color color) { // 色を変える this.fillColor = color; owner.Invalidate(); } ... } ... class Form1 ... { List shapeList; void Form1_Load(...) { ... var rnd = new Random(); shapeList = Enumerable.Range(0, 50) .Select(dummy => { // オリジナルコードでは図形位置がランダムになってないので若干変更してます var x = rnd.Next(0, pictureBox1.Width - 100); // 簡単のため100と直接書いてます var y = rnd.Next(0, picutreBox1.Height - 100); return new SquareShape(picuteBox1, x, y, Color.Blue); }) .ToList(); pictureBox1.Image = new Bitmap(pictureBox1.Width, pictureBox1.Height); pictureBox1.Invalidate(); // ここでは必要ないかも } Point lastMouseLocation; // 直前のMouseDown/MoveMoveでのマウス位置 List draggingShapes; // 異動対象の図形リスト void pictureBox1_MouseDown(...) { ... 全部の図形からマウス位置にあるものを探しそれを覚える。それらの色を赤に変える。 } void pictureBox1_MouseMove(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Left) { // 左ボタンでドラッグするならこのように ... 異動対象の図形の位置をマウス位置に従い移動する(SquareShape.Moveで) } } void pictureBox1_MouseUp(...) { ... 移動対象の図形の色を青に戻すなどの後始末 } // PictureBoxのInvalidateを呼び出すと適当なタイミングでこのハンドラーが呼ばれる // 複数の図形の状態を変更してInvalidateを複数回読んでもこのハンドラーは1回しか // 呼ばれませんので何度も何度も無駄に書き直すといったことになる心配はありません void pictureBox1_Paint(...) { Graphics g = Graphics.Graphics.FromImage(pictureBox1.Image); g.Clear(pictureBox1.BackColor); foreach (var sq in shapeList) { sq.Draw(g); } } } ```