Dla kogoś, kto do tej pory tworzył oprogramowanie bazujące na WinForms, a przechodzi na WebForm, ważne jest zrozumienie cyklu życia strony w ASP.NET. Dlaczego? Proces, w którym strona ASP.NET jest dla przeglądarki generowana, składa się z wielu etapów, z których każdy pełni odmienną funkcję w tworzeniu i generowaniu strony. Jeśli umieścimy kod w nieodpowiednim miejscu, wówczas może braknąć kontrolek, które wg nas powinny się pojawić, lub też, jeśli już się pojawiają, to ich stan może być nieokreślony, czy też nieprzewidywalny.
Etapy i zdarzenia w życiu strony w ASP.NET
Poniższa tabela pokazuje kolejność etapów i zdarzeń dla strony ASP.NET.
Etap |
Zdarzenie/funkcja |
Uwagi |
Żądanie strony |
|
|
|
Konstruktor
|
|
Rozpoczęcie strony |
|
|
|
PreInit |
|
Inicjalizacja |
Init |
|
Ładowanie |
Load |
|
Walidacja |
|
|
Obsługa zdarzeń wywołania zwrotnego |
|
|
|
LoadComplete |
|
|
PreRender |
|
Generacja |
|
|
Wyładowanie |
Unload |
|
Przykład sprawdzający
Informacje
zaprezentowane w powyższej tabeli, można sprawdzić również
samodzielnie. Przeanalizujmy następujący przykład:
Default.aspx:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="asp_net_experiments._Default" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"><title></title></head> <body> <form id="form1" runat="server"> <div> <asp:Button ID="btnSubmit" runat="server" Text="Button" UseSubmitBehavior="true" OnClick="btnSubmit_Click" /> </div> </form> </body> </html>
Default.aspx.cs:
using System; using System.Web.UI; namespace asp_net_experiments { public partial class _Default: System.Web.UI.Page { protected override void OnPreInit( EventArgs e ) { Response.Write( "Wywołano OnPreInit().<br/>" ); base.OnPreInit( e ); } protected override void OnInit( EventArgs e ) { Response.Write( "Wywołano OnInit().<br/>" ); base.OnInit( e ); } protected override void OnInitComplete( EventArgs e ) { Response.Write( "Inicjalizacja zakończona (OnInitComplete()).<br/>" ); base.OnInitComplete( e ); } protected override void OnPreLoad( EventArgs e ) { Response.Write( "Wywołano OnPreLoad().<br/>" ); base.OnPreLoad( e ); } protected override void OnLoad( EventArgs e ) { Response.Write( "Wywołano OnLoad().<br/>" ); base.OnLoad( e ); } protected void Page_Load( object sender, EventArgs e ) { if ( Page.IsPostBack ) { Response.Write( "To jest wywołanie typu postback." ); } else { Response.Write( "To jest nowe wywołanie." ); } Response.Write( "<br/>Wywołano Page_Load().<br/>" ); } protected override void OnLoadComplete( EventArgs e ) { Response.Write( "Ładowanie zakończone (OnLoadComplete()).<br/>" ); base.OnLoadComplete( e ); } protected override void OnUnload( EventArgs e ) { // nie można w tym miejscu niczego wyprowadzić ponieważ // Response/Request są niedostępne podczas tego etapu. base.OnUnload( e ); } protected override void OnPreRender( EventArgs e ) { Response.Write( "Wywołano OnPreRender().<br/>" ); base.OnPreRender( e ); } protected override void OnPreRenderComplete( EventArgs e ) { Response.Write( "Wywołano OnPreRenderComplete().<br/>" ); base.OnPreRenderComplete( e ); } protected void btnSubmit_Click( object sender, EventArgs e ) { Response.Write( "Kliknięto przycisk Button.<br/>" ); } } }
W wyniku wykonania powyższego kodu powinniśmy otrzymać następujące
informacje w przeglądarce:
Pierwsze wywołanie |
Po kliknięciu przycisku |
Wywołano OnPreInit().
Wywołano OnInit(). Inicjalizacja zakończona (OnInitComplete()). Wywołano OnPreLoad(). Wywołano OnLoad(). To jest nowe wywołanie. Wywołano Page_Load(). Ładowanie zakończone (OnLoadComplete()). Wywołano OnPreRender(). Wywołano OnPreRenderComplete(). |
Wywołano OnPreInit().
Wywołano OnInit(). Inicjalizacja zakończona (OnInitComplete()). Wywołano OnPreLoad(). Wywołano OnLoad(). To jest wywołanie typu postback. Wywołano Page_Load(). Kliknięto przycisk Button. Ładowanie zakończone (OnLoadComplete()). Wywołano OnPreRender(). Wywołano OnPreRenderComplete(). |
Strona z kontrolką
Teraz
przeanalizujmy, jak są wywoływane zdarzenia w przypadku osadzenia
na stronie kontrolki:
Wywołano OnPreInit(). - GridView1.Rows.Count=0. - Wywołano GridView1_Load(). Wywołano OnInit(). - GridView1.Rows.Count=0. Inicjalizacja zakończona (OnInitComplete()). - GridView1.Rows.Count=0. Wywołano OnPreLoad(). - GridView1.Rows.Count=0. Wywołano OnLoad(). - GridView1.Rows.Count=0. To jest nowe wywołanie. Wywołano Page_Load(). - GridView1.Rows.Count=0. - Wywołano GridView1_Load(). Ładowanie zakończone (OnLoadComplete()). - GridView1.Rows.Count=0. Wywołano OnPreRender(). - GridView1.Rows.Count=0. - Wywołano GridView1_DataBinding(). - Wywołano GridView1_DataBound(). Wywołano OnPreRenderComplete(). - GridView1.Rows.Count=91.
Aby otrzymać powyższy wynik, do zdarzeń kontrolki podpięcie
zostało wykonane w PreInit (nie można tego zrobić w konstruktorze
strony, gdyż kontrolki nie zostały jeszcze powołane do życia, a
odwołania mają wartość null). Dodatkowo osadzona kontrolka miała
dowiązanie do źródła danych (to, czy dowiązanie miało miejsce
sprawdzane, jest przy pomocy wyświetlania ilości wierszy
(GridView1.Rows.Count), które zawiera kontrolka GridView), dlatego
do listy śledzonych zdarzeń kontrolki zostały dodane DataBinding i
DataBound. Została również dodana obsług zdarzenia DataBinding
strony.
Co należy tutaj
zauważyć:
- Nie ma informacji na temat wystąpienia zdarzenia PreInit dla osadzonej kontrolki, oznacza to że ma ono miejsce przed zdarzeniem PreInit strony (a tam dopiero została dodana obsługa zdarzeń.
- Osadzona kontrolka jest już ładowana w zdarzeniu PreInit strony.
- Bindowanie do danych jest wykonywane dopiero podczas etapu PreRender, więc dane w kontrolce są dopiero dostępne w zdarzeniu PreRenderComplete.
- UWAGA: Zdarzenie DataBind dla kontrolki nie zawsze musi być wywołane! Przy wywołaniu PostBack może ono nie być wywołane powtórnie.
- UWAGA: w przypadku wykorzystania Templated User Control metodę Page.DataBind należy jawnie wywołać, aby zapewnić, że kontener jest powiązany z layoutem.
Strona powiązana ze stroną wzorcową
Teraz
przeanalizujmy, jak są wywoływane zdarzenia w przypadku
wykorzystania strony wzorcowej (Master Page):
Wywołano OnPreInit(). MasterPage: Wywołano OnInit(). Wywołano OnInit(). Inicjalizacja zakończona (OnInitComplete()). Wywołano OnPreLoad(). Wywołano OnLoad(). To jest nowe wywołanie. Wywołano Page_Load(). MasterPage: Wywołano OnLoad(). MasterPage: To jest nowe wywołanie. MasterPage: Wywołano Page_Load(). Ładowanie zakończone (OnLoadComplete()). Wywołano OnPreRender(). Wywołano OnPreRenderComplete().
Warto tutaj zauważyć (pisałem też już o tym w post'cie: „Strony
Wzorcowe (Master Pages)”), że nie ma reguły, że najpierw
występuje zdarzenie dla MasterPage później dla Page (lub
odwrotnie). Zobaczmy, że najpierw następuje etap Init dla
MasterPage, później dla Page, natomiast w przypadku etapu Load,
najpierw jest ładowanie strony, a później wzorca. Oczywiście
takie zachowanie związane jest z faktem, że MasterPage jest tak na
prawdę kontrolką, które na pewnym etapie dziedziczy po
UserControl.
Podsumowanie
Mam nadzieję, że
udało mi się wskazać, co to jest ten cykl życia strony i dlaczego
jest on taki ważny. Teraz w ramach podsumowania chciałbym jeszcze
teraz dać kilka uwag związanych z tematem:
- W przypadku stron ASP.NET zastosowanie konstruktora, jako miejsca, gdzie możemy zainicjalizować pewne elementy, jest właściwie bezużyteczne, gdyż kontrolki nie są jeszcze nawet wytworzone.
- Nie należy całego kodu inicjalizującego umieszczać w domyślnie dodawanej (przez Visual Studio) funkcji PageLoad:
- Kontrolki wypełniane danymi (przez DataBinding) zostaną uzupełnione dopiero na późniejszym etapie.
- Własne kontrolki powinniśmy dodawać wcześniej – w obsłudze zdarzenia PreInit, podobnie w przypadku ustawiania stron Wzorcowych (które też są kontrolkami), czy Skórek (Themes)
Literatura
Niniejsze opracowanie powstało na
podstawie następujących źródeł:
- "MCTS Self-Paced Training Kit (Exam 70-562): Microsoft .NET Framework 3.5—ASP.NET Application Development", Autorzy: Mike Snell; Glenn Johnson; Tony Northrup; and GrandMasters, Wydawnictwo: Microsoft Press, 2009
- "Microsoft Visual C# 2005 Księga eksperta", Autor: Kevin HoffMan, Wydawnictwo: Helion, 2007
- http://msdn.microsoft.com/
bardzo pomocne!
OdpowiedzUsuńPropsy!
OdpowiedzUsuńNadążenie za wszystkimi etapami jest bardzo wymagające na początkowym etapie.
OdpowiedzUsuń