UpdatePanelについて

僕の周りでは、「asp:Panelって書く替わりにasp:UpdatePanelってかくと画面がちらつかないらしいよ!! 」というある種呪術的なノリで使われており、悲しかったので、UpdatePanelについて考えてみたいと思います。僕が分かりにくいと感じているのは、UpdatePanelそのものというより、UpdatePanelがページ内に存在するとき、ほかのコントロールのイベントが通常のポストバックとして扱われるか、非同期ポストバックとして扱われるか、です。たとえば、あるButtonコントロールが押されたときに、そのイベントが非同期ポストバックを引き起こすのか、通常のポストバックとなるのか、というのは、そのButtonコントロールのプロパティで制御できるのが自然であるように思われますが、実際はそうではありません。

UpdatePanelには、これに関連して重要なプロパティが二つあります。

UpdateMode
"Conditional" or "Always" デフォルトでは、"Always"
ChildrenAsTriggers
"true" or "false" デフォルトは"true"

UpdateModeは、非同期ポストバックが起きた場合に、そのUpdatePanelが更新されるかどうか、を示します。デフォルトは"Always"なので、そのページ内で非同期ポストバックが起きた場合、そのUpdatePanelは常に更新されます。通常のポストバックが発生した場合は、もちろん更新されます(*)。"Conditinal"の場合は、非同期ポストバックが起きても、そのUpdatePanelのAsyncPostBackTriggerに指定されていなければ更新されません。
ChildrenAsTriggersは、そのUpdatePanelの中に含まれるコントロールが非同期ポストバックを引き起こすトリガーとして登録されるかどうか、を指定します。通常は"true"なので、そのUpdatePanel内のコントロールで発生したイベントはすべて非同期ポストバックになります。つまり、"トリガーとして登録される"というのは、そのコントロールは非同期ポストバックを行うようになる、ということです。
# (*)余談ですが、

UpdateMode プロパティが Conditional に設定されており、次の条件のいずれかに当てはまる場合。
……

UpdatePanel..::.UpdateMode プロパティ

を読む限りでは、通常のポストバックが起こっても、UpdateModeが"Conditional"で、明示的にトリガーとして指定されていない場合、UpdatePanelの内容は更新されない、となると思うのですが、実際には、通常のポストバックなら必ず更新されます。
#ここまで余談

すべてのUpdatePanelコントロールをデフォルトの設定で運用した場合、「すべてのUpdatePanelは通常のポストバックでも非同期ポストバックでも必ず更新され」、かつ、「UpdatePanel内のコントロールで発生したイベントは非同期ポストバック、それ以外のイベントは通常のポストバック」になります。
これで問題なければいいのですが、次のような要件があると設定を変えなければなりません。

  1. UpdatePanel内のコントロールから通常のポストバックを起こしたい
  2. UpdatePanel内/外コントロールから特定のUpdatePanelのみを更新したい

1.は画面の遷移を伴う処理をイベント内で行いたい場合必要です。2.はイベントによって更新するUpdatePanelを制御したい場合に必要です。

UpdatePanel内のコントロールから通常のポストバックを起こしたい

これには二つ手段があります。一つ目は、「ChildrenAsTriggers="true"のまま、PostBackTriggerに登録する」で、二つ目は、「ChildrenAsTriggers="false"にして、そのほかのコントロールをAsyncPostBackTriggerに登録する」です。
# RepeaterやDataGrid内にあるコントロールを指定したい場合は、ひとつ前のエントリの方法を使う必要があります

UpdatePanel内/外コントロールから特定のUpdatePanelのみを更新したい

これを実現するには、当該UpdatePanel"以外"のUpdatePanelのUpdateModeを"Conditional"に変更する必要があります。当然、UpdateModeを"Conditional"に変えただけだと、当該UpdatePanel内のコントロール以外で起きた非同期ポストバックでは更新されなくなってしまうので、必要があれば、すべてのそのコントロールで非同期ポストバックが起きた時にUpdatePanelを更新したいコントロールをAsyncPostBackTriggerに指定する必要があります。文章が下手ですいません……。
また、僕はこの部分が分かりにくいと思うのですが、すべてのUpdatePanelのUpdateModeを"Conditinal"に変えても、UpdatePanelの外にあるコントロールAでイベントが発生した場合はすべてのUpdatePanelが更新されます。UpdatePanelの外にあるコントロールAは通常のポストバックを起こすからです。ところが、UpdatePanelのうちの一つが、UpdatePanelの外にあるコントロールAをAsyncPostBackTriggerとして登録すると、ほかのUpdatePanelは更新されなくなってしまいます。つまり、AsyncPostBackTriggerとして登録されたのでコントロールAは非同期ポストバックを起こすようになり、ほかのUpdatePanelはUpdateMode="Conditional"なので更新されない、というわけです。

なんか書いているうちに複雑なところがあるのかどうかもよくわからなくなってきた……。最終的に、僕は基本的にすべてのUpdatePanelのUpdateModeを"Conditional"、ChildrenAsTriggersを"false"にして、非同期ポストバックしたいところは個別にトリガーとして登録しています。ただ、画面にエラーメッセージをだしたり、ValidationSummaryを出したりしているUpdatePanelだけはUpdateModeを"Always"にすることで、毎回更新されるようにしてます。