Bublání změnových událostí v ASP.NET 1.
V diskusních fórech zaměřených na .Net technologii se často objevují dotazy, jejichž společným jmenovatelem je snaha o odchycení změnových událostí ovládacích prvků vnořených v šablonách prvků DataGrid, Repeater či Datalist. Pisatelé většinou končí zjištěním, že například událost Click tlačítka odchytí v události ItemCommand DataGridu, ale k události SelectedIndexChanged DropDownListu se přes DataGrid žádným elegantním způsobem nedostanou. Tento článek ukazuje, jak lze toto omezení obejít.
Nutná dávka teorie
Pokud se v serverovém událostním modelu ASP.NET neorientujete, přečtěte si článek, který jej prodrobně popisuje. Používání šablon a kompozitních serverových ovládacích prvků ulehčuje takzvané bublání událostí z vnořených ovládacích prvků. Co bublání událostí přesně znamená? Představte si, že do sloupce se šablonou (TemplateColumn
) v DataGridu
vložíte tlačítko, na jehož událost Click
chcete reagovat. Tlačítko je po připojení datového zdroje k DataGridu
vytvořeno v každém řádku. Bez bublání událostí bychom si museli manuálně přihlásit odběr události Click každého tlačítka. To konkrétně znamená, že bychom museli projít každou položku DataGridu, v ní vyhledat prvek tlačítko a zaregistrovat k události Click delegáta. Pokud jste podobná řešení vyzkoušeli při registraci změnových událostí, protože jste si mysleli, že jiný způsob neexistuje, jistě budete se mnou souhlasit, že vlastnosti jako čitelnost a spravovatelnost kódu značně utrpěly.
Tvůrci ASP.NET přišli s jednoduchým řešením – události z vnořených ovládacích prvků je možné posílat (bublat) do nadřazených prvků. Právě k tomuto účelu byla vytvořena chráněná metoda RaiseBubbleEvent
deklarovaná ve třídě Control
. Všechny prvky v ASP.NET dědí přímo nebo nepřímo ze třídy Control
, proto mohou metodu RaiseBubbleEvent
použít k bublání vlastních událostí. Pro zajímavost si uvedeme kód metody RaiseBubbleEvent
:
protected void RaiseBubbleEvent (object source, EventArgs args)
{
Control curParent = Parent;
while (curParent != null)
{
if (curParent .OnBubbleEvent (source, args))
return;
curParent = curParent.Parent;
}
}
Metoda má dva argumenty. Argument source
představuje zdroj události, argument args
nese další informace o události. Všechny třídy, které nesou informace o události, musí dědit přímo či nepřímo z třídy EventArgs
, a proto může být metoda použita k bublání jakékoli události. V kódu je volána metoda OnBubbleEvent
rodiče prvku, který bublá událost. Metoda OnBubbleEvent
je deklarována ve třídě Control
a její výchozí implementace pouze vrátí hodnotu false
.
Ovládací prvky mohou metodu OnBubbleEvent
přepsat a reagovat specifickým způsobem na bublanou událost. V případě, že metoda OnBubbleEvent
vrátí true
, je bublání události ukončeno, protože rodič dal najevo, že událost zpracoval. Když metoda OnBubbleEvent
vrátí false
, je událost bublána dalšímu rodiči v hierarchii ovládacích prvků (přesněji řečeno jde o rodiče rodiče původního prvku). Celá sekvence příkazů se opakuje, dokud metoda OnBubbleEvent
nevrátí true
nebo dokud metoda RaiseBubbleEvent
nedojde na počátek hierarchie ovládacích prvků, což je indikováno hodnotou null
v proměnné curParent
. Prvek, který bublanou událost zpracuje, ji většinou zapouzdří do vlastní generické události, jejíž odběr si přihlásí zájemce o události vnořených prvků. Příkladem může být událost ItemCommand
DataGridu
.
Popsané řešení je pro autory ASP.NET stránek velmi přínosné, ale má jednu nevýhodu. Standardně jsou v ASP.NET bublány pouze události, jejichž třída nesoucí dodatečné informace o události dědí z třídy CommandEventArgs
. V praxi to znamená, že je bublána událost Click
tlačítek (Button
, LinkButton
, ImageButton
), ale nejsou například bublány změnové události (SelectedIndexChanged
DropDownListu, TextChanged
TextBoxu…).
Příklad bublání událostí
Na DropDownListu
si ukážeme, jak můžeme bublání změnových událostí do ASP.NET sami doplnit (zdrojový kód ke stažení). Náš příklad se pro názornost soustředí na bublání události SelectedIndexChanged
potomka DropDownListu
(třída DropDownListEx
), který je vložen do sloupce se šablonou v prvku odvozeném z DataGridu
(třída DataGridEx)
. V některém dalším článku si ukážeme ovládací prvek, který dokáže bublat události do DataGridu
, Repeateru
i DataListu
.
Nejdříve vytvoříme třídu, která ponese informace o bublané události. Třída bude obsahovat vlastnost s názvem Source
, která vrací odkaz na DropDownListEx
, jež událost vyvolal, a vlastnost Item
, která obsahuje odkaz na položku DataGriduEx
(DataGridItem
), jejíž součástí je DropDownListEx
. Vlastnost Source
je typu Object
, abychom třídu BubbledIndexChangeEventArgs
mohli případně použít i k přenosu informací o bublaných událostech z jiných ovládacích prvků.
public class BubbledIndexChangeEventArgs : EventArgs
{
private DataGridItem m_item;
private object m_source;
public BubbledIndexChangeEventArgs(DataGridItem item, object source) : base()
{
if (item == null)
throw new ArgumentNullException(„item“);
m_item = item;
if (source == null)
throw new ArgumentNullException(„source“);
m_source = source;
}
public DataGridItem Item
{
get
{
return m_item;
}
}
public object Source
{
get
{
return m_source;
}
}
}
Dále vytvoříme potomka třídy DropDownList
, který bude bublat událost SelectedIndexChanged
.
public class DropDownListEx : DropDownList
{
public DropDownListEx() : base()
{
}
protected override void OnSelectedIndexChanged(EventArgs e)
{
base.OnSelectedIndexChanged (e);
DataGridItem item = null;
Control hc = null;
hc = this.NamingContainer as Control;
while (hc != null)
{
item = hc as DataGridItem;
if (item != null)
break;
hc = hc.NamingContainer as Control;
}
if (item != null)
RaiseBubbleEvent(this, new BubbledIndexChangeEventArgs(item, this));
}
}
Přepsaná metoda OnSelectedIndexChanged
, která je odpovědná za vyvolání události SelectedIndexChanged
, nejprve zavolá metodu OnSelectedIndexChanged
bázové třídy, aby byli notifikováni klienti, kteří si přihlásili odběr události SelectedIndexChanged
přímo u DropDownListuEx. Dále metoda v cyklu while
hledá položku DataGriduEx
, ve které je DropDownListEx
obsažen. Využita je vlastnost NamingContainer
, která vrací odkaz na rozhraní INamingContainer
. INamingContainer
je značkovací rozhraní, které nemá žádné metody a kterým ovládací prvky, jež jsou rozhraním označeny, signalizují běhovému prostředí ASP.NET, že má jejich vnořený prvkům generovat unikátní jména závislá na jejich pozici v hierarchii ovládacích prvků. Rozhraním je označena i třída DataGridItem
.
Pokud není položka datagridu nalezena, má proměnná item
hodnotu null
a DropDownListEx
událost nebublá, protože je zřejmé, že nebyl vložen do DataGriduEx. V opačném případě je vytvořena instance třídy BubbledIndexChangeEventArgs
, které je předán odkaz na nalezenou položku DataGriduEx
a na aktuální instanci prvku DropDownListEx
. Poté je událost bublána metodou RaiseBubbleEvent
.
Nyní musíme vytvořit potomka DataGridu
, který bublanou událost zachytí, zpracuje a zpřístupní ji formou vlastní generické události.
public class DataGridEx : DataGrid
{
protected static object DropDownSelectedIndexChangedKey = new Object();
public delegate void DropDownSelectedIndexChangedEventHandler(object source, BubbledIndexChangeEventArgs e);
public event DropDownSelectedIndexChangedEventHandler DropDownSelectedIndexChanged
{
add
{
Events.AddHandler(DropDownSelectedIndexChangedKey, value);
}
remove
{
Events.RemoveHandler(DropDownSelectedIndexChangedKey, value);
}
}
public DataGridEx() : base()
{
}
protected override bool OnBubbleEvent(object source, EventArgs args)
{
if (args is BubbledIndexChangeEventArgs)
{
OnDropDownSelectedIndexChanged((BubbledIndexChangeEventArgs)args);
return true;
}
else
return base.OnBubbleEvent (source, args);
}
protected virtual void OnDropDownSelectedIndexChanged(BubbledIndexChangeEventArgs e)
{
DropDownSelectedIndexChangedEventHandler eh = (DropDownSelectedIndexChangedEventHandler)Events[DropDownSelectedIndexChangedKey];
if (eh != null)
eh(this, e);
}
}
Ve třídě DataGridEx
je deklarována nová událost DropDownSelectedIndexChanged
a delegát pro tuto událost s názvem DropDownSelectedIndexChangedEventHandler
. Událost DropDownSelectedIndexChanged
je vyvolána, když je zachycena bublaná událost SelectedIndexChanged
vnořeného DropDownListuEx
.
Chráněný statický člen s názvem DropDownSelectedIndexChangedKey
je unikátním klíčem události. Jestliže jste neslyšeli o optimalizované implementaci událostí, přečtěte si článek, který se tomuto tématu podrobně věnuje.
Třída DataGridEx přepisuje metodu OnBubbleEvent
, ve které zkontroluje, zda třída s informacemi o události je typu BubbledIndexChangeEventArgs
. V případě, že byla předána instance třídy BubbledIndexChangeEventArgs
, je vyvolána událost DropDownSelectedIndexChanged
metodou OnDropDownSelectedIndexChanged
a metoda OnBubbleEvent
vrátí hodnotu true
, která ve tříde DropDownListEx
zamezí dalšímu bublání události. Pokud nebyla předána instance třídy BubbledIndexChangeEventArgs
, metoda deleguje zpracování bublané události na implementaci v bázové třídě.
Uživatel si musí prvky DataGridEx
a DropDownListEx
na ASP.NET stránce zaregistrovat a po vložení prvku DropDownListEx
do sloupce se šablonou si jen přihlásí odběr události DropDownSelectedIndexChanged
. Ještě raději připomenu, že musíte nastavit vlastnost AutoPostBack
prvku DropDownListEx
na true
, pokud chcete být na výběr nové položky upozorněni ihned.
Snad tento článek sníží počet v úvodu zmiňovaných příspěvků v diskusních fórech, ve kterých se lamentuje nad absencí bublání změnových událostí, a že si jejich pisatelé uvědomí bezprecedentní míru flexibility, kterou jim .Net Framework poskytuje.
Starší komentáře ke článku
Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.
Mohlo by vás také zajímat
-
Aukce CZ domén: Jak vydražit expirovanou CZ doménu?
12. června 2024 -
Jak nainstalovat šablonu ve WordPressu
23. července 2024
Nejnovější
-
Doména .io v ohrožení: Co přinese předání Čagoských ostrovů?
10. října 2024 -
Jak se chránit před podvody na internetu – část 1
8. října 2024