-
Notifications
You must be signed in to change notification settings - Fork 0
MS_ASPNETWebFormsEventOrder
ASP.NET Web Forms のイベント発生順について説明する。
補足(なぜ順序が重要か): Web Forms は
ページ ライフサイクルの各段階で「できること」が異なるため、
「動かない」の原因が書いた場所(イベント)の誤りである
ことが非常に多い。【典型的な失敗】 ・Page_Load でコントロールの値を書き換えたのに反映されない → ViewState / ポストバック データの復元との前後関係を誤っている ・Page_Load で毎回データバインドしてしまい、 ユーザーの入力が消える → if (!IsPostBack) の判定漏れ ・動的に追加したコントロールが消える → Page_Init で作らないと ViewState が復元されない
- コンストラクタ
- Page.Init イベント
- Page.Load イベント
の順番で動作する。
補足(全体のライフサイクル): 原文が扱う 3 つは冒頭部分で、
実際にはもっと多くの段階がある。実務で押さえるべきものを整理する。
# 段階 この時点でできること 1 コンストラクタ ほぼ何もできない(コントロール未生成) 2 PreInitマスター ページ / テーマの動的切り替え 3 Init動的コントロールの生成(ここで作らないと ViewState が復元されない) 4 InitComplete5 ViewState の復元 ← ここで前回の状態が戻る 6 ポストバック データの処理 ← ここでユーザー入力が反映される 7 Load(Page_Load)コントロールの値が確定している。通常の処理はここ 8 コントロール イベント Button.Click 等(後述) 9 LoadComplete10 PreRender描画直前の最終調整(表示の書き換えはここ) 11 ViewState の保存 12 RenderHTML 生成 13 Unload後始末(応答は書けない) 要点は 5・6 と 7 の前後関係である。
ViewState 復元(5)→ ポストバック データ(6)→ Page_Load(7) → Page_Load の時点では、既にユーザーの入力が コントロールに反映されている → ここで無条件にデータバインドすると、入力が上書きされるprotected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) // ← 初回表示時だけ { BindData(); } }
if (!IsPostBack)は Web Forms の基本作法であり、
これを忘れることが最も多い不具合である。
「親クラス1」 <--- 「親クラス2」 <--- 「派生の末端クラス」と
継承を行っていた場合、
- コンストラクタ
- 親クラス1 の コンストラクタ
- 親クラス2 の コンストラクタ
- 派生の末端クラス の コンストラクタ
- Page.Init イベント
- 親クラス1 の イベント ハンドラ
- 親クラス2 の イベント ハンドラ
- 派生の末端クラス の イベント ハンドラ
- Page.Load イベント
- 親クラス1 の イベント ハンドラ
- 親クラス2 の イベント ハンドラ
- 派生の末端クラス の イベント ハンドラ
の順番で動作する。
移行メモ(誤記): 原文では 2.・3.(Page.Init / Page.Load)の
内訳もすべて「コンストラクタ」と記載されていたが、
文脈からイベント ハンドラの誤記と判断し修正した
(直後の説明「同一のイベントのイベント ハンドラは、
ベースクラスから派生クラスに向けて順番に呼び出される」と整合する)。
- 基本的に、
- 同一のイベントのイベント ハンドラは、
ベースクラスから派生クラスに向けて順番に呼び出される。 - また、コンストラクタや、Page.Init、Page.Load イベント以外にも様々なイベントがある。
- 上記以外のイベント種類やイベント発生順については、
ASP.NET のマニュアルを参照のこと。
- 同一のイベントのイベント ハンドラは、
補足(なぜ基底クラスから呼ばれるのか): これは
AutoEventWireupによる自動結線の仕組みによる。AutoEventWireup="true"(既定) → Page_Init / Page_Load という「名前」のメソッドが 自動的にイベント ハンドラとして登録される → 基底クラスの分から順に登録される → デリゲートは登録順に呼ばれる = 基底クラス → 派生クラス**
overrideではなく「イベント ハンドラの多重登録」**である点が要点で、
派生側で基底側の処理を打ち消すことはできない(両方走る)。// 基底側の処理を制御したい場合は、override にする protected override void OnLoad(EventArgs e) { // 基底の処理より前に何かする base.OnLoad(e); // ← 呼ばないと Load イベント自体が発火しない // 基底の処理より後に何かする }フレームワークの基底ページ クラスを作る場合は、
Page_LoadではなくOnLoadをoverrideする方が
呼び出し順を制御できて確実である。
初期化イベントの次に、コントロール イベントが発生する。
PostBack につき、1イベントしか発生しない。
- PostBack につき、複数イベントが発生し得る。
- 例えば、Button.Click + TextBox.TextChanged
補足(複数イベント時の順序): 原文が指摘する
「複数イベントが発生し得る」場合の順序も、実務では重要になる。① 変更通知イベント(Changed 系) TextBox.TextChanged、DropDownList.SelectedIndexChanged、 CheckBox.CheckedChanged … → コントロールの宣言順(ページ上の並び順)で発火 ② ポストバックを引き起こしたイベント(1 つだけ) Button.Click、LinkButton.Click …つまり、「Click より先に TextChanged が走る」。
テキストを書き換えてから[保存]ボタンを押した場合 TextChanged → Click の順 → TextChanged 側で何か副作用を起こすと、 Click 側の前提が変わっている、という事故が起きる
AutoPostBackの意味の整理:
設定 挙動 AutoPostBack="true"そのコントロールを変更した瞬間にポストバックする
(onchangeで JavaScript が__doPostBackを呼ぶ)AutoPostBack="false"(既定)変更してもポストバックしない
次にボタン等でポストバックした時に、まとめて発火する
AutoPostBack="true"を多用すると、
入力のたびに全体がポストバックされて重くなるため、
連動する必要がある箇所に限って使うのが定石である。なお、これらの挙動はすべて ViewState と
__doPostBackに
依存しており、部分描画とJavaScript で
述べられる UpdatePanel の複雑さの根にもなっている。
- ASP.NET ページのライフサイクルの概要
https://learn.microsoft.com/ja-jp/previous-versions/aspnet/ms178472(v=vs.100) - ASP.NET Web ページのライフサイクル イベント
https://learn.microsoft.com/ja-jp/previous-versions/aspnet/ms178472(v=vs.100)#life-cycle-events - ASP.NET 状態管理の概要(ViewState)
https://learn.microsoft.com/ja-jp/previous-versions/aspnet/75x4ha6s(v=vs.100)
Tags: 移行, .NET開発, ASP.NET, ASP.NET Web Forms
このWikiは「Open棟梁Project」,「OSSコンソーシアム 開発基盤部会」によって運営されています。