Generování grafů v .NET podle šablony v XSLT

4. března 2004

Generování grafů na serveru v ASP.NET se na Intervalu probíralo již dříve, použitý kód je však poněkud nepřehledný a špatně se udržuje. Přesunutím vzhledu do XSLT šablony dosáhneme nejen jeho lepší struktury, ale také úplné skinovatelnosti grafů. Ukázky jsou napsány v jazyce C#.

Abych vás upoutal, hned na začátku vám podstrčím finální produkt. Jsou to dva grafy ankety vygenerované stejným kódem, avšak s různými skiny v XSLT. V tomto článku jsou pouze ukázkové fragmenty kódu, na kterých si popíšeme, jak generování obrázků pracuje, zbývající zdrojový kód je obdobný a není třeba jej rozebírat, všechny potřebné soubory si můžete stáhnout. Doporučuji vám však nastudovat článek Grafy v ASP.NET – koláče, z něhož jsem vycházel.

Základní principy

Jakým způsobem by to celé mělo fungovat? Docela jednoduše. Mějme nějaká XML data (řekněme například výsledky anket), které potřebujeme zobrazit v našem grafu. Tyto data necháme projít XSL transformací podle naší šablony, takže výsledný XML dokument bude obsahovat grafické entity pro nakreslení grafu. Množinu těchto entit a jejich syntaxi si přitom určíme sami. Podle těchto entit nakonec dojde k samotnému vykreslení grafu. Grafické entity si definujeme později, nyní se domluvíme, že celý obrázek bude reprezentován XML elementem bitmap.

Celý proces bude uschován stejně jako dříve v události Page_Load. Relativní cestu k datům a stylu předáme ASPX souboru v parametrech URL. O samotné vykreslení se bude starat objekt třídy BitmapRenderer, kterou si vytvoříme později. Jejímu konstruktoru předáme XML uzel obrázku, který má nakreslit. Pro vykreslení zavoláme metodu Render, které předáme parametrem stream, do něhož modul vygeneruje svůj výstup.

Rovněž se zde postaráme o odchycení všech výjimek, a to opět velice jednoduchým způsobem. Při každé chybě dojde k přesměrování na obrázek s velice nekonkrétní informací o chybě.

private void Page_Load(object sender, System.EventArgs e)
{
  try
  {
    // načtení zdrojových dat
    XmlDocument data = new XmlDocument();
    data.Load(MapPathSecure(Request.QueryString[„zdroj“]));
    // načtení transformačního stylu
    XslTransform Transformator = new XslTransform();
    Transformator.Load(
        MapPathSecure(Request.QueryString[„styl“]));
    // transformace dat na kresbu
    XmlDocument kresba = new XmlDocument();
    kresba.Load(Transformator.Transform(data,null));
    // vykreslení grafu
    BitmapRenderer Renderer = new BitmapRenderer(
        kresba.SelectSingleNode(„bitmap“));
    Renderer.Render(Response.OutputStream);
  }
  catch
  {
    Response.Redirect(„graf_error.gif“);
  }
}

Vytváření kresby XSL transformací

Připomeňme si, že XML je dnes již standardní formát pro uchování strukturovaných dat a XSLT umožňuje tato data transformovat do jiného formátu, například jiného XML dokumentu. S technologií XSLT se můžete naučit pracovat například pomocí výborné učebnice Jiřího Koska XSLT v příkladech.

Jak by tedy vypadala XSLT šablona v našem případě? Klíčový element bude bitmap, ten bude obsahovat jednotlivé kreslené entity. Jeho atributy width a height určují velikost generovaného obrázku.

<?xml version=“1.0″ encoding=“UTF-8″?>
<xsl:stylesheet version=“1.0″
  xmlns:xsl=“http://www.w3.org/1999/XSL/Transform“>
  <xsl:output method=“xml“ encoding=“UTF-8″
    omit-xml-declaration=“yes“ standalone=“no“
    indent=“no“ />
    <xsl:template match=“anketa“>
    <bitmap width=“200″ height=“200″>
    <!– definice kresby bude zde –>
    </bitmap>
    </xsl:template>
</xsl:stylesheet>

Jednotlivé grafické elementy nebudeme procházet všechny, ale ukážeme příklad. Tím může být vyplněný obdélník. Jeho atributy jsou souřadnice umístění a rozměry. Obdélník má nějakou výplň, která bude určena patřičným poduzlem. Zmiňovaná výplň je nakreslena nějakou barvou, která bude určena dalším poduzlem. Ukázku máme zde:

<obdelnik left=“0″ top=“0″ width=“200″ height=“200″>
  <vypln typ=“solid“>
    <barva R=“255″ G=“255″ B=“204″ />
  </vypln>
</obdelnik>

Podobných struktur si můžeme vymyslet tolik, kolik potřebujeme, jmenný prostor System.Drawing je velice obsáhlý. Samozřejmě pak musíme implementovat pro každou značku patřičnou metodu, která ji zpracuje, o tom však bude řeč později.

Vykreslovací modul

Třída BitmapRenderer má za úkol generovat grafický výstup podle předpisu v XML dokumentu. Její metody si budou postupně předávat jednotlivé XML uzly, a ty pak budou zpracovávány.

Jak jsem uvedl výše, konstruktor bude přebírat parametrem XML uzel celého obrázku. Odkaz na tento uzel si uloží do soukromé vlastnosti.

protected XmlNode schema;
public BitmapRenderer(XmlNode _schema)
{
  schema = _schema;
}

A teď se podíváme na klíčovou metodu Render. Jejím parametrem je výstupní stream a jejím úkolem je poslat na něj bitmapu, kterou vygeneruje podle kresby, s níž je provázán. Metoda prochází všechny XML elementy bitmapy a dle jména vyvolává metody pro nakreslení dané grafické entity.

protected Bitmap image;
protected Graphics kreslic;
public void Render(System.IO.Stream stream)
{
  image = new Bitmap(
      int.Parse(schema.Attributes[„width“].Value),
      int.Parse(schema.Attributes[„height“].Value),
      System.Drawing.Imaging.PixelFormat.Format32bppArgb);
  kreslic = Graphics.FromImage(image);
  kreslic.SmoothingMode =
      System.Drawing.Drawing2D.SmoothingMode.None;
  foreach(XmlNode prvek in schema.ChildNodes)
  {
    switch(prvek.Name)
    {
      case „obdelnik“:
        KresliObdelnik(prvek);
        break;
      // … další grafické entity …
  }
  image.Save(stream, ImageFormat.Gif);
  image.Dispose();
}

Nakreslení obdélníku

Ukažme si například, jak vypadá metoda pro nakreslení tak jednoduché grafické entity, jakou je obdélník. Pro vytvoření výplně a její barvy budeme volat další metody.

protected void KresliObdelnik(XmlNode data)
{
  Brush vypln = DejVypln(data.SelectSingleNode(„vypln“));
  kreslic.FillRectangle(
      vypln,
      int.Parse(data.Attributes[„left“].Value),
      int.Parse(data.Attributes[„top“].Value),
      int.Parse(data.Attributes[„width“].Value),
      int.Parse(data.Attributes[„height“].Value));
  vypln.Dispose();
}
protected Brush DejVypln(XmlNode data)
{
  switch(data.Attributes[„typ“].Value)
  {
    case „solid“:
      return new SolidBrush(
          DejBarvu(data.SelectSingleNode(„barva“)));
    default:
      return new SolidBrush(Color.Red);
  }
}
protected Color DejBarvu(XmlNode data)
{
  return Color.FromArgb(
      int.Parse(data.Attributes[„R“].Value),
      int.Parse(data.Attributes[„G“].Value),
      int.Parse(data.Attributes[„B“].Value));
}

Podobně jako se kreslí obdélník můžeme kreslit i další tvary, záleží jen na našich požadavcích. Pro ně můžeme opět využít metody zajišťující potřebné objekty třídy Brush a Color. V ukázkovém příkladu je definováno grafických prvků více, aby byla vidět síla návrhu. Přesto tyto entity pokrývají stále pouze zlomek grafických vymožeností, které nám nabízí FCL.

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

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

Další článek repro.cz
Š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 *