neděle 11. prosince 2011

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.

středa 7. prosince 2011

“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.

pondělí 5. prosince 2011

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.

pondělí 21. února 2011

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ý stackoverflowmi 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.

Zbytek článku je na mém starším blogu zde: http://blog.vyvojar.cz/mstr/archive/2011/02/21/css-sprity-a-seznamy.aspx

neděle 5. prosince 2010

DRY – databáze podruhé

V první části jsem ukázal, jak zjednodušit a centralizovat práci s databázi. V tomto díle zajdu ještě dál a kód se stane ještě více obecnějším.


Visual Studio – seskupení souborů

Jeden kamarád si prohlížel jeden z mých projektů a zaujalo ho, že v Solution Explorer vidí některé soubory pěkně sloučené do skupinky. Jak na to?

středa 1. prosince 2010

Jak na DRY – databáze poprvé

Pokud někoho zkratka DRY mate, tak je to zkrácenina anglického Don’t Repeat Yourself – neboli “neopakujte se”. Samozřejmě je to ideál a nejde ho vždy a všude dosáhnout, v následujícím článku chci jen ukázat, jak toho docílit v případě práce s databázemi. Dále je ukázáno použít anonymní metody a využití delegátů, konkrétně generického Action<T>.