UserControl に WebBrowser コントロールを貼り付けると、
Load イベントハンドラーが呼ばれません。
private void UserControl1_Load(object sender, EventArgs e)
{
MessageBox.Show("呼ばれない orz");
}
このデリゲート(UserControl1_Load) を呼ぶのは、
ベースクラスの OnLoad() メソッドです。
というわけで、OnLoad() が呼ばれないのかな?と思い、
オーバーライドしてブレークを張ったところ、ちゃんと呼ばれています。
OnLoad() で追加したイベントハンドラーを呼ばれないなんてことがあるの?
など思いつつ Reflector で UserControl.OnLoad() を覗いてみました。
実装は次のようになっています。
protected virtual void OnLoad(EventArgs e)
{
EventHandler handler = (EventHandler)base.Events[EVENT_LOAD];
if (handler != null)
{
handler(this, e);
}
}
実際、OnLoad() でブレイクして
base.Events[EVENT_LOAD] をウォッチしてみると
やっぱり null になっていました。
これで、イベントハンドラーが呼ばれないのも納得です。
だって呼んでいないんだもの (T ^ T)
では、なぜ呼ばれないか?
ブレイク張ってみると一目瞭然。張るところは当然
1.) this.Load += new System.EventHandler(this.UserControl1_Load);
2.) UserControl1.OnLoad()
UserControl に WebBrowser を貼り付けなければ
1 -> 2 と正しい順序で動きます。
一方、WebBrowser を張ると、2 -> 1 の順番で動きます。
どうやら this.Load += ... より前に
OnLoad() を呼んでしまう処理があるようです。
InitializeComponent() の先頭でブレイクし、
ステップ実行していくと・・・ありました。
this.Controls.Add(this.webBrowser1);
この後、UserControl1.OnLoad() が実行されてしまいました。Refrelctor で今度は ControlCollection.Add() メソッドを覗いてみました。
public virtual void Add(Control value)
{
if (value != null)
{
if (value.GetTopLevel())
{
throw new ArgumentException(SR.GetString("TopLevelControlAdd"));
}
if (this.owner.CreateThreadId != value.CreateThreadId)
{
throw new ArgumentException(SR.GetString("AddDifferentThreads"));
}
Control.CheckParentingCycle(this.owner, value);
if (value.parent == this.owner)
{
value.SendToBack();
}
else
{
if (value.parent != null)
{
value.parent.Controls.Remove(value);
}
base.InnerList.Add(value);
if (value.tabIndex == -1)
{
int num = 0;
for (int i = 0; i < (this.Count - 1); i++)
{
int tabIndex = this[i].TabIndex;
if (num <= tabIndex)
{
num = tabIndex + 1;
}
}
value.tabIndex = num;
}
this.owner.SuspendLayout();
try
{
Control parent = value.parent;
try
{
value.AssignParent(this.owner);
}
finally
{
if ((parent != value.parent) && ((this.owner.state & 1) != 0))
{
value.SetParentHandle(this.owner.InternalHandle);
if (value.Visible)
{
value.CreateControl();
}
}
}
value.InitLayout();
}
finally
{
this.owner.ResumeLayout(false);
}
LayoutTransaction.DoLayout(this.owner, value, PropertyNames.Parent);
this.owner.OnControlAdded(new ControlEventArgs(value));
}
}
}
先ほど、
このデリゲート(UserControl1_Load) を呼ぶのは、
ベースクラスの OnLoad() メソッドです。
なんて言いましたが、では OnLoad() はどこから呼ばれるか?
答えは、UserControl.OnCreateControl() です。
となると、なんとなく怪しいのがマーカーを入れた value.CreateControl() で、
Visible プロパティを見て分岐していることから
実際、以下のようにすると UserControl1_Load が呼ばれるようになります。
それならと試しに、WebBrowser を継承して
OnCreateControl() をオーバーライドし、
base.OnCreateControl() を呼ぶ直前に Visible = false とすると
UserControl1_Load は呼ばれませんでした。残念。。orz
this.webBrowser1.Visible = false;
this.Controls.Add(this.webBrowser1);
this.webBrowser1.Visible = true;
てっきり WebBrowser の OnCreateControl() から
コンテナコントロールの OnCreateControl() を呼んでいるのだと
思ったのですが、見当違いだったみたいです。
間違いなく ControlCollection.Add() のどこかで
コンテナコントロールの OnCreateControl() が呼ばれるはずなのですが
長くなりすぎたし、とりあえずは解決できているので良しとします。