| ■103794 / ) |
Re[5]: Visual Basicで簡易CADを作成 |
□投稿者/ 魔界の仮面弁士 (3873回)-(2025/07/30(Wed) 15:46:27)
|
■No103792 (shiro さん) に返信 > 回答有難うございます。VBでの壁画は始めたばかりで分からない点が多く参考になります。 壁画というか、描画かな?
掲示板にコードを投稿する時は、 投稿モード:通常モード / 図表モード の選択肢を、「通常モード」から「図表モード」に変更しておいてください。 そうしないと、空白やタブでのインデントが潰れてしまいますので。
もし、「通常モード」のままでインデントを維持したいのであれば、 連続する半角スペース を 全角スペース に置き換えておくことでも代用できます。 全角空白を混入させられない言語の時は都合が悪いですけれどね。
インデントが崩れていないかを確認したい場合は、右下の送信ボタンの近くにある 「プレビュー」にチェックをいれて、投稿前に事前確認することもできます。
> 画像削除方法を勉強してみます。 たとえば、先のサンプルの最後にある 「マウスをなぞった位置にリアルタイムに四角形を描画し、マウスを離すとその位置に四角形を追加し、後で追加された四角形をクリックすると色が変わる」 について。 https://www.umayadia.com/vbsample/VBdotNet-Samples201/Sample275WinFormMouseToDraw.htm#A5
ここに下記のコードを加えると、選択状態の黄色い矩形が取り除かれます。
Private Sub PictureBox1_DoubleClick(sender As Object, e As EventArgs) Handles PictureBox1.DoubleClick Dim selectedPolygon As Polygon = polygons.FirstOrDefault(Function(p) p.IsSelected) If selectedPolygon IsNot Nothing Then '選択済みの矩形があれば削除 polygons.Remove(selectedPolygon) PictureBox1.Invalidate() 'PictureBoxを強制的に再描画する End If End Sub
もしも元のサンプルを拡張して、複数選択できるようにしていた場合はこのようにします。
Private Sub PictureBox1_DoubleClick(sender As Object, e As EventArgs) Handles PictureBox1.DoubleClick For n = polygons.Count - 1 To 0 Step -1 '削除するとインデックス番号がズレるので、後ろの番号から探していく If polygons(n).IsSelected Then polygons.RemoveAt(n) End If Next PictureBox1.Invalidate() 'PictureBoxを強制的に再描画する End Sub
上記ではダブルクリックで制御しています。 ダブルクリックで削除する方式では操作しにくいという場合は、別途、削除ボタンを用意し、 それを押したときに IsSelected = True なものを取り除く方式にしても良いでしょう。 あるいは、右クリックやコンテキストメニューなどで指示する手法を採用することもできますね。
> そこで、さらに柱を選択し柱を配置すると、全ての壁が消えてしまいます。 たとえば…
最初は、柱が 0 本、壁が 0 枚の状態です。 この場合、Paint イベントでは「方眼グリッドと、0 本の柱と、0 枚の壁」を描画するようにします。
柱を選択して、マウス操作で柱を建てる場所を決めました(MouseUp/MouseDown で座標を決めていきます)。 これにより、柱が 1 本、壁が 0 枚の状態になりました、 この場合、Paint イベントでは「方眼グリッドと、1 本の柱と、0 枚の壁」を描画するようにします。
同様にして、柱を 4 本建てました;左上、右上、左下、右下と正方形の頂点位置にくるように。 この場合、Paint イベントでは「方眼グリッドと、4 本の柱と、0 枚の壁」を描画します。
さらに、壁を 3 枚描きました。上壁、右壁、下壁を作るイメージです。 この場合、Paint イベントでは「方眼グリッドと、4 本の柱と、3 枚の壁」を描画します。壁が【コ】の字に描かれます。
右クリック等で右側の壁を一枚選択して、その壁だけを削除し、再描画を依頼します。 この場合、Paint イベントでは「方眼グリッドと、4 本の柱と、2 枚の壁」を描画します。壁が【=】の字に描かれます。
すなわち、それぞれのオブジェクト(壁や柱)が、どの座標に描かれているのかを List(Of ) 等で保持しておく必要があるということです。これは、Visual Basic 中学校サンプルでいう Private polygons As New List(Of Polygon) のことです。現状はそれが無いため、再描画に耐えられるコードになっていません。
> それをToolStripMenuItemで「柱・壁」をselect文で場合分けをしようとしました。 変数 element の宣言が無いようですが、Form1 上に Private element As String があるのでしょうか?
> g = PictureBox1.CreateGraphics() これは使わないでください。
CreateGraphics は一時的な描画であり、恒久的に描画結果を残すものではありません。 ウィンドウのリサイズなどで容易に失われてしまうものです。 また、Invalidate が呼び出された時点で、CreateGraphics による描画結果は失われることになります。
CreateGraphics の呼び出しは行わないようにして、描画処理を PictureBox1_Paint イベントの引数 e.Graphics に集約させるべきです。
KOZ さんが紹介してくださった Visual Basic 中学校 のサンプルにおいても、 MouseMove/MouseDown 中に Graphics を操作したりはせず、 Graphics での描画処理は、すべて Paint イベント内だけで行っていましたよね。
とはいえ、ラバーバンドのように一時的な描画に対しては、CreateGrapchis でも 用を足せるのですが、その場合も、Dispose の呼び出しは忘れないようにしてください。
Graphics は、Pen や Brush と同様に「破棄が必要なオブジェクト(IDisposable)」です。
CreateGraphics や Graphics.FromImage によって生成した Graphics オブジェクトは、 使用後に処分せねばならない、ということです。Graphics を自分で生成して使うケースでは 保持させる Graphics 変数を Using ブロックで囲んでおくことが望ましいです。
※ただし、Paint イベントの e.Graphics プロパティで得られる Graphics オブジェクトについては 自分で生成したインスタンスではないため、処分してはいけません。
もしも描画する内容が多く、すべてを Paint イベント毎回再描画するとコストが高すぎる場合には、 永続的に表示し続けたい画像を Bitmap として用意する方法を併用できます。
たとえば、描画先のキャンバス(たとえば PictureBox1)と同サイズの Bitmap を Dim bmp As New Bitmap(幅, 高さ) などで作成して Using g = Graphics.FromImage(bmp) … End Using を使って描画した後、PictureBox1.Image = bmp で割り当てるという手法です。
この時、背景画像(PictureBox1.BackgroundImage)と前景画像(PictureBox1.Image)を併用しても構いません。 割り当てた Bitmap の背景部が、完全透過あるいは半透明だった場合、その下の画像や背景色が 透けて描画されますので、これを CAD のレイヤーのように使うことができます。
レイヤー数を増やしたい場合には、透過背景の Bitmap を複数枚用意しておき、 それが g.DrawImage メソッドで重ね合わせて描画することで対応できます。
なお、Bitmap も破棄が必要なオブジェクト(IDisposable)なため、 使わなくなった時点で bmp.Dispose() を呼ぶ必要があります。
|
|