Dynamické vytváření ASP.NET server controls

V životě programujícího koně (a předpokládám, že i v životě lidském) se najde řada příležitostí, které vyžadují dynamické vytváření uživatelského rozhraní. V klasických ASP 3.0 bylo v tomto ohledu řešení snadné: prostě se do výsledného kódu vygenerovalo nějaké HTML.

To výtečně funguje i v případě ASP.NET (nic vám nebrání dát si na stránku asp:literal a načítat hodnoty přes Request.Form či tak něco). Pro mnohé situace to může být dokonce nejlepší možné řešení. Jindy ale prahnete po využití inteligence ASP.NET server controls. Po možnosti užívat dobrodiní validace, postbacku a jiných radostí, které vám nabízí svět .NET.

Vytváření

Serverový ovládací prvek je objekt jako kterýkoliv jiný: vznikne vytvořením instance třídy, zpravidla nacházející se v namespace System.Web.UI.WebControls nebo System.Web.UI.HtmlControls. Programově pak nastavíte všechny potřebné vlastnosti. Takto vytvořený prvek jest ovšem nutno umístit na vhodné místo stránky.

Všechny prvky odvozené od třídy System.Web.UI.Control (což se týká všech serverových ovládacích prvků) mají vlastnost Controls. Ta obsahuje kolekci podřízených ovládacích prvků. Obvykle najde využití v případě "kontajnerů", jako je třeba asp:panel nebo ještě lépe asp:placeholder. Posledně jmenovaný je přímo určený k hrátkám s dynamickými prvky - nic jiného v podstatě neumí. Do kolekce můžete nově vygenerovaný prvek přidat pomocí metod Add (na konec) nebo AddAt (na určenou pozici).

Pokud chcete mezi vygenerované prvky vložit něco statického HTML (což zpravidla chcete, byť by to mělo být prosté odřádkování), použijte k tomuto účelu vložení System.Web.UI.LiteralControl.

Následující kód vygeneruje deset textových polí oddělených odřádkováním a nastaví jim různé užitečné vlastnosti, příkladně ID (budeme ho potřebovat později) a obsah (Text).

For I As Int32 = 1 To 10
    ' Vytvoř nový textbox
    Dim T As New System.Web.UI.WebControls.TextBox

    ' Nastav jeho vlastnosti
    T.ID = "DynamicTextBox" & I
    T.Text = "Dynamicky generovaný TextBox č. " & I
    T.Width = New System.Web.UI.WebControls.Unit(400, System.Web.UI.WebControls.UnitType.Pixel)

    ' Přidej ho na placeholder a odřádkuj
    Me.PlaceHolder1.Controls.Add(T)
    Me.PlaceHolder1.Controls.Add(New System.Web.UI.LiteralControl("<br>"))
Next

Přežití Postbacku

Obvyklým postupem (a začátečnickou chybou) je umístit shora uvedený kód do obsluhy události Page.Load. Funguje to skvěle, želbohu jenom do prvního Postbacku. Při něm se všechny pracně vytvořené prvky ztratí.

Pro vysvětlení tohoto jevu jest třeba nahlédnout podrobněji na způsob, jakým se při zpracování ASPX stránky volají jednotlivé události. V článku o psaní HTTP modulů jsem popisoval události, které nastanou při zpracování HTTP požadavku. Poměrně složitý proces volání ASPX stránky (Web Formu) jsem v něm shrnul do stručné věty "V tomto okamžiku se zavolá příslušný HTTP handler a vykoná se vlastní kód stránky." Nyní nastal čas povědět si, co vlastně provádí HTTP handler při volání web formu.

Podrobný popis najdete v MSDN, názorný obrázek na weblogu Raymonda Lewallena. Pro nás jsou v tomto okamžiku důležité první čtyři události:

  • Init - vůbec první událost volaná po vytvoření instance stránky.
  • LoadViewState - načtení hodnot z ViewState.
  • LoadPostData - zpracování dat z postbacku.
  • Load - provedení vlastních operací společných pro všechny požadavky na stránku.

Ve většině případů ASP.NET programátor obslouží jako první událost Load. Ta ovšem v našem případě přichází příliš pozdě, až po zpracování ViewState a Postbacku. Dynamické vytváření prvků jest tedy třeba si odbýt už mnohem dříve, v handleru události Init.

Pokud stránky píšete ve VS.NET, je příslušná metoda již přítomna, leč skrytá v regionu Web Form Designer Generated Code. Standardně tento handler obsahuje pouze volání metody InitializeComponent a důtklivé doporučení, abyste ho neodstraňovali, neb ho k životu potřebuje designer VS.NET.

Přesuňte si metodu Page_Init kam je libo a zapište kód do ní (nezapomeňte tam ale nechat ono vyžadované volání InitializeComponent). Výsledek bude vypadat nějak takto:

Private Sub Page_Init(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Init
    'CODEGEN: This method call is required by the Web Form Designer
    'Do not modify it using the code editor.
    InitializeComponent()

    ' Vygeneruj 10 textboxů a umísti je na stránku
    For I As Int32 = 1 To 10
        ' Vytvoř nový textbox
        Dim T As New System.Web.UI.WebControls.TextBox

        ' Nastav jeho vlastnosti
        T.ID = "DynamicTextBox" & I
        T.Text = "Dynamicky generovaný TextBox č. " & I
        T.Width = New System.Web.UI.WebControls.Unit(400, System.Web.UI.WebControls.UnitType.Pixel)

        ' Přidej ho na placeholder a odřádkuj
        Me.PlaceHolder1.Controls.Add(T)
        Me.PlaceHolder1.Controls.Add(New System.Web.UI.LiteralControl("<br>"))
    Next
End Sub

Tímto postupem se controly vytvoří včas a úspěšně se na ně aplikují změny z ViewState a Postbacku.

Načtení hodnot

V porovnání s předchozím kódem je poslední fáze, tedy načtení hodnot z controlu po Postbacku, procházkou růžovou zahradou. Není ovšem možné se na dynamicky vytvořený prvek odkazovat obvyklým voláním Me.názevprvku, ale je nutno použít elaborovanější postup.

V zásadě můžete využít již dříve popisovanou kolekci Controls a postupně procházet jednotlivé ovládací prvky a na základě definovaných charakteristik (příkladně typu) je rozpoznat a vhodně s nimi naložiti.

Druhou možností je použít u nadřazeného prvku (v našem případě je to asp:placeholder) metodu FindControl, která najde ovládací prvek s definovaným jménem (ID). My víme, jak se naše textboxy jmenují (DynamicTextBox1DynamicTextBox10) a můžeme tuto metodu s výhodou použít, například k vypsání všech zadaných hodnot:

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    Dim SB As New System.Text.StringBuilder

    For I As Int32 = 1 To 10
        ' Najít TextBox podle jména
        Dim T As System.Web.UI.WebControls.TextBox
        T = DirectCast(Me.PlaceHolder1.FindControl("DynamicTextBox" & I), System.Web.UI.WebControls.TextBox)

        SB.Append(T.ID & ".Text = " & T.Text & "<br>")
    Next

    Me.Label1.Text = SB.ToString()
End Sub
Titulek:
Text komentáře:
Vaše jméno:
Váš e-mail: (nebude zveřejněn)

WWW stránka:
Opište text z obrázku:
odpovědětodpovědět Gravatar

nelze tisknout kód

29.3.2005 11:01:2129.3.2005 11:01:21 AjulaAjula ---.alphanet.cz

Článek super ale nelze vytisknout (tak jako u ostatních tady) zdrojový kód. Vytiskne se jen viditelny a to co se roluje už není na papíře :o(

Tohoto problému jsem si vědom, ale neznám žádné jeho řešení. Prohlížeče nejsou ochotny tisknout text uzavřený v PRE, je-li širší než stránka.

Bohužel, nevím o jiném způsobu prezentace zdrojových kódů. Samozřejmě je mohu ručně zalamovat, ale to nějak nejsem ochoten dělat pravidelně.

odpovědětodpovědět Gravatar

RE: nelze tisknout kód

Mozilla Firefox mi tento článek vytiskla korektně.

Spíše mě zaráží jedna věc z úplně jiného soudku. Pokud si vytvořím composite custom control a nastavím jednotlivým ovládacím prvkům visuální vlastnosti (velikost, apod.), server pošle non-IE prohlížeči informaci bez vloženého stylu.

Zatímco u IE mám vyrenderovaný <input type=textbox style="...." ...>, v Mozilla Firefoxu je jen <input type=textbox ....>

Odpozorováno na .NET Frameworku 1.1

To je to slavné adaptivní renderování v ASP.NET. Naštěstí se to dá vypnout, ještě dneska to blognu.

odpovědětodpovědět Gravatar

RE: nelze tisknout kód

Pokud chci dynamicky vytvořít více prvků stejné třídy a přidat je do panelu, pak napíšu

Literal litHTML = new Literal();

litHTML.Text=strTemp;

pnlMessage.Controls.Add(litHTML);

pokud však změním hodnotu strTemp, změní se mi hodnota u výše naplněného colntolu u panelu.

Nevíte, co dělám špatně?

odpovědětodpovědět Gravatar

RE: nelze tisknout kód

Odvolávám předchozí příspěvek pro svoji slepost.

odpovědětodpovědět Gravatar

RE: nelze tisknout kód

Díky za článek o adaptivním renderingu.

odpovědětodpovědět Gravatar

Ukazka v C#

Tady je ukázka použití dynamicky generovaných kontrolů včetně zpracování událostí v C#

http://sweb.cz/xaman/DynamicControlsSample/

Nově jsem tam přidal i ukázku s TextBoxy:)

JM

odpovědětodpovědět Gravatar

Dynamický TextBox

Dobrý den,

tento příklad perfektně funguje, ale já bych potřeboval něco trošku jiného a je to už ASP.NET 2.0 ....

Pokud importuju CSV soubor tak nemohu vytvářet control TextBox na úrovni Page_Init, ale až ve chvíli, kdy stisknu tlačítko "Import". Textboxy se zobrazí, jenomže s nimi nelze pracovat protože byly vytvořeny později. Existuje na to nějaký fígl ??

Díky Moc

Existuje - po postbacku je prostě musíte vytvořit "podruhé" - při inicializaci.

odpovědětodpovědět Gravatar

dynamické generování gifů

Díky, přesně tohle jsem potřeboval :-)

odpovědětodpovědět Gravatar

Generování controlů podle jména

6.8.2008 17:03:186.8.2008 17:03:18 KostkáčKostkáč ---.net.upc.cz

Ahoj, potřeboval bych ošetřit problém, kde mám název controlu dynamicky se měnící a podle toho ho vytvářet a vkládat do placeHolderu. Můžete mi někdo poradit prosím.

odpovědětodpovědět Gravatar

Zarovnání

28.12.2009 10:47:3628.12.2009 10:47:36 PetrPetr 89.190.94.---

Ahoj. Mohli by jste mi prosím někdo poradit, jak takový dinamicky vytvořený prvek např. tlačítko zarovnat třeba doleva:? Děkuju moc za odpověď

  • Altairis
  • Nemesis
  • Microsoft MVP
  • IIS
  • ASP.NET