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

わんくま同盟

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

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


■90071 / )  Re[3]: ユーザーコントロールの使い方
□投稿者/ 魔界の仮面弁士 (2037回)-(2019/02/04(Mon) 13:47:10)
2019/02/04(Mon) 14:32:50 編集(投稿者)

No90070 (ルパン さん) に返信
>   pnlSub.Controls.Remove(pnlSub.Controls[0]);
>   pnlSub.Controls[0].Dispose();

そのコードは正しくありません。
1 行目が指している [0] と 2 行目が指している [0] が
別のコントロールを指していることに注意してください。


提示のコードだと、pnlSub 上の子コントロールが【奇数個】の場合に
最後の 1 個が Remove されないことになってしまいます。
※例外:ArgumentOutOfRangeException

また、【偶数個】であった場合、一見するとすべて Remove されるように
見えますが、実際には偶数・奇数いずれの場合にも、
「半数は Remove しただけ(Dispose していない)」
「半数は Dispose しただけ(自動的に Remove される)」
という動作に陥っていることに注意してください。



代案1:削除対象のコレクションと列挙用のコレクションを分ける

Control[] children = pnlSub.Controls.OfType<Control>().ToArray();
foreach (Control c in children)
{
 pnlSub.Controls.Remove(c);
 c.Dispose();
}


代案2:子アイテムを変数等に保持しておき、コレクションから除去後に破棄する
while (pnlSub.Controls.Count > 0)
{
 using (pnlSub.Controls[0])
 {
  pnlSub.Controls.RemoveAt(0);
 }
}


代案3:後ろから前に Dispose する
for (int i = pnlSub.Controls.Count - 1; i >= 0; i--)
{
 // Dispose されると自動的に Remove される
 pnlSub.Controls[i].Dispose();
}



> ネットで調べたら 「UserControl の後処理は Dispose で行う」とあったので、

親コントロールが破棄される際には、それに先んじて
子コントロールが破棄されますので、Disposed イベント中には
他のコントロール(もちろん Label にも)アクセスすることはできません。

故にたとえば Dispose 時には、「子コントロールそのもの」に
アクセスするのではなく、予め保持しておいた
「子コントロールが持っていた値」を使って処理するようにします。


新規プロジェクトに下記を貼り、実行してフォームを閉じてみてください。


using System.Diagnostics;
public partial class Form1 : Form
{
 private UserControl uc;
 public Form1()
 {
  InitializeComponent();
  Controls.Add(uc = new UC() { Dock = DockStyle.Fill });
 }
}

public class UC : UserControl
{
 private Label lbl;
 private string _labelText = null;
 private string LabelText { get { return _labelText; } }
 public UC()
 {
  Controls.Add(lbl = new Label() { Text = "lbl", Dock = DockStyle.Fill });
  
  // Label が破棄された後でも Text を拾えるよう、保持しておく
  lbl.TextChanged += delegate { _labelText = lbl.Text; };
  _labelText = lbl.Text;

  //
  lbl.Disposed += delegate
  {
   Debug.WriteLine("Label.Disposed");
   Debug.WriteLine(" UserControl.Disposing = " + this.Disposing);
   Debug.WriteLine(" UserControl.IsDisposed = " + this.IsDisposed);
   Debug.WriteLine(" Label.Disposing = " + lbl.Disposing);
   Debug.WriteLine(" Label.IsDisposed = " + lbl.IsDisposed);
   Debug.WriteLine(" Label.Text = [" + lbl.Text + "]");
   Debug.WriteLine(" LabelText = [" + this.LabelText + "]");
  };
  this.Disposed += delegate
  {
   Debug.WriteLine("UserControl.Disposed");
   Debug.WriteLine(" UserControl.Disposing = " + this.Disposing);
   Debug.WriteLine(" UserControl.IsDisposed = " + this.IsDisposed);
   Debug.WriteLine(" Label.Disposing = " + lbl.Disposing);
   Debug.WriteLine(" Label.IsDisposed = " + lbl.IsDisposed);
   Debug.WriteLine(" Label.Text = [" + lbl.Text + "]");
   Debug.WriteLine(" LabelText = [" + this.LabelText + "]");
  };
 }
}


---- 実行結果 ----
Label.Disposed
 UserControl.Disposing = True
 UserControl.IsDisposed = False
 Label.Disposing = True
 Label.IsDisposed = False
 Label.Text = []
 LabelText = [lbl]
UserControl.Disposed
 UserControl.Disposing = True
 UserControl.IsDisposed = False
 Label.Disposing = False
 Label.IsDisposed = True
 Label.Text = []
 LabelText = [lbl]
返信 編集キー/


管理者用

- Child Tree -