C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

コントロールが全て削除されてしまう [1]

[トピック内 30 記事 (21 - 30 表示)]  << 0 | 1 >>

■87016 / inTopicNo.21)  Re[12]: コントロールが全て削除されてしまう
  
□投稿者/ FRMC (29回)-(2018/04/07(Sat) 13:57:08)
> それに、どこかのサイトを参考にしながら新しいことをやってみているのだろうけど、
> 基礎が出来ていないからうまく応用できない。

はい、私の知識力不足です。

> 恐らく、アプリの概要をHPで公開しているから、
> コードを出すのが難しいかも知れないね。

いえ、コードは後で出しますよ。

> HP訪問者がそれを知ったら、どう思うか。
> (そもそも、HP訪問者がこの掲示板でのやり取りを知ったらどう思うか。)

訪問者はいません。海外からのアンチコメントがくるだけです。
引用返信 編集キー/
■87025 / inTopicNo.22)  Re[11]: コントロールが全て削除されてしまう
□投稿者/ FRMC (31回)-(2018/04/07(Sat) 19:19:26)
No87011 (魔界の仮面弁士 さん) に返信
> 問題となっている個所のコードを提示することはできないのでしょうか?

iPic.Controls.Clear() '←ここ
'For Each l As Control In iPic.Controls
' If TypeName(l) = "Label" Then 'これだと頂点が残ってしまうコード
' iPic.Controls.Remove(l)
' End If
'Next
'頂点すべてを作成
Refresh()
Dim ps() As Point
ps = iPic.PointN.ToArray
For i As Long = 0 To UBound(ps)
iLabel = New Label
With iLabel
.Width = 11
.Height = 11
.BackColor = Color.White
.BorderStyle = BorderStyle.FixedSingle
.Tag = i
.Name = iPic.Name
.Location = ps(i)
.ContextMenuStrip = ContextMenuStrip1 '右クリックメニューの追加
End With
iPic.Controls.Add(iLabel)
AddHandler iLabel.MouseDown, AddressOf マウスダウンドラッグ開始
AddHandler iLabel.MouseMove, AddressOf マウスムーブドラッグ中
AddHandler iLabel.MouseUp, AddressOf マウスアップ
GC.Collect(GC.MaxGeneration)
Next
引用返信 編集キー/
■87030 / inTopicNo.23)  Re[13]: コントロールが全て削除されてしまう
□投稿者/ 報告者 (1回)-(2018/04/07(Sat) 22:08:26)
同じ質問?

https://teratail.com/questions/120671
引用返信 編集キー/
■87031 / inTopicNo.24)  Re[12]: コントロールが全て削除されてしまう
□投稿者/ 魔界の仮面弁士 (1625回)-(2018/04/07(Sat) 23:10:48)
No87025 (FRMC さん) に返信
> iPic.Controls.Clear() '←ここ

iPic というのが何を指しているのか不明瞭ですが、PictureBox の名前でしょうか?
だとしたら、 No86971No86986 でも述べているように、これでは NG です。

https://msdn.microsoft.com/ja-jp/library/system.windows.forms.control.controlcollection.clear%28v=vs.100%29.aspx

》 Clear メソッドを呼び出しても、コントロールのハンドルが
》 メモリから削除されることはありません。 メモリ リークを防ぐには、
》 Dispose メソッドを明示的に呼び出す必要があります。


Dispose 漏れ自体は、Label が消えずに残る問題とは別の話ですが、
コントロールの動的生成・削除を繰り返すようなアプリならばなおの事、
確実に解放するコードを用意すべきでしょう。


> 'For Each l As Control In iPic.Controls
> ' If TypeName(l) = "Label" Then 'これだと頂点が残ってしまうコード
> ' iPic.Controls.Remove(l)
> ' End If
> 'Next

そりゃそうですよ。
For Each で Controls を列挙している最中に、Remove や Add で
Controls の数を増減させてはいけません。列挙位置がズレちゃいます。

No86986 のコードにしても、.Controls を直接 For Each してはいなかったですよね?


> ' If TypeName(l) = "Label" Then 'これだと頂点が残ってしまうコード
わざわざ TypeName で型名を文字列化してから判定させるのではなく、
 If TypeOf l Is Label Then
のように、型そのものを直接判定する方が望ましいです。
コンパイル時にチェックされるので、スペルミスも防げます。

それと Label だけを列挙させたいのであれば、先の例にも挙げた
OfType メソッドを使うのが便利ですよ。



> .Name = iPic.Name
これはあまり望ましくないですね…。
これでは、すべての Label が同じ名前になってしまっています。
(それも、コンテナとなる iPic と同じ名前に)

Visual Studio のフォームデザイナでも、
複数の Label に同じ名前を付けることはできませんよね?

デザイン時とは違って、名前が競合したとしても Add はできるのですが、
すべて同じ Name を付けてしまうと、個々の名前では識別できなくなってしまいます。
というか、どうせ識別に使わない予定なら、Name は未設定のままでも十分でしょう。


> ps = iPic.PointN.ToArray

んん…?

先ほどは iPic が PictureBox と仮定しましたが、
だとしたら PointN というメンバーは無いはず…。

ということは、iPic は UserControl もしくは Form なのかな…?


> For i As Long = 0 To UBound(ps)
これも違っていますね。As Long ではなく、As Integer が正しいです。
VB.NET では UBound の戻り値は Integer です。配列の添字も Integer 型で指定します。


> GC.Collect(GC.MaxGeneration)
Label が消せなかったのは、列挙方法に問題があったからであって、
ガベージコレクションとは無関係です。

不用意にガベージコレクトを強制発動させることはデメリットにもなりえるので、
ここで呼ぶべきでは無いでしょう。Controls.Add するたびに呼ぶような物ではありません。
引用返信 編集キー/
■87032 / inTopicNo.25)  Re[13]: コントロールが全て削除されてしまう
□投稿者/ Azulean (945回)-(2018/04/08(Sun) 08:20:12)
No87031 (魔界の仮面弁士 さん) に返信
> というか、どうせ識別に使わない予定なら、Name は未設定のままでも十分でしょう。

元としたサンプルの悪い使い方の影響を受けているのかもしれませんね。

> 先ほどは iPic が PictureBox と仮定しましたが、
> だとしたら PointN というメンバーは無いはず…。

なぜ、このスレッドでは明かしていないのか謎ですが、 No86825 では元のサンプルのサイトをリンクしています。
 https://blogs.yahoo.co.jp/gogowaten/12512794.html

それによると、PictureBox を継承したクラス(ExPictureBox)だそうで。

Public PointN As New Generic.List(Of Point)


// 前のスレッドでの反応がイマイチだったので、このスレッドはスルー気味です。
引用返信 編集キー/
■87034 / inTopicNo.26)  Re[14]: コントロールが全て削除されてしまう
□投稿者/ FRMC (32回)-(2018/04/08(Sun) 11:35:20)
No87032 (Azulean さん) に返信
>サンプルの悪い使い方の影響を受けているのかもしれませんね。

だとしたら修正します。

> なぜ、このスレッドでは明かしていないのか謎ですが、 No86825 では元のサンプルのサイトをリンクしています。
>  https://blogs.yahoo.co.jp/gogowaten/12512794.html
>
> それによると、PictureBox を継承したクラス(ExPictureBox)だそうで。

そうです。Expictureboxで線の描画をしています。
pictureboxはあくまでも背景です。
引用返信 編集キー/
■87039 / inTopicNo.27)  Re[15]: コントロールが全て削除されてしまう
□投稿者/ 魔界の仮面弁士 (1627回)-(2018/04/08(Sun) 14:53:23)
2018/04/08(Sun) 14:54:51 編集(投稿者)

No87025 (FRMC さん) に返信
> iLabel = New Label
> With iLabel

この iLabel はどこで宣言していますか?

ローカル変数であるのなら良いですが、
フィールド変数として
  Private iLabel As Label
などとするのは NG ですよ。


No87032 (Azulean さん) に返信
>> 先ほどは iPic が PictureBox と仮定しましたが、
>> だとしたら PointN というメンバーは無いはず…。
> なぜ、このスレッドでは明かしていないのか謎ですが、 No86825 では元のサンプルのサイトをリンクしています。

なるほど。そこまでは見ていませんでした。 No86812 から始まるスレッドですね。
http://bbs.wankuma.com/index.cgi?mode=al2&namber=86812#5


> 元としたサンプルの悪い使い方の影響を受けているのかもしれませんね。
Web ページに書かれた文章はそうなっていますが、
公開されている試作品では下記のように実装されていました。
一応、一意な名前を割り当てようとしているようですね。

 'Name = "exL" & i 'Withの中だとName、tag、textが空白になる
 'Tag = i
 'Text = i
End With
Me.iLabel.Name = i
Me.iLabel.Tag = i
iLabel.Text = i

Name だけでなく Text もセットされているので、あちらの実装では
各頂点ラベルに [0]、[1]、[2] …の文字も表示させているようです。



No87034 (FRMC さん) に返信
> だとしたら修正します。

頂点ラベルの位置も補正した方が良いと思います。

先ほどの
 https://pbs.twimg.com/media/DaF-QOYVwAAY1CR.jpg
を見てみると、ラベルの左上が頂点になっていますが、
マーカーの中心が頂点にくるようにする方が綺麗に見えます。

 .Width = 11
 .Height = 11
 .Location = ps(i) - New Point(5, 5)

もちろんドラッグ処理についても、同様の補正が必要になりますが。


で、本題となる「ラベルの削除」ですが、これについては既に
残ってしまう原因を No87031 で説明しており、
正しく消せる実例を No86986 で述べております。

すなわち、iPic に置き換えると
  'これだと Label のハンドルがリークする
  'iPic.Controls.Clear()
  '
  'なので No86986 の対策コードに差し替える
  For Each lbl In iPic.Controls.OfType(Of Label).ToArray()
    iPic.Controls.Remove(lbl)
    lbl.Dispose()
  Next
ということですね。

もしも先の説明で分からなかった点があるようであれば、
理解できなかった部分を示していただければ、追加説明を加えることもできますよ。


> Expictureboxで線の描画をしています。
> pictureboxはあくまでも背景です。
うーん。あのサンプルをそのまま真似ているのだとすると、
ドラッグ処理のところで、直線の再描画のために
  Dim canvas As New Bitmap(Me.ExPictureBox1.Width, Me.ExPictureBox1.Height)
  Dim g As Graphics = Graphics.FromImage(canvas)
  '
  '中略(描画処理)
  '
  Me.ExPictureBox1.Image = canvas
というコードが使われているのが気がかりです。
差し替える前の古い Image と、作成した Graphics が解放漏れとなっているので…。
(既に修正済みかもしれませんが)
引用返信 編集キー/
■87042 / inTopicNo.28)  Re[16]: コントロールが全て削除されてしまう
□投稿者/ FRMC (33回)-(2018/04/08(Sun) 20:08:33)
2018/04/08(Sun) 20:14:19 編集(投稿者)

No87039 (魔界の仮面弁士 さん) に返信
> ローカル変数であるのなら良いですが、
> フィールド変数として
>   Private iLabel As Label
> などとするのは NG ですよ。

まさにNGの状態です。これはどう直せばいいのでしょうか?

> 頂点ラベルの位置も補正した方が良いと思います。

補正しました。

>
> で、本題となる「ラベルの削除」ですが、これについては既に
> 残ってしまう原因を No87031 で説明しており、
> 正しく消せる実例を No86986 で述べております。
>
> すなわち、iPic に置き換えると
>   'これだと Label のハンドルがリークする
>   'iPic.Controls.Clear()
>   '
>   'なので No86986 の対策コードに差し替える
>   For Each lbl In iPic.Controls.OfType(Of Label).ToArray()
>     iPic.Controls.Remove(lbl)
>     lbl.Dispose()
>   Next
> ということですね。

なるほど、これで他のpictureboxが消されずに済むようになりました。
しかし、線の描画に関係のないLabelは相変わらず消されてしまいます。

>>Expictureboxで線の描画をしています。
>>pictureboxはあくまでも背景です。
> うーん。あのサンプルをそのまま真似ているのだとすると、
> ドラッグ処理のところで、直線の再描画のために
>   Dim canvas As New Bitmap(Me.ExPictureBox1.Width, Me.ExPictureBox1.Height)
>   Dim g As Graphics = Graphics.FromImage(canvas)
>   '
>   '中略(描画処理)
>   '
>   Me.ExPictureBox1.Image = canvas
> というコードが使われているのが気がかりです。
> 差し替える前の古い Image と、作成した Graphics が解放漏れとなっているので…。
> (既に修正済みかもしれませんが)

修正はしていません。そのあたりはかなり複雑で、自分も特に修正の必要がないと思っていたので…。
引用返信 編集キー/
■87044 / inTopicNo.29)  Re[17]: コントロールが全て削除されてしまう
□投稿者/ よもやま (3回)-(2018/04/08(Sun) 20:46:17)
No87042 (FRMC さん) に返信
>
> 修正はしていません。そのあたりはかなり複雑で、自分も特に修正の必要がないと思っていたので…。
「自分も」?
「自分は」の間違いでしょ?
指摘を受けてるじゃない。


引用返信 編集キー/
■87045 / inTopicNo.30)  Re[17]: コントロールが全て削除されてしまう
□投稿者/ 魔界の仮面弁士 (1629回)-(2018/04/08(Sun) 22:26:25)
2018/04/08(Sun) 23:01:06 編集(投稿者)

No87042 (FRMC さん) に返信
>>フィールド変数として
>>  Private iLabel As Label
>>などとするのは NG ですよ。
> まさにNGの状態です。これはどう直せばいいのでしょうか?

先に述べた通り、直し方は
>>ローカル変数であるのなら良いですが、
です。「フィールド変数」を「ローカル変数」に変更してください。


> しかし、線の描画に関係のないLabelは相変わらず消されてしまいます。
そもそも何故、関係のない Label が紛れ込んでいるのかを
見直すべきなのかも知れませんが、とりあえず今は、
紛れ込んでしまったものを、このタイミングで削除するのだとしましょう。

その場合、「関係のない Label」とは、そもそも何を指しておられますか?
あるいはその逆でも良いです。「線の描画に関係する Label」というものは
プログラム中でどのように管理されていますか?


それを明確にすれば、削除のためのループ内で If 判定することで、
不要なものだけを削除できますよね。

For Each lbl In iPic.Controls.OfType(Of Label).ToArray()
 If 不要なlblかどうか Then
  iPic.Controls.Remove(lbl)
  lbl.Dispose()
 End If
Next

あるいは

For Each lbl In iPic.Controls.OfType(Of Label).ToArray()
 If Not 描画に必要なlblかどうか Then
  iPic.Controls.Remove(lbl)
  lbl.Dispose()
 End If
Next


あるいは、Label をすべて削除したのち、その直後に改めて
線の描画に必要な Label 群を作り直す手法にしても良いと思います。


> 修正はしていません。
するべきかと。放っておけばガベージコレクトはされるでしょうけれども、
ドラッグ時のマウス座標が移動するたびに、以前の Bitmap を処分しないまま
新しい Bitmap がどんどん生成されていくのは、良い実装とは言えませんよね。
引用返信 編集キー/

このトピックをツリーで一括表示

<前の20件
トピック内ページ移動 / << 0 | 1 >>

このトピックに書きこむ