Vlastní primitivní Repeater – templated data-bound control

Primitivní Repeater

Začněme velmi jednoduchým příkladem, primitivní napodobeninou Repeateru, na níž si ukážeme základy vytváření složených datově-vázaných controlů (composite data-bound controls) a použití šablon v controlech (templates).

Dejme tomu chceme vytvořit control, který bude podle šablon vypisovat seznam zákazníků. Oproti standardnímu Repeateru z ASP.NET však bude pracovat se strong-typed prvky a přidáme si jednoduchou podporu pro načítání šablon ze souborů. Spokojíme se prozatím bez podpory ViewState (data bude tedy nutno bindovat v každém roundtripu) i bez eventů (IremCreated, ItemDataBound, Command, …).

<havit:MyRepeater ID="cosi" runat="server">
      <ItemTemplate>
         Jméno: <%# Container.Zakaznik.Jmeno %><br/>
         Datum narození: <%# Container.Zakaznik.DatumNarozeni.ToShortDateString() %></br>
      </ItemTemplate>
</havit:MyRepeater>

Základy práce se šablonami (templates)

Princip šablon je velmi jednoduchý. Když si uvědomíme, že controly tvoří hiearchii (kořenem je Page a podřízené controly jsou vždy v kolekci Controls), pak použití šablony není nic jiného než připojení nové větve controlů do této hiearchie.

V controlu se šablona samotná vytvoří pomocí property typu ITemplate.

[PersistenceMode(PersistenceMode.InnerPropety)]
[TemplateContainer(typeof(MyRepeaterItem))]
public ITemplate ItemTemplate
{
      get { return _itemTemplate; }
      set { _itemTemplate = value; }
}

Atribut PersistenceMode nastavený na InnerPropety nám říká, že ItemTemplate bude ve zdrojovém kódu controlu zapsán jako vnořený tag. Atribut TemplateContainerzase parseru říká, jakého typu je objekt Container, který budeme používat uvnitř template.

Pro začátek je to vše, co potřebujeme pro definici šablony, parser se sám postará o naplnění property ItemTemplate, pokud ji v .aspx stránce použijeme.

Rozhraní ITemplate definuje jedinou metodu InstantiateIn(Control container), která nedělá nic jednoduššího, než že připojí control-hiearchy tvořené šablonou jako větev do containeru (toho containeru, jehož typ jsme udali v atributu TemplateContainer).

Nutno zdůraznit, že container, do kterého template připojujeme, musí implementovat rozhraní INamingContainer, aby nám korektně fungoval objekt Container v kódu šablony.

Základy vytváření data-bound controls

Pro vytváření data-bound control je v ASP.NET 2.0 připraven pattern v podobě abstraktní třídy CompositeDataBoundControl. Composite proto, že nové controly vytváříme skládáním jiných controlů do hiearchie, nikoliv renderováním HTML kódu controlů úplně nových.

Základní implementace se v podstatě celá odehrává v metodě CreateChildControls(IEnumerable dataSource, bool dataBinding), přičemž základní pattern chování by měl být zhruba následující

override int CreateChildControls(IEnumerable dataSource, bool dataBiding)
{
      if (dataBinding)
      {
            // vytahej z dataSource data a ulož si je ve své podobě, obvykle do this.Items
            // this.Items bychom si měli ukládat do ViewState, drží nám data mezi postbacky
      }



      // vytvoř controly pro každou položku this.Items a přidej ji do this.Controls
      // foreach (MyItem item in Items) { ... Controls.Add(..); }
      // return počet vytvořených items
}

Pokud nemáme ViewState, tak lze samozřejmě obě fáze sloučit a rovnou tvořit control-hiearchy z dat v dataSource.

MyRepeater – kombinace CompositeDataBoundControl a ITemplate

No a není nic jednoduššího, než obě možnosti zkombinovat a vytvořit tak control, který bude data-bound a pro reprezentaci dat bude používat šablony:

public class MyRepeater : CompositeDataBoundControl
{
      [PersistenceMode(PersistenceMode.InnerPropety)]
      [TemplateContainer(typeof(MyRepeaterItem))]
      public ITemplate ItemTemplate
      {
            get { return _itemTemplate; }
            set { _itemTemplate = value; }
      }
      private ITemplate _itemTemplate;

      /// <summary>
      /// Soubor .ascx, který představuje případnou externí ItemTemplate.
      /// </summary>
      public string ItemTemplateFile
      {
            get { return _itemTemplateFile; }
            set { _itemTemplateFile = value; }
      }
      private string _itemTemplateFile;
      public Collection<MyRepeaterItem> Items
      {
            get
            {
                  if (_items == null)
                  {
                        _items = new Collection<MyRepeaterItem>();
                  }
                  return _items;
            }
      }
      private Collection<MyRepeaterItem> _items;
      protected override CreateChildControls(IEnumerable dataSource, bool dataBinding)
      {
            // vytaháme data z dataSource
            if (dataBinding)
            {
                  if (dataSource != null)
                  {
                        foreach (object o in dataSource)
                        {
                              if (!(o is Zakaznik))
                              {
                                    throw new ApplicationException("DataSource musí obsahovat prvky typu Zakaznik.");
                              }
                              MyRepeaterItem item = new MyRepeaterItem(o as Zakaznik);
                              Items.Add(item);
                        }
                  }
            }
            // vytvoříme controly
            foreach (MyRepeaterItem in Items)
            {
                  if (!String.IsNullOrEmpty(ItemTemplateFile))
                  {
                        _itemTemplate = Page.LoadTemplate(ItemTemplateFile);
                  }
                  _itemTemplate.InstantiateIn(item);
                  item.DataBind();
                  Controls.Add(item);
            }
            return Items.Count;
      }
}

Kód ukazuje i druhou možnost získávání šablon – jejich načítání z .ASCX souboru pomocí Page.LoadTemplate(). Šablona totiž může úplně stejně, jako ve vlastní .aspx stránce, být uložena v samostatném .ASCX souboru (jen pro Container je tam potřeba použít explicitní přetypování, protože tam parser neví o atributu TemplateContainer).

Pro úplnost ještě MyRepeaterItem:

public class MyRepeaterItem : Control, INamingContainer
{
      public Zakaznik Zakaznik
      {
            get { return _zakaznik; }
            set { _zakaznik = value; }
      }
      public MyRepeaterItem(Zakaznik zakaznik)
      {
            this._zakaznik = zakaznik;
      }
}
Odkazy na související články

Napsat komentář

Vyplňte detaily níže nebo klikněte na ikonu pro přihlášení:

WordPress.com Logo

Komentujete pomocí vašeho WordPress.com účtu. Log Out / Změnit )

Twitter picture

Komentujete pomocí vašeho Twitter účtu. Log Out / Změnit )

Facebook photo

Komentujete pomocí vašeho Facebook účtu. Log Out / Změnit )

Google+ photo

Komentujete pomocí vašeho Google+ účtu. Log Out / Změnit )

Připojování k %s