インテリセンスその他 への返答
投稿で使用できる特殊コードの説明。(別タブで開きます。)
以下の返答は逆順(新しい順)に並んでいます。
投稿者 taka  (社会人)
投稿日時
2019/12/27 20:28:58
魔界の仮面弁士さま解答ありがとうございます。かたい頭が少しほぐれてます、
プログラミング初心者というと語弊がありましたか、四半世紀ぶり必要に駆られて3か月前から取り組んでいます。当時はBASIC、浅くアセンブリとCを目的もなく遊んでいた。それでいま疑問点が結構沸いている状態です。
creategraphicsですが、「コントロールのGraphicsを作成します」とのことですが、
内部的なこと質問で恐縮ですが要するにピクチャーボックス型が示す領域に実仮想のメモリを割り当てますということなんですか?あとdispose()でメモリ開放でしょうか。
またよろしければ助けていただければ幸いです。^^
プログラミング初心者というと語弊がありましたか、四半世紀ぶり必要に駆られて3か月前から取り組んでいます。当時はBASIC、浅くアセンブリとCを目的もなく遊んでいた。それでいま疑問点が結構沸いている状態です。
creategraphicsですが、「コントロールのGraphicsを作成します」とのことですが、
内部的なこと質問で恐縮ですが要するにピクチャーボックス型が示す領域に実仮想のメモリを割り当てますということなんですか?あとdispose()でメモリ開放でしょうか。
またよろしければ助けていただければ幸いです。^^
投稿者 魔界の仮面弁士  (社会人)
投稿日時
2019/12/25 20:01:23
taka さんが提示されたサンプルでは、Graphics の後始末が漏れているようですね。
本来はイベントの最後に「g.Dispose()」と書いて、
使い終わった Grapchis を処分することが望ましいです。
また、CreateGraphics() で行える描画処理は一時的なものです。
描いた画面が最小化されたり、あるいは描いた内容の上に
他のウィンドウが重なったりすると、折角描いた内容は消えてしまいます。
そのため、継続的な描画処理が必要な場面では、CreateGraphics() 以外の方法で
Graphics インスタンスを得る手法がしばしば用いられます。
(Graphics.FromImage メソッドや、Paint イベントの e.Graphics プロパティなど)
> センダーはピクチャーボックス型 gはグラフィックス型で「コントロールのGraphicsを作成します」ということのようです。
以下、ちょっと細かい話になりますが:
★イベントの第一引数は、「そのイベントの対象となったオブジェクト」を示しています。
これは通常、「sender As Object」と書かれることになります。
Button1_Click なら、sender の中身は Button1 になります。
PictureBox1_MouseMove なら、 sender の中身は、PictureBox1 になります。
下記のように、一つの Click イベントに Button1 と Button2 の二つが
割り当てられている場合、引数 sender を使って、どちらが押されたのかを調べられます。
★イベントの第二引数は、「そのイベントの追加情報」を示します。
これは、「e As EventArgs」もしくは「e As 何某EventArgs」の形式を取ります。
たとえば MouseDown や MouseUp イベントの時は、e As MouseEventArgs となっており、
この引数から、マウスの座標や押されたマウスボタンなどの情報を得ることができます。
> Dim g As Graphics = sender.CreateGraphics()
sender が As PictureBox や As TextBox なデータ型で宣言されていれば、
sender.CreateGraphics() を呼び出すことができますし、IntelliSense でも表示されます。
sender が String や Integer なデータ型であった場合には、それらの型には
.CreateGraphics() メソッドが存在しないので、呼び出すことができません。
そして通常、sender は As Object なデータ型で宣言されています。
Object 型とは、ざっくり言えば「何でも入る万能な型」です。
何でも入るのは良いですが、PictureBox1 と String では、使用可能なメソッドも当然異なりますよね。
変数の型は Object 型でも、その中身が本当に CreateGraphics を
利用可能であるのかどうかは、そこに入っているインスタンス次第です。
PictureBox などのインスタンスであれば、.CreateGraphics を利用できますが、
String などのインスタンスだった場合には 利用できない、ということになります。
つまり中身が確定するのは実行時であり、コンパイル時には中身が未確定のため、
IntelliSense のサポートが受けられない……という事情だったわけです。
ちなみに、.ToString() であれば IntelliSense に現れるかと思いますが、
これは ToString メソッドが「Object 型によって提供されているメソッド」だからです。
一方、CreateGraphics メソッドは「Control 型によって提供されているメソッド」なので、
変数のデータ型が As Control の時の場合に、IntelliSense に現れるようになっています。
あるいは、「Control を継承した型」であっても呼び出せますので、
As PictureBox や As Form や As TextBox などの場合にも、CreateGraphics を使えます。
> センダーのあとのワードがインテリセンスされません。これは何ですか?
IntelliSense には現れるのは「その変数の型で提供されているメンバー」に限られるためです。
存在しないメンバーを記述すれば、エラーになります。
ただし変数が Object 型であった場合に限り、IntelliSense で
現れなかったメンバーでも記述することが可能です。
これを実行時バインディングと呼びます。
実行時バインディングの場合、スペルミスをしてもコンパイル時には検出されません。
もしもスペルが間違っていた場合、そんなメソッドは存在しないので、
実際に呼び出そうとした段階でエラー通知されることになります。
実行するまで分からないので、不具合に気がつきにくいのが難点です。
スペルミスは、コンパイル時に検出された方が安全ですよね。
そこで前回の回答のように、DirectCast を使うなどして、明示的な型変換(キャスト)を
行ってあげる方法を取ることが望ましいです。これを事前バインディングと呼びます。
事前バインディングであれば、コンパイル時にメソッド名の存在チェックが行われるので、
スペルミスの可能性を避けることができるので、より安全なコードとなります。
本来はイベントの最後に「g.Dispose()」と書いて、
使い終わった Grapchis を処分することが望ましいです。
また、CreateGraphics() で行える描画処理は一時的なものです。
描いた画面が最小化されたり、あるいは描いた内容の上に
他のウィンドウが重なったりすると、折角描いた内容は消えてしまいます。
そのため、継続的な描画処理が必要な場面では、CreateGraphics() 以外の方法で
Graphics インスタンスを得る手法がしばしば用いられます。
(Graphics.FromImage メソッドや、Paint イベントの e.Graphics プロパティなど)
> センダーはピクチャーボックス型 gはグラフィックス型で「コントロールのGraphicsを作成します」ということのようです。
以下、ちょっと細かい話になりますが:
★イベントの第一引数は、「そのイベントの対象となったオブジェクト」を示しています。
これは通常、「sender As Object」と書かれることになります。
Button1_Click なら、sender の中身は Button1 になります。
PictureBox1_MouseMove なら、 sender の中身は、PictureBox1 になります。
下記のように、一つの Click イベントに Button1 と Button2 の二つが
割り当てられている場合、引数 sender を使って、どちらが押されたのかを調べられます。
Private Buttons_Click(sender As Object, e As EventArgs) Handles Button1.Click, Button2.Click
MsgBox(sender.Text)
End Sub
★イベントの第二引数は、「そのイベントの追加情報」を示します。
これは、「e As EventArgs」もしくは「e As 何某EventArgs」の形式を取ります。
たとえば MouseDown や MouseUp イベントの時は、e As MouseEventArgs となっており、
この引数から、マウスの座標や押されたマウスボタンなどの情報を得ることができます。
> Dim g As Graphics = sender.CreateGraphics()
sender が As PictureBox や As TextBox なデータ型で宣言されていれば、
sender.CreateGraphics() を呼び出すことができますし、IntelliSense でも表示されます。
sender が String や Integer なデータ型であった場合には、それらの型には
.CreateGraphics() メソッドが存在しないので、呼び出すことができません。
そして通常、sender は As Object なデータ型で宣言されています。
Object 型とは、ざっくり言えば「何でも入る万能な型」です。
' Object 型には、どんなものでも入れられる
Dim a As Object = PictureBox1
Dim b As Object = 123
Dim c As Object = "abc"
何でも入るのは良いですが、PictureBox1 と String では、使用可能なメソッドも当然異なりますよね。
変数の型は Object 型でも、その中身が本当に CreateGraphics を
利用可能であるのかどうかは、そこに入っているインスタンス次第です。
PictureBox などのインスタンスであれば、.CreateGraphics を利用できますが、
String などのインスタンスだった場合には 利用できない、ということになります。
つまり中身が確定するのは実行時であり、コンパイル時には中身が未確定のため、
IntelliSense のサポートが受けられない……という事情だったわけです。
' a も b も Object 型。この場合、.CreateGraphics は IntelliSense に現れない。
Dim g1 As Graphics = a.CreateGraphics() 'a は PictureBox1 なので、コレは呼び出せる
Dim g2 As Graphics = b.CreateGraphics() 'b は Integer なので、実行時にエラーとなる
ちなみに、.ToString() であれば IntelliSense に現れるかと思いますが、
これは ToString メソッドが「Object 型によって提供されているメソッド」だからです。
一方、CreateGraphics メソッドは「Control 型によって提供されているメソッド」なので、
変数のデータ型が As Control の時の場合に、IntelliSense に現れるようになっています。
あるいは、「Control を継承した型」であっても呼び出せますので、
As PictureBox や As Form や As TextBox などの場合にも、CreateGraphics を使えます。
> センダーのあとのワードがインテリセンスされません。これは何ですか?
IntelliSense には現れるのは「その変数の型で提供されているメンバー」に限られるためです。
存在しないメンバーを記述すれば、エラーになります。
ただし変数が Object 型であった場合に限り、IntelliSense で
現れなかったメンバーでも記述することが可能です。
これを実行時バインディングと呼びます。
実行時バインディングの場合、スペルミスをしてもコンパイル時には検出されません。
もしもスペルが間違っていた場合、そんなメソッドは存在しないので、
実際に呼び出そうとした段階でエラー通知されることになります。
実行するまで分からないので、不具合に気がつきにくいのが難点です。
スペルミスは、コンパイル時に検出された方が安全ですよね。
そこで前回の回答のように、DirectCast を使うなどして、明示的な型変換(キャスト)を
行ってあげる方法を取ることが望ましいです。これを事前バインディングと呼びます。
事前バインディングであれば、コンパイル時にメソッド名の存在チェックが行われるので、
スペルミスの可能性を避けることができるので、より安全なコードとなります。
投稿者 (削除されました)  ()
投稿日時
2019/12/25 19:42:05
(削除されました)
投稿者 魔界の仮面弁士  (社会人)
投稿日時
2019/12/25 18:49:53
> センダーのあとのワードがインテリセンスされません。これは何ですか?
今回のように、sender の中身が PictureBox であることが事前に分かっている場合は、
もしくは、単純に
sender As PictureBox に書き換えておくということもできます。
> これは何ですか?
ザックリ言えば、 Object 型の「遅延バインディング」と呼ばれる機構です。
> 大文字小文字は関係ないですね?
はい。Visual Basic は大文字小文字を区別しません。…基本的には。
今回のように、sender の中身が PictureBox であることが事前に分かっている場合は、
Dim g As Graphics = DirectCast(sender, PictureBox).CreateGraphics()
のように、明示的な型変換を行ってやれば OK です。もしくは、単純に
Dim g As Graphics = sender.CreateGraphics()
とだけ書けるようにするために、イベント引数の sender As Object をsender As PictureBox に書き換えておくということもできます。
> これは何ですか?
ザックリ言えば、 Object 型の「遅延バインディング」と呼ばれる機構です。
> 大文字小文字は関係ないですね?
はい。Visual Basic は大文字小文字を区別しません。…基本的には。
投稿者 (削除されました)  ()
投稿日時
2019/12/25 18:32:09
(削除されました)
投稿者 taka  (社会人)
投稿日時
2019/12/25 15:49:10
プログラミング初心者です。質問があります。
sender.CreateGraphics() のところなんですが、センダーのあとのワードがインテリセンスされません。これは何ですか?creategraphics()の小文字でも実行できますね。大文字小文字は関係ないですね?
センダーはピクチャーボックス型 gはグラフィックス型で「コントロールのGraphicsを作成します」ということのようです。
gは描画のために型を宣言しました。gの出力先をピクチャーボックス1に割り当てているのですか?インスタンス化でも違いますか?
Dim g As Graphics = sender だけでは描画されませんね。
Private Sub PictureBox1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseMove
Dim g As Graphics = sender.CreateGraphics()
If e.Button = MouseButtons.Left Then
g.FillEllipse(Brushes.Red, e.X, e.Y, 10, 10)
ElseIf e.Button = MouseButtons.Right Then
g.FillEllipse(Brushes.Blue, e.X, e.Y, 10, 10)
End If
End Sub
sender.CreateGraphics() のところなんですが、センダーのあとのワードがインテリセンスされません。これは何ですか?creategraphics()の小文字でも実行できますね。大文字小文字は関係ないですね?
センダーはピクチャーボックス型 gはグラフィックス型で「コントロールのGraphicsを作成します」ということのようです。
gは描画のために型を宣言しました。gの出力先をピクチャーボックス1に割り当てているのですか?インスタンス化でも違いますか?
Dim g As Graphics = sender だけでは描画されませんね。
Private Sub PictureBox1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseMove
Dim g As Graphics = sender.CreateGraphics()
If e.Button = MouseButtons.Left Then
g.FillEllipse(Brushes.Red, e.X, e.Y, 10, 10)
ElseIf e.Button = MouseButtons.Right Then
g.FillEllipse(Brushes.Blue, e.X, e.Y, 10, 10)
End If
End Sub
開放というよりは解放ですね。処分と言い換えても良いですが。
> creategraphicsですが、「コントロールのGraphicsを作成します」とのことですが、
Bitmap や PictureBox は、絵を描くためのキャンバスに相当します。
一方 Graphics オブジェクトは、ペンやブラシなどのお絵かきツールだと思ってください。
Graphics クラスの FillEllipse メソッドを呼び出せば、楕円が描かれますし、
Graphics クラスの DrawString メソッドを呼び出せば、文字列が描画されます。
Graphics クラスの Clear メソッドを呼び出せば、指定した色でキャンバス全体が塗り潰されます。
このとき、これらの描画結果が何処に描画されるかといえば、
「その Graphics が指しているキャンバス」になります。
そのキャンバスは PictureBox のこともあれば、プリンターの場合もありますが、
出力先が何であっても、Graphics クラスを使って同じように描いていくことが出来ます。