IconHyperLink - .NET wrapper nad Silk icons s přidanou hodnotou

Velice rád ve svých aplikacích (zejména v administrační části) používám ikonky ze sady Silk od FamFamFam. Pokud je neznáte tak vězte, že se jedná o rovnou tisícovku volně použitelných ikonek ve formátu PNG o rozměru 16x16 px.

Jejich použití je ale v praxi poněkud komplikované - věčně si nepamatuju jejich názvy, natožpak vědět které ikonky jsem kde použil, takže musím obvykle kopírovat celou sadu. To ale není moc praktické, protože 1000 malinkých souborů znamená velkou režii při ukládání, přenosu a obecně jakékoliv manipulaci.

Naštěstí .NET umí uložit do resource a tudíž "zakompilovat" do assembly (v našem případě tedy do DLL souboru) libovolná data, včetně binárních. ASP.NET pak pomocí speciálního vestavěného handleru s adresou WebResource.axd umí tato data vytáhnout a zobrazit. Pomocí této techniky jsem všech tisíc malých obrázků vecpal do jednoho souboru, se kterým je přece jenom snazší pořízení.

Dále pak jsem vytvořil nový server control, nazvaný IconHyperLink. Tento control se chová podobně jako normální HyperLink, ale umožnuje specifikovat pojmenovanou ikonku, kterou zobrazí. Kromě toho umí vedle ikonky zobrazit i vysvětlující text, takže chci-li zobrazit současně text a obrázek, stačí mi na to jeden prvek, nemusím na stránku dávat dva.

Web Resources

Postup pro vytvoření komponenty s vloženými web resources je vcelku jednoduchý, ale musí se dodržet naprosto přesně, včetně například velikosti písmen v názvech souborů a adresářů a podobně.

V první řadě si vytvořte projekt typu Class library. Přímo do webové aplikace samotné resources vložit nemůžete. Poté si otevřete vlastnosti projektu (Project Properties) a na záložce Application nastavte v poli Default namespace výchozí namespace, který chcete používat. V mém případě to je Altairis.Web.Icons.

Dále pak si v projektu vytvořte složku a vhodně ji pojmenujte - já osobně ji obvykle nazývám Resources, přílišná kreativita je v tomto případě na škodu. Do této složky pak přidejte soubory, které chcete do resources uložit - v našem případě je to tedy oněch 1000 PNG obrázků. Nejjednodušší způsob je přetáhnout je myší z nějakého správce souborů, ale jde to i přes vkládací dialog.

Po vložení si označte soubory, kterých se to týká (může jich naštěstí být víc současně) a zobrazte si jejich vlastnosti, například stiskem F4. Hodnotu vlastnosti Build Action nastavte na Embedded Resource.

Poté si kdekoliv v projektu vytvořte nový soubor s kódem (Code file). Je jedno, jak se bude jmenovat, já ho obvykle pojmenovávám _ResourceRegistration.cs. Do tohoto souboru zadejte obsah dle následujícího vzoru (všimněte si, že tam není žádná definice class, namespace a podobně):

using System.Web.UI;

 

[assembly: WebResource("Altairis.Web.Icons.Resources.Accept.png", "image/png")]

[assembly: WebResource("Altairis.Web.Icons.Resources.Add.png", "image/png")]

[assembly: WebResource("Altairis.Web.Icons.Resources.Anchor.png", "image/png")]

Atribut WebResource opakujte tolikrát, kolik souborů chcete vložit. První argument jeho konstruktoru je logický název resource - ten budete potřebovat později, až budete chtít předmětná data získat. Musí přesně, včetně velkých a malých písmen, odpovídat default namespace (Altairis.Web.Icons) plus názvu složky (Resources) a souboru (Accept.png). Druhý argument je MIME typ daného souboru. Handler při odpovědi na požadavek na resource pošle i jeho MIME typ.

Kdekoliv pak budete potřebovat získat URL, na němž si budete moci obsah tohoto resource stáhnout, stačí zavolat metodu Page.ClientScript.GetWebResourceUrl. Jako první argument bere typ určující assembly (můžete použít jakýkoliv typ, který je obsažený v assembly, kde jsou resources registrovány), jako druhý argument pak logický název resource, který musí být stejný, jako při registraci v atributu WebResource.

Podrobnější informace o fungování web resources najdete v těchto výtečných (i když poněkud zastaralých) článcích:

Použití CompositeControl

Existují v zásadě dva přístupy, které můžete využít k tomu, abyste vytvořili ovládací prvek jako je IconHyperLink. První, na první pohled jednodušší, způsob spočívá v tom, že si vytvoříte novou třídu, kterou podědíte od System.Web.UI.Control nebo System.Web.UI.WebControls.WebControl. V ní pak přepíšete metodu Render a vypíšete do TextWriteru HTML, jaké jest vám libo.

Existuje ale ještě jeden přístup, který můžete využít tehdy, když váš control v podstatě nedělá nic jiného, než že sdružuje několik existujících server controls ve vzájemné interakci. To je i případ prvku IconHyperLink - v podstatě se jedná jenom o kontajner, který obsahuje dva controly typu HyperLink s nějak specificky nastavenými vlastnostmi - jeden pro ikonku, druhý pro textový popisek. Stačí třídu controlu podědit od System.Web.UI.WebControls.CompositeControl a místo metody Render přepsat metodu CreateChildControls. V ní pak do kolekce Controls nacpete podřízené ovládací prvky.

Tento přístup má dvě hlavní výhody: Získáte tím bez námahy podporu veškeré infrastruktury v ASP.NET, včetně design time podpory, control adaptérů, skinů a podobně. Kromě toho v případě složitějších prvků patrně oceníte, že výsledný HTML kód nemusíte generovat sami.

Kompletní zdrojový kód třídy IconHyperLink vypadá takto:

using System.ComponentModel;

using System.Web.UI;

using System.Web.UI.WebControls;

 

namespace Altairis.Web.Icons {

    public class IconHyperLink : CompositeControl {

        private const string ResourceNameFormat = "Altairis.Web.Icons.Resources.{0}.png";

 

        public IconHyperLink() {

            this.Icon = IconType.NoIcon;

            this.Mode = DisplayMode.IconLeft;

        }

 

        [Category("Navigation"), DefaultValue(""), Bindable(true), UrlProperty]

        public string NavigateUrl {

            get { return (string)(this.ViewState["NavigateUrl"] ?? string.Empty); }

            set { this.ViewState["NavigateUrl"] = value; }

        }

 

        [Category("Navigation"), DefaultValue(""), TypeConverter(typeof(TargetConverter))]

        public string Target {

            get { return (string)(this.ViewState["Target"] ?? string.Empty); }

            set { this.ViewState["Target"] = value; }

        }

 

        [Category("Appearance"), DefaultValue(""), Bindable(true), Localizable(true)]

        public string Text {

            get { return (string)(this.ViewState["Text"] ?? string.Empty); }

            set { this.ViewState["Text"] = value; }

        }

 

        [Category("Appearance"), Themeable(true), Bindable(true), DefaultValue(IconType.NoIcon)]

        public IconType Icon {

            get { return (IconType)(this.ViewState["Icon"] ?? IconType.NoIcon); }

            set { this.ViewState["Icon"] = value; }

        }

 

        [Category("Appearance"), Themeable(true), DefaultValue(DisplayMode.IconLeft)]

        public DisplayMode Mode {

            get { return (DisplayMode)(this.ViewState["Mode"] ?? DisplayMode.IconLeft); }

            set { this.ViewState["Mode"] = value; }

        }

 

        protected override void CreateChildControls() {

            switch (this.Mode) {

                case DisplayMode.IconLeft:

                    this.AddIconLink();

                    this.AddTextLink();

                    break;

                case DisplayMode.IconRight:

                    this.AddTextLink();

                    this.AddIconLink();

                    break;

                case DisplayMode.IconOnly:

                    this.AddIconLink();

                    break;

                default: // DisplayMode.TextOnly:

                    this.AddTextLink();

                    break;

            }

        }

 

        private void AddIconLink() {

            // If no icon specified, do nothing

            if (this.Icon == IconType.NoIcon) return;

 

            // Create new hyperlink and setup basic properties

            HyperLink link = new HyperLink();

            link.NavigateUrl = this.NavigateUrl;

            link.Text = this.Text;

            link.Target = this.Target;

            link.Width = Unit.Pixel(16);

            link.Height = Unit.Pixel(16);

            link.ToolTip = this.ToolTip;

 

            // Set image source to web resource to this item

            link.ImageUrl = this.Page.ClientScript.GetWebResourceUrl(

                this.GetType(),

                string.Format(ResourceNameFormat, this.Icon));

 

            // Add to child controls

            this.Controls.Add(link);

        }

 

        private void AddTextLink() {

            // If no text specified, do nothing

            if (string.IsNullOrEmpty(this.Text)) return;

 

            // Create new hyperlink and setup basic properties

            HyperLink link = new HyperLink();

            link.NavigateUrl = this.NavigateUrl;

            link.Text = this.Text;

            link.Target = this.Target;

 

            // Add to child controls

            this.Controls.Add(link);

        }

 

    }

}

Myslím, že je vcelku jednoduchý a srozumitelný. Enum IconType obsahuje seznam všech podporovaných ikon a využívá ho vlastnost Icon. Obdobně vlastnost Mode a jí odpovídající enum DisplayMode určuje, jak se vedle sebe zobrazí ikonka a text:

namespace Altairis.Web.Icons {

 

    public enum DisplayMode {

        IconLeft,  // Icon on left side of text

        IconRight, // Icon on right side of text

        IconOnly,  // Show icon only

        TextOnly   // Show text only

    };

 

    public enum IconType {

        NoIcon,

        Accept,

        Add,

        // ...

        ZoomOut

    }

 

}

Download

Kompletní zdrojové kódy k článku si můžete stáhnout zde: 20080109-AltairisWebIcons.zip (800 kB). Projekt je psaný ve Visual Studiu 2008, ale pro .NET Framework 2.0 a měl by jít otevřít i ve VS 2005. Bohužel nyní služebně dlím v ošklivé severomoravské vesničce jménem Zlín a mám s sebou jenom notebook, na němž už VS 2005 nemám, takže to nemohu vyzkoušet.

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