Klávesové zkratky na tomto webu - rozšířené Na obsah stránky

Generický kontejner a repeater

| ASP.NET 2.0

Zrovna předevčírem jsem měl v plánu napsat tenhle článek, ráno jsem si napsal ukázkovou třídu, kterou najdete níže, a pak šel do práce. Připravoval jsem dva celkem důležité stagingy a nakonec ještě ta Havana Party. No, a když jsem se vrátil z nákupu, zastihl jsem přímý přenos ze spouštění La Trine. A bylo po článku. Teda jen dočasně, protože i včera jsem měl nějakou snahu, tak jsem napsal první půlku článku.

Někdy před rokem jsem publikoval ukázku, jak si vytvořit vlastní kontrol s Layout Template. Před pár týdny zase pomocníka na vyhledávání komponent ve stránce i mimo jmenný kontejner. To jsou takoví pomocníci, kteří mi usnadňují tvorbu serverových ovládacích prvků. A dneska si ukážeme jejich dalšího bratříčka – generický kontejner.

Kontejner

Kontenery se používají u šablonovaných prvků svázaných s daty. Slouží jako držitel dat pro konkrétní instanci šablony. Jistě jste se setkali s Evalováním, když jste prováděli databinding. Problém je v tom, že s sebou přináší značnou režii spojenou s boxingem. Výhodnější je používat plně typový kontejner. Zvýšíte tím nejen výkon stránky, ale i zístkáte vyšší komfort v podobě Intellisense v šabloně.

Já jako základ takových kontejnerů používám generický kontejner, který má následující rozhraní.

public interface IGenericContainer<T> : INamingContainer {
  T DataItem { get; }
}

Toto rozhraní pak implementuju následovně:

public class GenericContainer<T> : Control, IGenericContainer<T> {
  public GenericContainer<T>(T dataItem) {
    DataItem = dataItem;
  }
  public T DataItem { get; priváte set; }
}

Dalším vhodným kontejnerem je prázdný kontejner:

public class EmptyContainer : Control, INamingContainer {
}

S těmito kontejnery se dá pokrýt obrovské množství potřeb. Jak je využít v praxi si ukážeme v následující čás­ti.

Generický repeater

Nyní vezmu kostičky naší stavebnice a postavím z nich základ pro vypisovače silně typových dat.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace Rarous.Web.UI {
  [ParseChildren(true)]
  public abstract class GenericRepeaterBase<T> : CompositeControl, ILayoutTemplateable {

    protected bool IsBound { get; set; }

    public IEnumerable<T> DataItem { get; set; }

    public abstract ITemplate ItemTemplate { get; set; }

    public abstract ITemplate AlternatingItemTemplate { get; set; }

    [DefaultValue(typeof(ITemplate), "")]
    [PersistenceMode(PersistenceMode.InnerProperty)]
    [TemplateContainer(typeof(INamingContainer))]
    [TemplateInstance(TemplateInstance.Multiple)]
    public ITemplate SeparatorTemplate { get; set; }

    #region ILayoutTemplateable Members
    [PersistenceMode(PersistenceMode.Attribute)]
    public string LayoutContainerId { get; set; }

    [DefaultValue(typeof(ITemplate), "")]
    [PersistenceMode(PersistenceMode.InnerProperty)]
    [TemplateContainer(typeof(INamingContainer))]
    [TemplateInstance(TemplateInstance.Single)]
    public ITemplate LayoutTemplate { get; set; }
    #endregion

    #region CompositeControl Members
    protected override void CreateChildControls() {
      Control container = TemplatingHelper.CreateLayoutContainer(this, this);

      bool alternate = false;
      foreach (T item in DataItem) {
        ITemplate template = alternate ? AlternatingItemTemplate ?? ItemTemplate : ItemTemplate;
        TemplatingHelper.Instantiate(new GenericContainer<T>(item), template, container);
        TemplatingHelper.Instantiate(new EmptyContainer(), SeparatorTemplate, container);
        alternate = !alternate;
      }
    }
    public override void DataBind() {
        base.DataBind();
        IsBound = true;
    }
    protected override void OnInit(EventArgs e) {
      EnableViewState = false;
      base.OnInit(e);
    }
    protected override void OnPreRender(EventArgs e) {
      if (IsBound == false) {
        DataBind();
      }
      base.OnPreRender(e);
    }
    protected override void Render(HtmlTextWriter writer) {
      base.RenderContents(writer);
    }
    #endregion
  }
}

Na takovémto základě se dá stavět už slušná řada konkrétních ovládacích prvků, kde už jen doplníme šablonový typ a můžeme přidat další vlastnosti dle potřeby.

V ukázce si můžete povšimnout, že jako typ kontejneru šablony uvádím pouze rozhraní. Dělám to kvůli zpřehlednění IntelliSense. Když pak používám výraz pro svázání s daty v šabloně, dostanu pouze vlastnosti a metody kontejneru, které jsou definovány rozhraním, nikoli spoustu zbytečných vlastností a metod, které sebou nese třída Control ze které musí být kontejner poděděný.

Doufám, že se bude hodit a pokud najdete nějakou chybu, neváhejte a reportujte :)

Našli jste v článku chybu? Máte námět na reportáž? Založte mi ticket.