Vítejte na blog.vyvojar.cz Přihlásit | Registrovat | Pomoc

Sbírka plakátků pro C# a ASP.NET MVC

Občas se snažím zachytit doporučené postupy a nebo popisy graficky ve formě malého plakátku. Mám je pak umístěné na pracovním stole a slouží mi jako rychlá reference v případě potřeby. Část z nich dávám nyní k volnému použití a pokud chcete,  můžete si je  stáhnout z http://martinstrimpfl.blogspot.cz/2014/12/sbirka-plakatku-pro-c-aspnet-mvc.html

Posted by mstr | 0 Comments

ASP.NET MVC - sdílený helper

Tento článek by se možná hodil do série "Poznámky z code review" - ale vlastně je to jen další variace na téme DRY :-). Narazil jsem na to, že v jednom web projektu (ASP.NET MVC) se poměrně často generoval tooltip (v daném projektu ikonka, která při najetí myší ukáže boxík s dalšími údaji). Na každém místě, kde se i zobrazoval, byl použit téměř stejný kód, lišil se jen vlastním tělem tooltipu - tedy tím, jaké informace zobrazoval.

Zbytek článku jako obvykle na http://martinstrimpfl.blogspot.cz/2014/12/aspnet-mvc-sdileny-helper.html

Posted by mstr | 0 Comments

Poznámky z code review 3

Další nedostatkem, se kterým se setkávám, je buď neznalost používaných technologiích a nebo nevyužívání jejich možností. To lze určitě pochopit, pokud kodér nepracuje s danou technologii určitý čas a nebo je jasné, že není expertem. Code review by ho  v takovém případě mělo upozornit na jiné možnosti a tedy tak vlastně přispívá k jeho odbornému růstu - a nebo může sloužit k vyvolání diskuze, který přístup je lepší a proč.

 

Pokračování na  http://martinstrimpfl.blogspot.cz/2014/12/poznamky-z-code-review-3.html

Posted by mstr | 0 Comments

Poznámky z code review 2

Při programování, ale vlastně asi při každé činnosti, je dobré se řídit pár zásadami. Jendou z nich je alespoň v případě programování něco, co se anglicky označuje jako DRY. Ne, neznamená to, že je sucho a je nutno zajít do hospody na jedno, ale jsou to první písmena Don't Repeat Yourself.

 

Zbytek článku je dostupný zde: http://martinstrimpfl.blogspot.cz/2014/12/poznamky-z-code-review-2.html

Posted by mstr | 0 Comments
Vedeno pod: , ,

Poznámky z code review 1

Občas dělám i code review hotového kódu a některé věci se neustále opakují či se vyskytují v kódu poměrně často a tak mne napadlo, že by nebylo špatné na ně upozornit a nabídnout k zamyšlení či diskuzi. První příspěvek bude věnován kódu, který provádí rozhodnutí dle výčtové hodnoty, tedy enumu.

 

Zbytek článku je opět na mém blogu http://martinstrimpfl.blogspot.cz/2014/12/poznamky-z-code-review-1.html

Posted by mstr | 0 Comments

Razor a email

V přechozím příspěvku jsem ukázal, jak využít Razor i mimo web aplikace. Nyní popíši, jak tohoto přístupu využít v případě, že chceme v aplikaci vytvořit email a ten poslat uživateli. Email bude obsahovat i obrázky a přílohu.

 

Zbytek příspěvku je na http://martinstrimpfl.blogspot.cz/2014/12/razor-email.html

Posted by mstr | 0 Comments
Vedeno pod:

Využití Razor mimo web

Při programování aplikací je často nutné vygenerovat nějaký text - například email - který je částečně pozměněn - například se mění oslovení (pan-paní), jméno (Martin-Petr) apod. Obvyklé postupy jsou buď tvrdě kódové - tedy skládání textu přímo v kódu za využití například StringBuilderu a nebo se použije textový soubor sloužící jako předloha (šablona), kde se vyhledávájí značky a místo nich se vkládá určitá hodnota.

 

Zbytek článku je dostupný na mém blogu http://martinstrimpfl.blogspot.cz/2014/12/vyuziti-razor-mimo-web.html

Posted by mstr | 0 Comments
Vedeno pod: , , , ,

A opět do oblak …

Minulý rok v září uspořádal Microsoft konferenci BuildWindows. Zde mimo jiné zazněla i myšlenka, že budoucí vývoj pro Windows bude probíhat v HTML a javascriptu. Následovaly úvahy o budoucnosti .NETu, C#, jak budou aplikace vypadat apod. V tomto příspěvku nehodlám tyto úvahy rozvíjet – spíše ukážu, jak napsat aplikaci v HTML. I ta bude ale závislá na C# – web stránky jsou realizováný pomocí MVC technologie a celý backend generování kartiček je napsán v C#. Klientská část naproti tomu spoléhá zcela na javascript a vzhled stránkua je řešen pomocí CSS – samotná stránka tak obsahuje téměř čisté HTML.

Před pár týdny jsem zde popsal řešení pro tisk kartiček – nejprve jsem se věnoval tomu, jak generovat vlastní PDF dokumentu, v druhém pokračování pak přesunu tohoto generování do cloudu. Oba díly naleznete zde:

V tomto příspěvku popíši, jak vytvářet vlastní kartičky. Pomocí formuláře v prohlížeči se zadají jednotlivé hodnoty a ty se posléze odešlou na server a zpět dostanete PDF dokument. Podobných stránek jsou ale na webu mraky, takže zkusím odlišit se trochu funkčností – vytváření kartiček bude fungovat tak, že jen zadáte slovíčko a vyhledají se jak jeho překlad do češtiny, tak i přepis jeho výslovnosti a příklad použití. Tyto vyhledané údaje si budete moci upravit. Navíc se kartičky budou lokálně ukládat ve vašem prohlížeči, práci tedy budete moci kdykoliv přerušit  a opět v ní začít bez výslovného ukládání.

Jak získat údaje o slovíčku

Na webu je plno on line slovníků – já si vybral dva, WordReference EnCz Slovník a Oxford Advanced Learner’s Dictionary.  Pro jejich využíti je ale potřeba vyřešit tyto problémy:

  • jak je volat a předat jím slovo, které hledáme
  • jak je volat v rámci naší stránky – tedy cross-domain problém
  • jak vyhledat potřebné údaje v tom, co nám stránky vratí

                                                                      image    image

Volání

To je ta nejjednodušší část, v případě stránek Oxford slovníku stačí volat adresu  http://oald8.oxfordlearnersdictionaries.com/dictionary/ + dané slovo, v případě WordReference pak http://www.wordreference.com/encz/  + dané slovo;

Řešení Cross-domain omezení

Z jedné stránky není možné volat a získat obsah z jiné domény. Částečně je to řešeno tím, že lze volat a použít skript z jiné domény a pokud tedy dotyčný web umí vracet JSON data, není potřeba nic vymýšlet a lze použít přímo metod knihovny jQuery a Ajax volání. Nicméně  v našem případě žádná z vybraných stránek toto nepodporuje. Naštěstí i zde existuje řešení a to služba YQL společnosti Yahoo a knihovna jquery.xdomainajax.js.

YQL je vlastně web služba, která získá obsah stránky, upraví jej tak, aby jej bylo možné zaslat jako skript (rozuměj ve formátu Json) a tak jej pošle volajícímu:

image

Knihovna xdomainajax pak jen volání YQL obaluje tak, aby s ním bylo možné pracovat tak, jako s ajax voláním, takže skript, který slovník volá, vypadá stejně, jako pro normální ajax volání:

image

Vyhledání údajů

To je opět snadná část, využívám možností jQuery selektorů a vyhledám příslušné informace – vyhledávání pro oba zvolené slovníky lze najít v souboru search.js

 

Ukládání v prohlížeči

Zde si opět usnadním práci pomocí existující knihovny jStorage. Každou vytvořenou kartičku ukládám lokálně v prohlížeči.  Pro zobrazení je pak využíván formulář, který se případně, v případě že má uživatel zájem o PDF kartičky, odesílá na server. Využívá se zde tzv. nesouvislých indexů, které jsem popsal v příspěvku  ASP.NET MVC3–Binding příklad.

 

Možnosti exportu

Uživateli jsou nabízeny dvě možnosti, jak data dostat z prohlížeče “ven”. Buď si může data vyexportovat jako csv soubor a nebo je odeslat na server a získat PDF dokument. Řešení popsané v příspěvku Vzhůru do oblak bylo ještě rozšířeno o možnost přijímat data z formuláře.

 

Postup práce s aplikací

Prosím mějte na paměti, že aplikace je jen příklad, není to žádné mistrovské dílo a má spíše jen demonstrovat, že lze vyvinout v HTML aplikaci s komfortem desktopové aplikace. Základní rozložení je velmi jednoduché, aplikace má tři hlavní části – v horní se zadává hledané slovíčko, prostřední slouží pro náhled a editaci kartičky a dole je seznam kartiček:

 

image

 

Do horního políčka napíšete slovo, pro které chcete vytvořit kartičku a stisknete Enter – ikonky slovníků se změní na dobu, kdy se slovo vyhledává a signalizují tak probíhající činnost:

image

Jakmile jsou údaje nalezeny, vyplní se kartičky nalezenými údaji:

image

Je možné na kterýkoliv údaj kliknout a změnit jej:

image

Jakmile jste s kartičkou spokojeni, klikněte na tlačítko Ulož a slovíčko se objeví v seznamu:

image

  • Kliknutím na kterékoliv slovíčko v seznamu se kartička vyplní jeho údaji a je možné kartičku aktualizovat či odstranit.
  • Práci můžete kdykoliv přerušit a prohlížeč zavřít – všechna data jsou uložena automaticky a při opětovném zavolání stránky se tato obnoví i se seznamem.
  • Výsledky své práce můžete uložit jak ve formátu CSV, tak si i nechat zaslat slovíčka v PDF dokumentu jako kartičky.

Další zajímavosti a popis řešení

Pro správu skriptů a css soubory je použita knihovna Cassette, kterou jsem ve stručnosti popsal v příspěvku ASP.NET– kazeta stále žije…...

Každá část obrazovky má svůj vlastní css styl:

  • inputBox.css – styly pro vrchní vyhledávací box
  • card.css – styly pro kartičky
  • cards.css – styly pro seznam kartiček
  • basicLayout.css – styly pro obecný vzhled stránky, tj. umístnění hlavičky, patičky apod.
  • site.css – styl dodaný MS

Všimněte si, že dva poslední styly nejsou přímo odkazovány ani ve view, ano v layoutu – je na ne odkaz v prvních třech souborech:

image

Funkčnost na straně klienta je také rozdělena do několika javascriptů:

  • buttonEvents.js – co se provede po kliku na tlačítka a také co se má provést po načtení stránky a vytvoření nové kartičky v seznamu (createEntry)
  • flashcard.js – je zde metoda pro export do csv (exportData),  testování, zda podobná kartička již neexistuje (isCardAlreadyStored) , vypsání seznamu uložených kartiček (listCard), vyplnění kartičky (fillCard) a také seznam operací, které se provedou, pokud vyhledáváte nové slovíčko (submitEdit)
  • search.js – dvě metody pro vyhledávání ve zvolených slovnících, pro každý slovník jedna

Jak pro skripty, tak i pro styly je použit bundle.txt soubor, který upřesňuje řazení skriptu a stylů.

Mimo tito skripty jsou použity tyto knihovny:

  • jQuery – netřeba představovat
  • jQuery.editable.js – umožňuje editaci na stránce po kliknutí, použití je velmi jednoduché,
  • jQuery.xdomainajax.js – umožňuje snadné použíti YQL, volá se jako bežné Ajax volání
  • jStorage.js – sjednocuje práci s lokálním uložištěm (HTML 5)

Aplikace funguje správně jen v posledních verzích prohlížečů Chrome, Opera, Firefox a asi i v IE (zde nefunguje export do csv). Cílem nebylo ani tak nabídnout naprostou funkčnost, spíše ukázat, jak udělat funkční aplikaci čistě v prohlížeči – i serverová podpora je omezena na generování PDF dokumentů, které nelze z asi pochopitelných důvodů generovat v prohlížeči (a nebo o tom zatím nevím).

Zdrojový kód

Aplikace běží na adrese http://flashcard.apphb.com/ a zdrojové kódy jsou k prohlédnutí na CodePlex. Komentáře v kódu moc nejsou, snažil jsem se vše popsat v tomto článku.

Posted by mstr | 10 Comments
Vedeno pod: , , , , ,

ASP.NET– kazeta stále žije…..

Nadpis je asi trochu matoucí  a  snad na vysvětlenou pro ty mladší  by bylo dobré zmínit, že kdysi dávno se hudba nekopírovala pomocí mp3, ale existovaly analogové kazety. Kopírování bylo zdlouhavé a s každou kopií klesala kvalita. Názvem tohoto média se inspiroval autor zajímavé knihovny(nepátral jsem proč a doufám, že to neznamená, že kvalita knihovny bude s každou verzí klesat).

Knihovnu  nalezenete na stránkách http://getcassette.net/.  Může vám pomoci při vývoji WebForms a nebo MVC webů (já ji použil pro zatím jeden malý projekt v MVC).

Řeší problém se správou skriptů a stylů –pro snazší údržbu a vývoj je vhodné mít styly uložené ve více css souborech, podobně i skripty. Jenže čím více toho musí prohlížeč pro zobrazení stránek stáhnout, tím je celková odezva stránek pomalejší a zátěž serveru a využití internetového spojení vyšší. A přesně tento problém Cassette řeší – při vývoji a ladění webu  zachovává malé soubory – můžete tedy snadno využívat klientských ladících nástrojů například ve Firefoxu, ostrá verze pak obsahuje soubory jak sloučené, tak minimalizované.

V případě MVC pak navíc nemusí stránky(views) obsahovat spousty tagů se skript soubory či linky na css. Stačí jen deklarovat, které soubory jsou pro dané View potřeba:

  1. @{
  2.     ViewBag.Title = "Index";
  3.  
  4.     Bundles.Reference("Scripts/Cards/export.js");
  5.     Bundles.Reference("Scripts/Cards/flashcard.js");
  6.     Bundles.Reference("Scripts/Cards/search.js");
  7.     Bundles.Reference("Content/inputBox.css");
  8.     Bundles.Reference("Content/card.css");
  9.     Bundles.Reference("Content/cards.css");
  10. }
  11. <div id="inputBox">

V MasterView souboru pak stačí na příslušných místech, kde mají byt tagy, zavolat:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4.     <title>@ViewBag.Title</title>
  5.     @Bundles.RenderScripts()
  6.     @Bundles.RenderStylesheets()
  7. </head>
  8. <body>
  9.     <div class="wrapper">

Všimněte si tak=, že nemusíte přímo odkazovat na jQuery knihovnu – ve skriptech, které si vytvoříte se stačí na tuto knihovnu odkázat a bude tak zahrnuta do vašich skriptů:

image

(ten odkaz by jste tam dávali asi tak jako tak, díky tomu vám začne fungovat intelliSense nápověda pro JQuery metody).

Totéž platí i o stylech, i  v nich můžete napsat odkaz na “nadřízený” styl:

image

Nyní můžeme v případě, že svůj web ladíme, získat všechny soubory:

image

Celková velikost jen skriptů je přes 600kB:

image

Naopak jakmile vývoj dokončíme a web nasadíme, začne nám Cassette soubory slučovat a minimalizovat:

image

Například JavaScripty jsou nyní jen v jednom souboru, který má velikost téměř sedmkrát menší:


image

Na webu autora knihovny  a nebo fórech pak lze nalézt další tipy, třeba jak vypnout minimalizaci:

 

  1. bundles.Add<ScriptBundle>("Scripts", b => b.Processor = new ScriptPipeline().Remove<MinifyAssets>());

 

Další informace o této knihovně jsou k dispozici na webu knihovny, popřípadě na tomto blogu. Instalace je jednoduchá, základní použití je popsáno na webu autora.

Posted by mstr | 10 Comments
Vedeno pod: , , , ,

ASP.NET MVC3–Binding příklad

V tomto příspěvku popíši, jak namapovat data pomocí “nesouvislých indexů” – ony se ty anglické výrazy špatně překládají, respektive nejsem jazykový odborník.

Problém

Představte si, že máte k dispozici seznam telefonních čísle a jmen a chcete je zobrazit uživateli, který pomocí zaškrtávátka určí, které z nich chce vložit do svého seznamu kontaktů. Běžně se tento problém řeší tak, že se uvažuje, že každý záznam má své ID (identifkátor).

Ale zkusme vyřešit situaci, kdy prostě žádné ID není k dipozici a nebo ho použít nechceme, tedy třída vypadá takto:

  1. public class Contact
  2. {
  3.     public string Name { get; set; }
  4.     public string Phone { get; set; }
  5. }

Zdroj dat nám nahradí tento kód:

  1. public static IEnumerable<Contact> Get()
  2. {
  3.     return new Contact[] {
  4.         new Contact() {Name= "Pepa", Phone= "123456789"},
  5.         new Contact() {Name= "Petr", Phone=  "987654321"},
  6.         new Contact() {Name= "Lopuch", Phone= "456789123"}
  7.     }.AsEnumerable();
  8. }

Kontroler není složitý:

  1. public class HomeController : Controller
  2. {
  3.     [HttpGet]
  4.     public ActionResult Index()
  5.     {
  6.         return View(Contact.Get());
  7.     }
  8. }

A View Index pak vypadá takto:

  1. @model IEnumerable<Contact>
  2. @{
  3.     ViewBag.Title = "Index";
  4. }
  5. <h2>Import - contacts</h2>
  6.  
  7. @using (Html.BeginForm())
  8. {
  9.     foreach (var contact in Model)
  10.     {
  11.     <input type="checkbox" name="index" value="@contact.GetHashCode()" /> @string.Format("{0}-{1}", contact.Name, contact.Phone) <br />
  12.     <input type="hidden" name="@string.Format("[{0}].Name", contact.GetHashCode())" value="@contact.Name" />
  13.     <input type="hidden" name="@string.Format("[{0}].Phone", contact.GetHashCode())" value="@contact.Phone" />
  14.     }
  15.     <input type="submit" value="send" />
  16. }

 

Metoda kontrolleru, který bude zpracovávat přijímaná data, pak vypadá takto:

  1. [HttpPost]
  2. public ActionResult Index(Contact[] contacts)
  3. {
  4.     return View("IndexPosted", contacts);
  5. }

 

a ve View IndexPosted vypisujeme přijaté kontakty pomocí jednoduchého kódu:

  1. <h2>Posted contects</h2>
  2. @foreach (var contact in Model)
  3. {
  4.      @string.Format("{0}-{1}", contact.Name, contact.Phone) <br />
  5. }

A takto se to chová:

imagea po odeslání stlačením tlačítka Send: image

Závěr

Veškeré kouzlo spočívá v checkboxu pojmenovaném index. Hodnota checkboxu je na server odeslána pouze v případě, že je zaškrtnut. MVC3 framework na serveru se pak snaží vytvořit kolekci objektů třídy  Contact (viz cílová akce formuláře Index(Contact[]  contacts)) – a hodnutu index použivá pro nalezení hodnot jednotlivých objektů.

Samozřejmě by se dal tento jednoduchý příklad řešit i jinak, ale všimněte si některých výhod tohoto řešení:

  • uživateli neposíláme žádné ID a Hashcode, který ho do jisté míry nahrazuje, je naprosto umělá hodnota.
  • Metodu pro zpracování dat do formuláře můžeme použít i pro zpracování normálního formuláře
  • díky tomu, ze využíváme model zpracování MVC frameworku, došlo by i k oveření všech podmínek, které bychom pro model případně definovali.

Celý zdrojový kód projektu BindingExample je dostupný na CodePlex – ale prakticky vše je uvedeno v tomto článku.

Poznámka na konec – index se nemusí jmenovat jen index, ale můžeme používat i prefixů při řešení složitějších případů:

  1. @Html.CheckBox("phones.index", new { value = contact.GetHashCode() })
  2.     @Html.Hidden(string.Format("phones[{0}].Name", contact.GetHashCode()), contact.Name)
Posted by mstr | 12 Comments

Vzhůru do oblak!…..a stejně skončíme v přístavu

Minulý příspěvek popisoval jednoduchou konzolovou aplikaci, která z údajů ze sešitu programu Excel udělá PDF dokument s kartičkami. Takový dokument pak stačilo jen vytisknout, rozstříhat či rozřezat a papírové kartičky byly hotové.

Tento příspěvek popíše, jak tuto aplikaci upravit tak, že:

  • umožní načítat z libovolného zdroje dat (například místo Excel z CSV souboru)
  • zdrojový kód bude přístupný v aktuální podobě každému a nemusí se přidávat jako příloha ke stažení
  • aplikace bude dostupná přes webové stránky – tedy pošleme soubor a obdržíme zpět PDF dokument

A proč nadpis “Vzhůru do oblak!”? – výsledný web bude v cloudu. To je dnes takové moderní slovo – v prostředí .NET nejčastěji spojované s Azure. Ale Azure nepoužijeme – Microsoft sice nabízí trial verze, jenže ani u nich není zaručeno, že na konci za to nebude platit (zde si neodpustím poznámku, že MS na Azure a WP7 předvádí jak velká firma není schopna správných rozhodnutí a nechápe, co je pro masivnější používání daných produktů klíčové – ale to je jen můj povzdech a soukromý názor). Takže místo Azure použijeme služeb AppHarbor.

Aktualizace 18.12 17:00 – jak jsem se dočetl zde, MS konečně umožnil bezplatné zkoušení. I tak je krok za AppHarbor.

Úprava stávající aplikace

Nejprve změníme typ z konzolové aplikace na knihovnu. To je jednoduchá změna, klikneme pravým tlačítkem na název projektu a ve vlastnostech změním typ:

image

Nyní se musí upravit samotná aplikace. Nejprve upravíme načítání dat – nyní je v kódu napevno zakodóváno načítání přes ADO.NET z Excel souboru:

Stávající kód
  1. void ReadAndPrintExcelSheet(...)
  2.         {
  3.             ...
  4.             Action<DbCommand> read = (command) =>
  5.             {
  6.                 command.CommandText = "SELECT * FROM [Sheet1$]";
  7.  
  8.                 using (DbDataReader reader = command.ExecuteReader())
  9.                 {
  10.                     ...
  11.                     while (reader.Read())
  12.                     {
  13.                         ....
  14.                     }
  15.                 }
  16.                 ...
  17.             };
  18.  
  19.             Db.Work("Excel", read);
  20.         }

Tohoto provázání se zbavíme poměrně snadno – zavedeme tento interface ICardData:

public interface ICardData
{
    string Word { get; }
    string Pronunciation { get; }
    string Example { get;  }
    string Meaning { get;  }
    string ExampleTranslation { get; }
}

Do definice třídy pro generování PDF pak přidáme delegáta  Func<IEnumerable<ICardData>>, který bude ukazovat na metodu, která bude schopna poskytnout údaje o kartičkách – přičemž nyní naší třídy pro generování PDF už nebude zajímat, jak to tato metoda dělá (tedy jestli čte databázi, CSV soubor, google dokument atd.):

Upravený kód
  1. Func<IEnumerable<ICardData>> getCardData;
  2. ...
  3. void ReadAndPrintExcelSheet(...)
  4. {
  5.     ...
  6.  
  7.     foreach (var cardData in getCardData())
  8.     {
  9.         ....
  10.     }
  11.  
  12.     ...
  13. }

Pokud porovnáte nový a předešlý kód navzájem, došlo ke zjednodušení. Dle mého názoru je kód i přehlednější. Zbývá ještě udělat metodu pro delegáta getCardData :-).

Poznámka pro ICardData"

Interface ICardData je implementován abstraktní třídou CardData – tato abstraktní třída pak slouží jako předek pro třídy navázané na konkrétní zdroje dat (bude ukázáno dále):

  1. public abstract class CardData: ICardData
  2. {
  3.     public string Word { get; protected set; }
  4.     public string Pronunciation { get; protected set; }
  5.     public string Example { get; protected  set; }
  6.     public string Meaning { get; protected set; }
  7.     public string ExampleTranslation { get; protected set; }
  8. }

V zdrojovém kódu je implementována i původní  podpora pro čtení Excel souborů – ale tento kód není využit (budou se zpracovávat jen CSV soubory). Slouží tedy spíše jako ukázka, jak implementovat podporu dalších zdrojů údajů o kartičkách. Jedná se o třídy DbCardDataProvider.cs, DbDataReaderCardData.cs a Database.cs.

Čteme CSV soubor

Pro čtení CSV souboru jsem si “vypůjčil” kód od Janathana Wooda.  Provedl jsem jen pár úprav – zajímá mne jen čtení ze streamu, místo čárky jsem jako oddělovač definoval středník. Výsledek je v souboru CsvStreamReader.cs.

Kód této třídy je jednoduchý – umí vzít vstupní stream a postupně jej číst “po řádcích”, přičemž každý řádek načte do objektu třídy CsvRow. To dělá metoda ReadRow(CsvRow), která vrací true, dokud nedosáhneme konce dat a aktualizuje vlastnosti objektu CsvRow tak, aby obsahoval data právě načteného řádku.

Nyní ještě zbývá udělat pomocnou třídu CsvStreamReaderCardDataProvider, která nám poskytne metodu pro delegáta Func<IEnumerable<ICardData>> pro třídu generující PDF dokument. 
  1. public class CsvStreamReaderCardDataProvider : IDisposable
  2. {
  3.     CsvStreamReader reader = null;
  4.  
  5.     public CsvStreamReaderCardDataProvider(MemoryStream stream)
  6.     {
  7.         this.reader = new CsvStreamReader(stream);
  8.     }
  9.  
  10.     public IEnumerable<ICardData> GetCardData()
  11.     {
  12.  
  13.         CsvRow row = new CsvRow();
  14.  
  15.         while (reader.ReadRow(row))
  16.         {
  17.             if (row.Count < 4)
  18.                 continue;
  19.  
  20.             CsvStreamReaderCardData card = new CsvStreamReaderCardData(row);
  21.  
  22.             yield return card;
  23.         }
  24.     }
  25. }

Funkčnost třídy je jednoduchá, vytvoří si instanci CsvStreamReaderu a poté v metodě GetCarData čte řádky, převede CsvRow na objekt třídy implementující ICardData – v tomto případě na objekt třídy CsvStreamReaderCardData, která je potomkem abstraktní třídy CardData:

  1. public class CsvStreamReaderCardData : CardData
  2. {
  3.     public CsvStreamReaderCardData(CsvRow row)
  4.     {
  5.         this.Word = row[0];
  6.         this.Pronunciation = row[1];
  7.         this.Example = row[2];
  8.         this.Meaning = row[3];
  9.         if (row.Count > 4)
  10.             this.ExampleTranslation = row[4];
  11.         else
  12.             this.ExampleTranslation = string.Empty;
  13.     }
  14. }

 

Proč vlastně stream a ne soubor (aneb iTextSharp v paměti)?

Jak jsem nastínil již v úvodu, má celá aplikace bežet v cloudu. Tam ukládání a čtení souborů může představovat problém – a protože nepředpokládám velké soubory, tak se veškeré zpracování (upload od uživatele a čtení) odehraje jen v paměti.

Výše uvedené má ještě jeden dopad – při generování PDF souboru se tento vytvářel na disku. Nyní i tento dokument musíme vytvořit v paměti a poté poslat uživateli zpět, tedy místo tohoto kódu:

  1. PdfWriter writer = PdfWriter.GetInstance(pdfDocument,
  2.                         new FileStream(
  3.                         Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
  4.                         "Flashcards.pdf"),
  5.                         FileMode.Create));

použijeme tento kód:

  1. var stream = new MemoryStream();
  2. PdfWriter writer = iTextSharp.text.pdf.PdfWriter.GetInstance(pdfDocument, stream);
  3. writer.CloseStream = false;

 

Souhrn změn původní aplikace

Mimo změny v typu aplikace (z konzolové na knihovnu) a způsobu načítání dat došlo i k dalším změnám:

  • Hlavní kód se přesunul ze souboru Program.cs do souboru Creator.cs, stejně se změnili i názvy tříd (program na Creator).
  • Třída Creator má jednu hlavní metodu CreatePdf, která vyžaduje dva vstupní paramatry – metodu pro získání údajů kartiček a třídu fontů, které se mají použít
  • správné umístnění kartičky na A4 je řešeno pomocí dvou statických funkcí BackNextPositionProvider a FrontNExtPositionProvider – zpřehlednil se tak tisk stránky, který se odehrává v metodě PrintFlashCardPage
  • kartičky se ze zdoje načítají do jediného pole, to se navíc při zpracování stránky neinicilizuje znovu, ale pouze se vyprázdní příkazem Array.Clear(pageCardData, 0, pageCardData.Length);

Zpracování je nyní toto:

  1. zavolá se metoda CreatePdf – inicilizuje se pdf dokument
  2. Načtou se kartičky pro stránku pomocí odkazu  na metodu getCardData
  3. Zavolá se metoda pro vložení stránky, které se mimo pole kartiček  předají  i odkazy na metodu vyrábějící kartičky pro rubovou stránku a odpovídají metoda pro výpočet polohy kartiček na stránce (tedy odkazy na metody CreateForeignFlashcard a FrontNextPositionProvider)
  4. Totéž se provede i pro lícovou stránku, jen se předávají metody CreateNativeFlashcard a BackNextPositionProvider
  5. Metoda CreatePdf vrátí vytvořený Pfd dokument jako stream

Mimo výše uvedené jsou v projektu třídy pro získání dat z CSV souborů a ze sešitů Excel.

 

Třída FlashcardFonts

Tato třída obsahuje fonty používané pro vypsání textů na kartičky. Protože má aplikace fungovat na webu, je nutné šetřit systémové prostředky a tedy stačí vytvořit tyto fonty jen jednou a poté je sdílet pro všechna volání objektů třídy Creator a jejich metody CreatePdf – pro všechna tato volání se použije stejná instance.

Protože jsou kartičky vykreslovány pomocí fontu Arial, je tento font přidán do AppData webovské aplikace a ve tříde FlashcardFonts pak slouží pro inicializaci základního fontu, ze kterého pak výchází ostatní.

 

 

Web aplikace

Web aplikace využívá knihovnu pro tvorbu Pdf dokumentů. Je to MVC3 aplikace vytvořená ze šablony visual studia, obsahuje jen jeden controller nazvaný Home, který zobrazí jednoduchý formulář umožňující upload souboru:

 

  1. @using (Html.BeginForm("UploadCsv", "Home", FormMethod.Post, new { enctype = "multipart/form-data" }))
  2. {
  3.     <input type="file" name="file" id="file" />
  4.     <input type="submit" value="submit" />
  5. }

Data odeslaná tímto formulářem na server jsou zpracována v metodě UploadCsv:

  1. MemoryStream downloadStream;
  2. string fileName;
  3.  
  4. using (MemoryStream uploadedStream = new MemoryStream())
  5. {
  6.     Request.Files[0].InputStream.CopyTo(uploadedStream);
  7.     fileName = Path.GetFileNameWithoutExtension(Request.Files[0].FileName);
  8.     uploadedStream.Position = 0;
  9.  
  10.     using (CsvStreamReaderCardDataProvider provider = new CsvStreamReaderCardDataProvider(uploadedStream))
  11.     {
  12.         
  13.         Creator creator = new Creator();
  14.         downloadStream = creator.CreatePdf(provider.GetCardData, fonts);
  15.     }
  16. }
  17.  
  18. downloadStream.Position = 0;
  19.  
  20. return new FileStreamResult(downloadStream, "application/pdf")
  21. {
  22.     FileDownloadName = fileName + "_flashcard.pdf"
  23. };

Obsah odeslaného souboru je vložen do streamu (Request.Files[0].InputStream.CopyTo(uploadedStream);) , následně je tento stream čten pomocí CsvReaderu (ten je skryt v objektu provider)  pomocí objektu provider . Následně je zavolán Creator, jako zdroj údajů o kartičkách mu slouží metoda GetCardData objektu provider – objekt creator pak  vytvoří stream obsahující Pdf dokument. Ten je následně odeslán zpět uživateli jako application/pdf s názvem odpovídajíc šabloně <jméno souboru>_flashcard.pdf.

 

Sdílíme zdrojový kód –CodePlex

V příloze k tomuto článku už nenaleznete zdrojový kód popisovaného řešení – ten je nyní umístněn na CodePlex serveru a to na adrese http://flashcardcreator.codeplex.com. Zdrojový kód si můžete volně procházet přímo v prohlížeči, popřípadě stáhnout a upravit jak chcete:

image

Poznámka – co je to CodePlex?

Dle wikipedie: CodePlex je internetový projekt společnosti Microsoft určený k hostování otevřeného softwaru. CodePlex byl založen v květnu 2009. K repozitářům lze přistupovat pomocí verzovacích systémů Team Foundation Server nebo Subversion. Uživatelé mají dále k dispozici nástroje pro sledování požadavků, chyb, podporu RSS, statistiky, diskuzní fóra,, vlastní Wiki atd. Ač se většina zdejších projektů týká .NET Frameworku, včetně ASP.NET a Microsoft SharePointu, jsou zde projekty zabývající se SQL, WPF a Windows Forms a další.

 

Jdeme do oblak

Náš projekt z CodePlex můžeme vystavit přímo na web díky službě AppHarbor. Základní verze služby je zdarma a zprovoznění je za normálních podmínek otázkou několika minut. V nápovědě AppHarbor naleznete i postup, jak spojit CodePlex s vaší aplikací – a při každém commitu/check-inu zdrojového kódu dojde i k aktualizace aplikace na AppHarbor.

Zatím jsem se ale setkal s těmito problémy:

  • ne každý commit zdrojového kódu na CodePlex se promítne do AppHarborAppHarbor dělá build jednou za cca 14-15 minut – i tak je mezi aktualizací zdrojových kódů na CodePlex a aktualizací AppHarbor občas několikaminutová prodleva:

    CodePlex:
    image
    AppHarbor:
    image
    image
  • Vložil jsem souboru arial.ttf do složky App_Data, ale zapomněl jsem nastavit (přenastavit) ve vlastnostech Build Action na Content.  Což vedlo k tomu, že při lokálním spuštění vše fungovalo, ale na AppHarbor soubor fyzicky nebyl – trvalo mi dost dlouho, než jsem přišel na to, kde je chyba – uznávám, je to školácká chyba. Kdybych použil svůj osvědčený postup spočívající v samostatném webu na lokálním IIS a deploymentu z VS, tak bych na to přišel dříve.

 

 

 

 

 

 

 

 

Nepopisuji podrobně, jak s oběma bezplatnými službami pracovat – obě požadují vytvoření účtu, což by nikomu nemělo činit problém a založení projektu na obou z nich je velmi jednoduché a přímočaré – v případě problémů existují diskuzní fóra i nápovědy u obou služeb.

Služba běží na adrese http://flashcard.apphb.com a základní rozhraní je velmi jednoduché:

image

Stačí vybrat soubor (ukázka souboru ve formátu UTF-8 CSV je přiložena jako slovicka.txt) a odeslat na server. Zpět vám přijde PDF dokument.

Závěr

Prosím mějte na paměti, že kód je pouze demonstrační.  Řetez událostí vypadá při použití CodePlex a AppHarbor takto: vývojář udělá změny a provede check-in změn do CodePlex. CodePlex upozorní AppHarbor na změnu, AppHarbor získá z CodePlex zdrojové kódy, zbuilduje je a nasadí.

Posted by mstr | 6 Comments

Attachment(s): Slovicka.txt

Víkendová rychlovka–tisk kartiček

Mé školou povinné děti se učí angličtinu. Což znamená učit se slovíčka. Mají slovníčky, jenže ty mají některé nevýhody – například se často naučíte slovíčka v nechtěném kontextu ostatních slov, tedy nakonec je umíte pěkně vyjmenovat v řadě, ale otázka na nahodilé slovíčko zůstane bez odpovědi. A nebo se špatně zvýrazňují obtížná slovíčka, aby jste si je mohli častěji opakovat.

 

Jistou naději jsem vkládal do elektronických pomůcek  - jenže chytrý mobil sloužil všemu možnému, jen ne na opakovaní slovíček a tak putoval pryč. Jedinou možností se tak jeví klasické papírové kartičky – jenže jejich příprava bere čas, obzvláště pokud by jste si je měli sami psát. Nutné je také zmínit, že na rozdíl od aplikaci jako AnyMemo jsou kartičky zcela tiché a slovíčko vám tedy nepřečtou.

 

Dal jsem tedy dohromady slovíčka na listu v sešitě Excel, stáhnul knihovnu iTextSharp a udělal aplikaci, která si údaje z Excelu načte a vytvoří PDF s kartičkami – ty stačí pak již jen vytisknout, rozstříhat a učit se, učit se, učit se (© Lenin).

 

Excel Sheet jako vstup

Formát informací je jednoduchý – slovíčko, přepis výslovnosti, příklad použití, český význam a překlad příkladu – vše na listě s názvem Sheet1 (pokud se list jmenuje jinak, musí se upravit příkaz pro načtení v programu) . Excel sheet pak vypadá  takto:

image

Poznámka – lze samozřejmě použít i jiný formát, ale musí se pak upravit aplikace – není to těžké. Použit je Excel 2010.

 

iTextSharp

Tuhle levnou, protože zadarmo, knihovnu na tvorbu PDF naleznete na této adrese. Trochu času zabralo přijít na nejlepší a nejjednodušší způsob vykreslení kartiček – nakonec každou kartičku vykresluji jako tabulku na určité pozici. Výhodou tabulek je možnost určit jak font použitý na obsah buňky, tak i pozadí buňky, její velikost a ohraničení. Obsah lze i otočit a lze tak dosáhnout možnosti přetáčet kartičky po delší straně s obráceným textem.

Vzhledem k tomu, že jsme v Evropě, tiskne se na papír A4, s okrajem 10mm (výsledné pédéefko by tedy mělo jít vytisknout na jakékoliv tiskárně):

image

Vlastní aplikace je velmi jednoduchá – otevře se Excel soubor a z něj se načítají data – to se odehrává v metodě ReadAndPrintExcelSheet. Ta načítá údaje ze sešitu a vytváří jednoduché objekty pro obě strany kartiček (objekty tříd ForeignFlashcardContent a NativeFlashcardContent). Ty pak vloží do pole na správné místo (aby při tisku na obě strany se obě strany kartiček správně “slicovaly”). Jakmile je načten dostatečný počet kartiček pro tisk stránky (v mém případě se tiskne 8 řádků po 4 sloupcích, tedy dohromady 32 kartiček), zavolá se vlastní “tisk” do PDF. Pro každý načtený údaj zhotovena kartička (metody  CreateForeignFlashcard a CreateNativeFlashcard) a ta je umístněna na stránku (metoda PrintFlashCardPage).  A to je vše.

Abych nemusel počítat body při generování PDF, je zde metoda mmToDots, která zadanou míru v milimetrech převede na počet bodů, se kterým metody iTextSharp knihovny pracují.

Abych mohl vykreslovat speciální znaky (hlavně u výslovnosti), používám jaké základ všech písem v dokumentu font Arial:

BaseFont baseFont = iTextSharp.text.pdf.BaseFont.CreateFont(@"C:/Windows/Fonts/ARIAL.TTF", BaseFont.IDENTITY_H, iTextSharp.text.pdf.BaseFont.EMBEDDED);

Celý kód je napsán prakticky jen ve statických metodách – nehledejte tam žádné architektonické kouzla a čáry, je to prostě jednoduchá a jednoúčelová aplikace, která ovšem udělá co má: kartičky.

Umístnění vstupního Excel souboru je v app.config – celý program lze samozřejmě upravit na čtení jména souboru z příkazové řádky apod. – každopádně nezapomeňte :

app.config
  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <configuration>
  3.     <connectionStrings>
  4.         <add name="Excel" connectionString="Dsn=Excel Files;dbq=D:\Slovicka.xlsx;driverid=790;maxbuffersize=2048;pagetimeout=5" providerName="System.Data.Odbc" />
  5.     </connectionStrings>
  6. </configuration>

a výstupní Flashcards.pdf pak naleznete ve svých dokumentech díky tomuto příkazu:

  1. PdfWriter writer = PdfWriter.GetInstance(pdfDocument,
  2.                                          new FileStream(
  3.                                             Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
  4.                                                         "Flashcards.pdf"),
  5.                                             FileMode.Create));


A toto je “hardwarový” výstup celého programu:

IMG_7305

 

Zdrojový kód je  přiložen včetně použitého xlsx souboru v balíčku zip.

Posted by mstr | 6 Comments
Vedeno pod: ,

Attachment(s): Flashcards.zip

“Vyber vše” aneb kombinace ASP.NET MVC a jQuery

V tomto článku popíši možné řešení zadání, podle kterého se má uživateli zobrazit seznam možností ve formě zaškrtávacího políčka, přičemž jedno ze zaškrtávacích políček má sloužit jako “Vyber vše” – pokud jej uživatel zaškrtne, zaškrtnou se všechny možnosti, pokud zaškrtnutí zruší, zruší se i všechny možnosti:

 image     image     image

Řešení podobného zadání jsou na netu mraky – takže k tomuto mraku přispěji dalším obláčkem.  Aby řešení bylo co nejvíce univerzální, stanovil jsem si tyto podmínky:

  • Všechny ovládané zaškrtávátka musí být uzavřeny do elementu <fieldset>
  • První zaškrtávátko slouží pro výběr všeho.

Obecné Html a javacript

Html kód splňující výše uvedené podmínky  pak vypadá takto:

<fieldset>
    <input type="checkbox" name="all" /><label for="all">Vyber ve</label><br /><br/>
    <input type="checkbox" name="praha" /><label for="praha">Praha</label><br />
    <input type="checkbox" name="brno" /><label for="brno">Brno</label><br />
    <input type="checkbox" name="ostrava" /><label for="ostrava">Ostrava</label>
</fieldset>

a odpovídající jQuery javascript pak takto:

selectAllCheckbox.js
  1. jQuery(function () {
  2.     jQuery('fieldset :checkbox').click(function () {
  3.         var $checkboxes = jQuery(this).closest('fieldset').find(':checkbox');
  4.         if ($checkboxes.index(this) == 0) {
  5.             $checkboxes.not(this).attr('checked', this.checked);
  6.         }
  7.         else {
  8.             $checkboxes.setSelectAllCheckbox();
  9.         }
  10.     });
  11.  
  12.     jQuery('fieldset').each(function () {
  13.         jQuery(this).find(':checkbox').setSelectAllCheckbox();
  14.     });
  15. })
  16.  
  17. jQuery.fn.setSelectAllCheckbox = function SetSelectAll() {
  18.     var first = this.first();
  19.     jQuery(first).attr('checked', this.not(first).filter(':checked').length == (this.length - 1));
  20. }

Metoda na řádcích 17.-20. nastaví předané ovládací zaškrtávátko buď do stavu zaškrtnuto a nebo nezaškrtnuto podle stavu ostatní zaškrtávátka (ovládací zaškrtávátko bude zaškrtnuto, pokud všechna ostatní jsou zaškrtnuta.

Kód na řádcích 2-10 zajišťuje, že při každém kliknutí na zaškrtávátko umístněné v elementu <fieldset> je zjištěno, zda se jedná o první zaškrtávátko v daném elementu. Pokud ano, nastaví se všechna ostatní zaškrtávátka podle stavu právě kliknutého (řádek 5.). Pokud ne, provede se funkce na řádcích 17. – 20.

Kód na řádcích 12. 15. slouží pro správná nastavení po načtení stránky do prohlížeče.

Upozorňuji, že kód není dokonalý, například neřeší důsledně situace, kdy je do sebe zanořeno více elementů fieldset.

Html helper method

Element <fieldset> nemusím generovat ručně. Můžeme použít rozšíření HtmlHelperu a použít tuto konstrukci v našem view. Tvorbu jednoduchých rozšíření nemá asi cenu popisovat, proto rovnou popíši něco o stupeň složitější, tedy konstrukci podobnou @using(Html.BeginForm….):

@using (Html.BeginSelectAllCheckbox())
{
    <input type="checkbox" name="all" /><label for="all">Vyber ve</label><br />
    @Html.CheckBox("Praha")<label>Praha</label><br />
    @Html.CheckBox("Brno")<label>Brno</label><br />
    @Html.CheckBox("Ostrava")<label>Ostrava</label>
}

Upozornění - je mi jasné, že v tomto případě se může hloubavému čtenáři zdát, že jde o přístup “s kanónem na vrabce”, ale chci na tomto jednoduchém příkladu demonstrovat tvorbu vlastních rozšíření – v některém z dalších příspěvků chci popsat složitější řešení a není tedy na škodu  začít jednoduchým příkladem.

Metoda BeginSelectAllCheckbox je pak statické metoda statické třídy, která vypadá takto:

public static class HelperExtension
    {
        public static IDisposable BeginSelectAllCheckbox(this HtmlHelper helper )
        {
            Action begin = () => {helper.ViewContext.Writer.Write("<fieldset>");};
            Action end = () =>    {helper.ViewContext.Writer.Write("</fieldset>");};

            return new DisposableHelper(begin, end);
        }
        
        private class DisposableHelper : IDisposable
        {
            private readonly Action end;

            public DisposableHelper(Action begin, Action end)
            {
                this.end = end;
                begin();
            }

            public void Dispose()
            {
                end();
            }
        }
    }

Tvorba vlastních rozšíření HtmlHelperu objektu v MVC tedy není složitá a princip je doufám z kódu patrný – díky using dojde kzavolání Dispose metody, která provede závěrečnou akci – zavolá metodu, kterou jsme při vytváření třídy definovali pomocí delegáta Action.

Přiložen je i ukázkový MVC projekt s veškerým kódem.

Posted by mstr | 6 Comments
Vedeno pod: , , , , ,

Attachment(s): DEMO_Select_All.zip

MVC3: odkaz v textu

MVC3 nabízí tuto metodu, pokud chceme na stránce vypsat odkaz:

@Html.ActionLink("klikni zde","Index")

Co ale dělat, pokud chceme vypsat odkaz jako součást delšího text, například:

Nejste registrován v našem systému, prosím klikněte zde a zaregistrujte se.

 

Řešením může byt rozdělení textu na části:

<p>Nejste registrován v našem systému, prosím klikněte @Html.ActionLink("zde","Index") a zaregistrujte se.</p>

To je fungující řešení – problém s ním ale nastane v okamžiku, kdy začneme stránku lokalizovat a texty umístíme do resource souborů – obvykle se pak celý text rozdělí na části:

image

a z nich se pak generuje stránka:

<p>@Resources.FirstPart @Html.ActionLink("zde","Index") @Resources.SecondPart</p>

 

To ale není příliš pěkné – vznikají zbytečné klíče. Spíše by bylo lepší mít toto:

image

Pokud ale zapíšeme tento kód:

<p>@string.Format(Resources.Registrace, Html.ActionLink("zde","Index"))</p>

objeví se nám na stránce:

image

Což není co bychom asi chtěli. Správný zápis totiž je:

<p>@Html.Raw(string.Format(Html.Encode(Resources.Registrace), Html.ActionLink("zde","Index")))</p>
A dostaneme:
image

Vysvětlení:

  • Html.Encode použijeme, abychom správně zakodovali HTML značky v původním textu, tedy aby resource mohl například obsahovat text “Musí být <> od 0”.
  • Html.Raw pak brání dalšímu kódování a text je vypsán tak jak je – nedojde tak k Html kodování vloženého odkazu.


                    
				            
Posted by mstr | 5 Comments
Vedeno pod: , , ,

CSS sprity a seznamy

Ač nerad, přeci jen jsem občas dotlačen k práci na uživatelském rozhraní :-). V rámci jednoho webového projektu vznikl požadavek použít CSS sprity  místo odrážek.

Pokud někdo neví, co jsou to CSS sprity, tak snad pomůže tento odkaz.

Přímé použití v seznamech je ale, jak jsem posléze zjistil, problematické. Prvek seznamu je obvykle větší než obrázek ve spritu a tak je často zobrazeno ze spritu více, než by bylo vhodné. Ani můj oblíbený stackoverflow mi nenabídl řešení – většina příspěvků, co jsem našel, radila buď předělat sprite tak, aby obsahoval obrázky jen vertikálně  a nebo  navrhovala použít element div jako kontejneru.

Pouze vertikální uspořádání spritu nebylo možné – sprite byl dodán a nebylo možné jej měnit. Použítí divu se mi moc nelíbílo, kód pak vypadal tak nějak divně:

<ul>
<li class="menu">
<div class="saveIcon"></div>Save
</li>
</ul>

Mým cílem je ale mnohem obyčejnější zápis:

<ul class="menu">
<li class="save">save</li>
<li class="edit">edit</li>
</ul>
a při použití jednoduchého spritu:
sprite_example

 

dosáhnout tohoto vzhledu:

image

o vše se pak postará tento css kód:

ul.menu
{
list-style: none outside none;
padding: 0;
margin: 0;
font-weight:bold;
}

ul.menu li
{
display: inline-block;
margin-left: 20px;
}

ul.menu li:before
{
content:url(content/empty_space.gif);
background-image: url(Content/sprite_example.jpg);
background-repeat: no-repeat;
margin-right:5px;
display: inline-block;
}

li.save:before
{
background-position: 0px -53px;
}

li.edit:before
{
background-position: -45px -53px;
}

Finta spočívá v použítí průhledného obrázku s rozměry odpovídajícími ikoně ze spritu. Ostatní již odpovídá běžnému postupu při používání Css spritu.

Následně je i velmi jednoduché změnit obrázek při najetí myší, stačí doplnit tento styl:

li.save:hover:before
{
background-position: 0px 0px;
}
a výsledkem je:
image Přiložena je i jednoduchá stránka s obrázky a css stylem.
Posted by mstr | 0 Comments
Vedeno pod:

Attachment(s): CssSpriteList.zip
Více článků Další stránka »