Výběr jazyka jako server control s vyhledáváním v ASP.NET

17. února 2004

V tomto článku vytvoříme serverový ovládací prvek, pomocí kterého poskytneme uživateli možnost výběru jazyka. A protože je seznam jednotlivých jazyků opravdu dlouhý, zároveň jej vybavíme schopností vyhledávání, kterou jsme popsali v jednom z předchozích článků v rubrice o JavaScriptu.

Použití prvku pro výběr jazyka je analogické výběru země bez databáze, o kterém jsme již psali – v ASP.NET se dokonce mnohem více než zvláštní databáze hodí využít vestavěné vlastnosti .net Frameworku pro podporu různých jazyků a národních zvyklostí. Funkce pro vyhledávání v seznamu je převzata z článku o vyhledávání v selectboxu JavaScriptem. Prohlédněte si ukázku (zdrojový kód).

Základ našeho nového prvku získáme zděděním vestavěného DropDownListu, dopíšeme přidání položek (jsou typu ListItem) a přepíšeme metodu render, obdobně jako třeba v již popsaném prvku Flash Player Control. Seznam jazyků získáme ze třídy CultureInfo (rovněž již popsané v článku o formátování data a času). Prvek by se dal v lecčems vylepšit, jde však spíše o ukázku a tak zde zveřejníme pouze jednu vlastnost, logickou proměnnou, která umožní povolit či zakázat vyhledávání pomocí klientského skriptu. Výsledkem bude knihovna, kterou zkompilovanou uložíme na server. Nový prvek ukážeme jako součást běžného formuláře.

Kód zdroje knihovny DropDownLanguageList.cs:

using System;
using System.Globalization;
namespace System.Web.UI.WebControls
{
  [ToolboxData(„<{0}:DropDownLanguageList runat=server></{0}:DropDownLanguageList>“)]
  public class DropDownLanguageList : System.Web.UI.WebControls.DropDownList
  {
    public DropDownLanguageList(): base()
    {
      foreach(CultureInfo ci in CultureInfo.GetCultures(CultureTypes.SpecificCultures))
        base.Items.Add(new ListItem(ci.DisplayName,ci.Name));
      base.SelectedValue = CultureInfo.CurrentUICulture.Name;
    }
    public Boolean AllowSearch
    {
      get
      {
        object o = this.ViewState[„AllowSearch“];
        if (o != null)
          return (Boolean) o;
        else
          return false;
      }
      set
      {
        this.ViewState[„AllowSearch“] = value;
        if (value)
        {
          base.Attributes.Add(„onblur“, „__searchDdlReset();“);
          base.Attributes.Add(„onclick“, „__searchDdlReset();“);
          base.Attributes.Add(„onkeypress“, „return __searchDdlList(event,this);“);
          base.AutoPostBack = false;
        }
      }
    }
    protected override void Render (HtmlTextWriter writer)
    {
      if (this.AllowSearch)
      {
        writer.WriteLine();
        writer.WriteBeginTag(„script“);
        writer.WriteAttribute(„type“,“text/javascript“);
        writer.WriteLine(HtmlTextWriter.TagRightChar);
        writer.WriteLine(„<!– <![CDATA[„);
        writer.WriteLine(“ function __getDdlKey(e)“);
        writer.WriteLine(“ {„);
        writer.WriteLine(“ var code;“);
        writer.WriteLine(“ if (!e)“);
        writer.WriteLine(“ var e = window.event;“);
        writer.WriteLine(“ if (e.keyCode)“);
        writer.WriteLine(“ code = e.keyCode;“);
        writer.WriteLine(“ else“);
        writer.WriteLine(“ if (e.which)“);
        writer.WriteLine(“ code = e.which;“);
        writer.WriteLine(“ return code;“);
        writer.WriteLine(“ }“);
        writer.WriteLine(“ var lastValue = “;“);
        writer.WriteLine(“ function __searchDdlReset()“);
        writer.WriteLine(“ {„);
        writer.WriteLine(“ lastValue = “;“);
        writer.WriteLine(“ window.status = “;“);
        writer.WriteLine(“ }“);
        writer.WriteLine(“ function __searchDdlList(eventObj,slcObj)“);
        writer.WriteLine(“ {„);
        writer.WriteLine(“ if (navigator.userAgent.indexOf(‚MSIE‘) > -1 || navigator.userAgent.indexOf(‚Opera‘) > -1)“);
        writer.WriteLine(“ {„);
        writer.WriteLine(“ var lastKey = __getDdlKey(eventObj);“);
        writer.WriteLine(“ if (lastKey == 8 && lastValue.length > 1)“);
        writer.WriteLine(“ lastValue = lastValue.substring(0,lastValue.length-1);“);
        writer.WriteLine(“ else“);
        writer.WriteLine(“ lastValue += String.fromCharCode(lastKey);“);
        writer.WriteLine(“ window.status = ‚Najít: ‚ + lastValue;“);
        writer.WriteLine(“ for (i=0; i<slcObj.length; i++)“);
        writer.WriteLine(“ {„);
        writer.WriteLine(“ if ((slcObj.options[i].text.toLowerCase().indexOf(lastValue.toLowerCase()) == 0) && (lastValue.value != “))“);
        writer.WriteLine(“ {„);
        writer.WriteLine(“ slcObj.options[i].selected = true;“);
        writer.WriteLine(“ return false;“);
        writer.WriteLine(“ }“);
        writer.WriteLine(“ }“);
        writer.WriteLine(“ return false;“);
        writer.WriteLine(“ }“);
        writer.WriteLine(“ else“);
        writer.WriteLine(“ return true;“);
        writer.WriteLine(“ }“);
        writer.WriteLine(„//]]> –>“);
        writer.WriteEndTag(„script“);
        writer.WriteLine();
      }
      base.Render(writer);
    }
  }
}

Ve zdroji vidíme definici nové třídy DropDownLanguageList, která dědí z třídy DropDownList. V konstruktoru cyklem projdeme všechny podporované kultury, získáme je metodou GetCultures() třídy CultureInfo, v závislosti na CultureTypes různé národní odlišnosti. S ohledem na použití pro uložení verze jazyka do databáze se mi jako nejvhodnější jeví použít SpecificCultures, pro jiné aplikace se samozřejmě může hodit jiný filtr. Každá položka kultury je přidána do DropDownListu jako kolekce ListItemCollection metodou Add(). Do výsledného seznamu je tak přidán název země v jazyce uživatelského rozhraní DisplayName s hodnotou dvojpísmenného rozlišení kódu země Name (např. cs-CZ). Nakonec ještě určíme vybranou položku – SelectedValue nastavíme na hodnotu shodnou s aktuálním Culture uživatele, které získáme prostřednictvím CurrentUICulture. Jen ještě zbývá doplnit, že je potřeba importovat prostor názvů System.Globalization, kde se třídy pro práci s kulturami nacházejí.

Dále je definována veřejná vlastnost AllowSearch, pokud bude její hodnota true, vygenerujeme navíc klientský skript, který umožní vyhledávání v seznamu. Hodnotu ukládáme a čteme prostřednictvím ViewState, jak je popsáno v článku o tvorbě serverových ovládacích prvků. Ve zmíněném článku je také popsáno, jak přepsat metodu Render(), která zajistí vykreslení prvku pro klienta. V našem příkladu v závislosti na nastavení hodnoty AllowSearch nejprve případně vyrenderujeme klientský skript a nakonec je vždy provedeno volání původní metody Render(), čímž bude kompletně zachováno renderování našeho prvku shodně jako původní DropDowList, který náš prvek rozšiřuje. Všimněte si, že nastavení AllowSearch na true způsobí také potlačení možnosti automatického odeslání formuláře při změně vybrané položky – vlastnost AutoPostBack je zde napevno nastavena na false. Možnost vyhledávat v seznamu totiž s touto volbou koliduje.

Hotovou knihovnu zkompilujeme a uložíme do složky Bin v kořeni virtuální aplikace na serveru. Kompilaci můžeme provést i z příkazového řádku zadáním povelu:

csc.exe /t:library DropDownLanguageList.cs

A ještě ukázka použití ve formuláři:

<%@ Page Language=“C#“ EnableSessionState=“False“ Trace=“False“ Debug=“false“ %>
<%@ Import Namespace=“System.Globalization“ %>
<%@ Register TagPrefix=“mycode“ Namespace=“System.Web.UI.WebControls“ Assembly=“DropDownLanguageList“ %>
<script language=“C#“ runat=“server“>
private void Page_Load(object sender, System.EventArgs e)
{
  if (!IsPostBack)
  {
    //DropDownList1.SelectedValue = „de-DE“;
  }
  lblError.Text = new CultureInfo(DropDownList1.SelectedValue).DisplayName;
}
</script><?xml version=“1.0″ encoding=“utf-8″ ?>
<!DOCTYPE html PUBLIC „-//W3C//DTD XHTML 1.0 Transitional//EN“ „http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd“>
<html xmlns=“http://www.w3.org/1999/xhtml“ xml:lang=“cs-CZ“ lang=“cs-CZ“ dir=“ltr“>
  <head>
    <meta http-equiv=“Content-type“ content=“text/html; charset=utf-8″ />
    <title>DropDownLanguageList server control s vyhledáváním</title>
  </head>
  <body>
    <asp:Label Id=“lblError“ RunAt=“server“ />
    <form id=“myform“ runat=“server“>
      <mycode:DropDownLanguageList Id=“DropDownList1″ AllowSearch=“true“ RunAt=“server“ />
      <asp:button Text=“Odeslat“ RunAt=“server“ />
    </form>
  </body>
</html>

Direktivy stránky obsahují mimo jiné import prostoru názvu System.Globalization a zaregistrování knihovny (assembly) nového prvku.

V jednoduchém formuláři je náš nový prvek (má nastaveno povolené vyhledávání) a odesílací tlačítko. V obsluze události Page_Load je jen pro ukázku možného použití vidět zakomentované předvolení hodnoty de-DE v případě, že jde o první volání stránky. Následně je potom vždy do prvku Label nastaveno jméno zvolené kultury – je zde dobře vidět, jak z Name (například „cs-CZ„) zpětně získáme DisplayName (například „Čeština (Česká republika)„), což se opět může hodit při zobrazení nebo editaci obsahu databázové tabulky s údaji v různých jazycích.

O tom, že jde skutečně spíše o ukázku, svědčí také fakt, že při generování klientského skriptu neřešíme vícenásobné použití prvku ve stránce. U tohoto prvku je to sice málo pravděpodobné a tak nám může i v této jednoduché podobě dobře posloužit, pokud bychom jej však v nějakém formuláře chtěli mít viditelný současně ve více instancích, dojde ke kolizi klientského skriptu. Takto jednoduše vytvořený zděděný prvek nepotřebuje ani zvláštní designer – stačí jej jednoduše přidat do panelu custom controls ve Visual Studiu nebo WebMatrixu a oba zmíněné editory s ním ihned budou umět vizuálně pracovat.

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

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

Štítky: Články

Mohlo by vás také zajímat

Nejnovější

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *