V předchozím článku byly vysvětleny základní postupy pro bublání změnových událostí do ovládacích prvků DataGrid, Repeater či DataList. Vytvořili jsme sice potomka DropodownListu, který dokázal bublat událost SelectedIndexChanged do potomka standardního DataGridu, ale v žádném případě nešlo o univerzální řešení. Jak jsem slíbil, tentokrát uvidíte vytvoření DropDownListu a CheckBoxu, které budou bublat změnové události (nejen) do standardních ASP.NET ovládacích prvků.

Požadavky na bublání změnových událostí

Jaké jsou nevýhody řešení popsaného v předchozím článku o bublání událostí? Hlavním nedostatkem je, že nové ovládací prvky DropDownListEx a DataGridEx jsou spolu velmi těsně svázány. DropDownListEx má smysl používat pouze ve sloupci se šablonou prvku DataGridEx, protože k bublané události SelectedIndexChanged se lze dostat pouze přes specifickou událost DropDownSelectedIndexChanged prvku DataGridEx. Když použijeme ovládací prvek DropDownListEx se standardním DataGridem, Repeaterem či DataListem, k události se nedostaneme. DropDownListEx také před bubláním události hledá položku DataGridItem, ve které je obsažen, aby byl uživatel informován, na jakém řádku DataGridu k události došlo. Opět důkaz, že jsou oba prvky nepřijatelným způsobem provázány.

Nyní zmíněné nedostatky transformujeme na požadavky, jež by měly splňovat nové ovládací prvky bublající změnové události. Ovládací prvek musí být schopen bublat změnové události do všech standardně dodávaných i vlastních ovládacích prvků jednotným způsobem. Standardní serverové ovládací prvky musí umožňovat svému uživateli zpracování bublaných změnových událostí způsobem zcela shodným se zpracováním bublaných „akčních“ událostí. Konkrétněji, obsluha bublaných změnových událostí musí probíhat přes událost ItemCommand. Z toho také plyne, že v události ItemCommand musíme být schopni poznat, v jakém řádku DataGridu, Repeateru či DataListu je prvek, jehož bublanou událost zachycujeme. Jen s ovládacím prvkem, který všechny uvedené požadavky splní, můžeme být spokojeni.

Podpora pro naše požadavky v ASP.NET

Pro lepší pochopení, čeho chceme dosáhnout, se nyní soustředíme na vysvětlení bublání události Click tlačítka DataGridu, ale poznatky můžeme analogicky vztáhnout i na třídy Repeater a DataList.

Jak jsme si již podrobně vysvětlili v předchozím článku, prvky iniciují bublání událostí chráněnou metodou RaiseBubbleEvent. Tlačítko při události Click vytvoří novou instanci třídy CommandEventArgs, které předá do konstruktoru hodnoty vlastností CommandName a CommandArgument. (Jak jistě víte, vlastnost CommandName je zvláště užitečná při nutnosti rozlišovat tlačítka v události ItemCommand a vlastnost CommandArgument většinou obsahuje upřesňující informace, například primární klíč aktuálního řádku.) Poté tlačítko zavolá metodu RaiseBubbleEvent a předá jí odkaz na sebe a vytvořenou instanci CommandEventArgs. Jak je vidět, tlačítko se nezajímá o ovládací prvek, ve kterém je umístěno.

Bublanou událost zachytí položka DataGridu (třída DataGridItem) v metodě OnBubbleEvent.

protected virtual bool OnBubbleEvent(object source, EventArgs e)
{
  DataGridCommandEventArgs ex;
  if (e as CommandEventArgs != null)
  {
    ex = new DataGridCommandEventArgs(this, source, (CommandEventArgs) e);
    this.RaiseBubbleEvent(this, ex);
    return true;
  }
  return false;
}

DataGridItem zpracovává pouze události, které předávají své parametry v instancích třídy CommandEventArgs, případně ve třídách, které jsou z CommandEventArgs přímo či nepřímo odvozeny. To je přesně náš případ. V metodě je vytvořena instance třídy DataGridCommandEventArgs, která v konstruktoru přijímá odkaz na DataGridItem, původní zdroj bublané události (Button) a původní parametry události. Zde je námi hledaný „trik“, jak lze zjistit položku DataGridu, ve které se ovládací prvek, bublající událost, nachází, aniž by byl ovládací prvek za nalezení položky odpovědný. Zdroj události je typu Object, což znamená, že zdrojem události může být každý ovládací prvek. DataGridItem zahájí nové bublání události s upřesněnými parametry.

Bublanou událost nyní zachytí jako poslední instance DataGrid.

protected virtual bool OnBubbleEvent(object source, EventArgs e)
{
  // Kód zkrácen
  DataGridCommandEventArgs dge;
  if (e as DataGridCommandEventArgs != null)
  {
    dge = (DataGridCommandEventArgs) e;
    this.OnItemCommand(dge);
    return true;
  }
  return false;
  // Kód zkrácen
}

Pokud je argument „e“ instancí třídy DataGridCommandEventArgs, jak je tomu v našem případě, je vyvolána událost ItemCommand, která uživateli poskytne veškeré informace o bublané události nasbírané „cestou“. To je vše – jak je zřejmé, jakákoli bublaná událost, která nese parametry události v instanci třídy CommandEventArgs, může být zpřístupněna v události ItemCommand rodičovského prvku. Získané znalosti využijeme při psaní vlastních prvků.

Příklad

Ve zdrojových kódech naleznete ovládací prvky CheckBoxEx a DropDownListEx, které dokáží bublat událost do jakéhokoli dalšího ovládacího prvku. Protože se kód obou tříd příliš neliší, budu v článku popisovat pouze třídu DropDownListEx.

public class DropDownListEx : DropDownList
{
  public DropDownListEx() : base()
  {
  }
  [Category(„Behavior“)]
  public virtual string CommandName
  {
    get
    {
      string commandName = (string) ViewState[„CommandName“];
      if (commandName != null)
        return commandName;
      return String.Empty;
    }
    set
    {
      ViewState[„CommandName“] = value;
    }
  }
  [Category(„Behavior“)]
  [Bindable(true)]
  public virtual string CommandArgument
  {
    get
    {
      string commandArgument = (string) ViewState[„CommandArgument“];
      if (commandArgument != null)
        return commandArgument;
      return String.Empty;
    }
    set
    {
      ViewState[„CommandArgument“] = value;
    }
  }
  protected override void OnSelectedIndexChanged(EventArgs e)
  {
    base.OnSelectedIndexChanged(e);
    RaiseBubbleEvent(this, new CommandEventArgs(this.CommandName, this.CommandArgument));
  }
}

Třída DropDownListEx dědí z třídy DropDownList a přidává dvě veřejné vlastnosti CommandName a CommandArgument, které jsou běžné u „akčních“ událostí. Do vlastnosti CommandName můžeme například zadat řetězec „ddlCity“, podle kterého v události ItemCommand poznáme, že je bublána událost SelectedIndexChanged z DropDownListu se seznamem měst.

Obě vlastnosti jsou dekorovány metaatributem Category, který designer ve VS.NET používá pro zjištění kategorie, do které má vlastnost umístit na stránce vlastností. Obě vlastnosti budou umístěny do kategorie „Behavior“. Vlastnost CommandArgument je navíc dekorována metaatributem Bindable s hodnotou „true“, která informuje designér, že hodnotu vlastnosti je možné získat z datového zdroje.

Myslím, že vytvořené prvky, které bublají změnové události, si v ničem nezadají se standardními prvky v ASP.NET, které bublají „akční“ události, a že se všechny vytčené požadavky podařilo bez kompromisů splnit.

Starší komentáře ke článku

Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.

Žádný příspěvek v diskuzi

Odpovědět