XAML.cz Magazín moderních technologií založených na XAML

DataBinding a DataTemplate

Napsáno pro WPF od Jarda Jirava  [22.01.2010]

Minule jsme si připravili data, která budeme chtít zobrazovat v naší aplikaci. V dnešním článku pak představím způsob, jak tato data navázat na zobrazovací prvky a zároveň je zobrazit uživateli tak, aby z toho měl, jak se říká, “uživatelský prožitek”.

DataBinding

DataBinding poskytuje flexibilní mechanismus pro synchronizaci dat a uživatelského rozhraní. Tak by se dalo v jedné větě shrnout vše. Pojďme si to však postupně rozebrat, jaké jsou všechny možnosti svázání dat s prvky.

Ve svém základu je data binding o synchronizaci mezi datovým zdrojem a cílem dat. Co tedy potřebujeme a hledáme je způsob, jak takový proces co nejlépe zautomatizovat, jak jej udělat jednoduchý a snadno pochopitelný. Bonusem by pak mělo být to, že takovýto způsob dokáže pracovat s většinou zdrojů dat.

Ve WPF může tento způsob specifikovat binding na cílovém elementu pomocí Markup extension nazvaného a zapisovaného v XAML jako {Binding}.

Tímto způsobem tak můžete provést vazbu na jakýkoliv objekt nebo vlastnost objektu. Pokud pak chcete, aby byl váš prvek notifikován o změnách, které se dějí ve zdroji dat, potřebujete nějaký mechanismus, který vám to umožní.

Mezi ty nejznámější a používané patří události a Dependency property. Nejběžnějším způsobem pak však je implementace rozhraní INotifyPropertyChanged ve vaší třídě. Jestliže pracujete se seznamy a potřebujete zajistit notifikaci o změně v tomto seznamu, nikoliv tedy nějaké konkrétní hodnoty, nejspíše nejčastěji využijete možnosti podědit nebo využít třídu ObservableCollection<T>.

U jednoduchých vazeb tak vytváříte vazbu mezi Dependency property s jednou vlastností nebo fieldem ve zdroji dat. Takovým příkladem tak může být například vazba na vlastnost TextBox.Text z nějaké vlastní třídy s vlastností Customer.Age.

Vazbu na seznam položek pak můžeme vytvořit tak, že propojíme právě tento seznam s některým prvkem, který slouží k zobrazení více prvků. Takovým prvkem je třeba ListView, ListBox nebo ComboBox.

V případě, že vážete data na nějaký .NET objekt, pak můžete využít možnosti použití DataTemplate, které “okoření” prezentaci dat uživateli. To se však dostáváme do druhé části dnešního článku.

DataTemplate

Data template umožňují úplnou kontrolu nad způsobem prezentace dat. Je celkem běžné, že pro zobrazování dat se nepoužívá výchozí způsob, který je příliš unifikovaný a strohý – tím pádem uživatelsky nepřívětivý a může vést k horšímu pochopení nebo reprezentaci dat.

Již z předchozích článků víme, že WPF umožňuje skládání uživatelského rozhraní z jednotlivých komponent. Data templates jsou pak prostředkem, jak tohoto skládání dosáhnout a to na jednom centralizovaném místě. Že vám to něco připomíná? Ano, je to něco obdobného, co jsme si popisovali u stylů – opakovaně použitelnou skupinu nastavení vlastností. Data templates pak ukládají opakovaně použitelné části uživatelského rozhraní, které pracují vždy s jednou instancí třídy (řádkem dat).

Data templates však nejsou jediný druh šablon, se kterými se můžeme ve WPF setkat. Další typy šablon představují ještě Hierarchical data templates a Control templates.

Rozšíření aplikace z příkladu

Jistě si pamatujete na příklad, který jsem zde uvedl. V něm jsme si nechali volný prostor pro zobrazování seznamu dat. Tento prostor můžeme v tuto chvíli vyplnit daty, na která již máme přístup z tohoto článku.

Na místo tohoto elementu

<TextBlock
               Margin="10"
               Foreground='White'>
      Todo: Wines Displayed Here....
</TextBlock>

tedy doplňme pro začátek tento řádek v souboru Window1.xaml

<ListBox x:Name="WineListBox" Grid.Row="1"
             DisplayMemberPath='ShortWineName' />

Zároveň potřebujeme nahrát nějaká data do tohoto seznamu. Necháme raději na uživateli, ať si zvolí, která data požaduje a tak si vygenerujeme handler pro stisk tlačítka. V elementu, kde máme deklarován Button, tedy přiřadíme do atributu s názvem Click název metody, která bude obsluhou této události.

Click="SearchButton_Click"

a nesmíme zapomenout pojmenovat TextBox, do kterého může uživatel zadat text pro vyhledání. Pojmenování jednotlivých prvků se provádí přiřazením atributu x:Name, zvolíme tedy třeba toto pojmenování

x:Name="_searchText"

Nyní již máme vše deklarováno a tak se můžeme přepnout do souboru Window1.xaml.cs a zapsat potřebný kód v jazyku c#. Obsloužíme tedy volání metody SearchButton_Click

private void SearchButton_Click(object sender, RoutedEventArgs e)
{
    var model = new CohoDataModel();
    var result = from wines in model.Wines
                 select wines;
    var search = _searchText.Text.Trim();
    if (!string.IsNullOrEmpty(search)) {
        result.Where(w => w.ShortWineName.Contains(search));
    }
    result = result.OrderBy(w => w.ShortWineName);
    WineListBox.ItemsSource = result.ToList();
}

Pokud aplikaci zkompilujeme a spustíme, ukáže se nám známé okno z předchozího příkladu. Po kliknutí na tlačítko by se nám pak měl zobrazit jednoduchý seznam s názvy vín. Jak se samy můžete přesvědčit, tento seznam je velice strohý a uživatel z něj nevyčte příliš informací.

To je právě prostor k tomu, abychom využili Data templates. Představte si nyní, že každý řádek, který v současné chvíli obsahuje pouze název, by obsahoval detailnější informace. Můžeme přidat třeba informace o  ročníku daného vína nebo zobrazit malý obrázek – což by mělo zpříjemnit uživatelský zážitek z používání aplikace. Výsledek pak může vypadat třeba následovně.

Data Template - Window

A jak jsme se k takovému výsledku dopracovali? To vidíte na následující ukázce kódu, kde použijeme deklaraci ItemTemplate, která změní způsob zobrazování jednotlivých položek seznamu a umožní nám zkomponovat řádek dle našich představ.

<ListBox x:Name="WineListBox"
         Style="{StaticResource WineList}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <StackPanel Style="{StaticResource VintagePanel}">

                    <TextBlock Text="{Binding Vintage}"
                       Style="{StaticResource VintageBlock}" />
                    <TextBlock Text="Vintage"
                       Style="{StaticResource VintageSubBlock}" />

                </StackPanel>
                <Border BorderBrush='DarkGray'
                BorderThickness='4'
                Margin='7,9'>
                    <Border BorderBrush='White'
                  BorderThickness='2'
                  >
                        <Border BorderBrush='Black'
                    BorderThickness='1'
                   >
                            <Image Source="{Binding ThumbnailUrl}"
                     Style="{StaticResource ThumbNailPreview}" />
                        </Border>
                    </Border>
                </Border>
                <!-- Wine Name -->
                <TextBlock Text="{Binding ShortWineName}"
                     Margin="5"
                     Style="{StaticResource TitleBlock}"
                     HorizontalAlignment="Stretch" />

            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

Pokusím se jen v krátkosti uvést, jak je tedy náš jeden řádek sestaven. Nejdříve máme deklarován StackPanel, který nám poskládá jednotlivé prvky vedle sebe. Uvnitř tohoto StackPanelu je další, který však uspořádá dva v něm obsažené prvky typu TextBlock pod sebe. Uvnitř prvního z těchto dvou prvků si všimněte použití Bindingu, kdy je tento svázán s hodnotou ročníku daného vína. Vedle těchto popisků jsou do sebe zanořeny rámečky, které ohraničují vinětu daného vína – obrázek. Opět je zde využito Bindingu na vlastnost ThumbnailUrl, a jak vidíte, do vlastnosti Source je možné přímo přiřadit url obrázku. Posledním prvkem je pak opět TextBlock, sloužící pro zobrazení názvu vína, tak jak již bylo uvedeno v předchozím, jednoduchém, seznamu.

Každý prvek má zároveň přiřazen nějaký styl, který můžeme pro tuto chvíli ignorovat, neboť stylům jsme se již věnovali a zde slouží jen pro zpřehlednění celé situace. Snad jedinou zajímavostí zde může být jedna vlastnost, přiřazená prostřednictvím stylu.

Jak jste si možná všimli z deklarace elementu ListBoxu se nám vytratilo přiřazení Attached property, která informuje nadřazený Grid o tom, do kterého řádku/sloupce má být tento ListBox umístěn. A právě tato Attached property je nastavena prostřednictvím stylu.

<Style x:Key="WineList" TargetType="ListBox">
      <Setter Property="Grid.Row"
              Value="1" />

Z toho je docela jasně vidět, že rozdělení kompetencí mezi vývojáře a designera je řešeno velice dobře. A vývojář nemusí mít žádné povědomí o tom, kde vlastně bude daný element umístěn, zná pouze jeho stromové neorientované zařazení v XAML dokumentu.

Závěrem

V dnešním, maličko delším, článku jsme si představili dvě poměrně mocné vlastnosti, které nabízí WPF. Prvním je použití flexibilního Bindingu. Druhou vlastností, se kterou se budeme ve WPF setkávat také poměrně často je využití Templates, konkrétně Data template. Celé povídání jsme pak zakončili rozšířením našeho příkladu vinotéky. V příštím článku budeme pokračovat jak s úpravou aplikace a podíváme se na User controls, tak pomocí těchto uživatelských prvků upravíme naši aplikaci.

Komentáře

ukládám komentář, vyčkejte prosím..
  1. Buďte první, kdo napíše komentář.

@xamlcz

  • RT @jvanrhyn: XAML, It's a bit like olives. Takes a while to get used to. But once you're used to it. It is actually pretty good. <3 XAML
  • RT @moser_christian: WPF Inspector 0.9.7 is released. It supports .NET 3.5 and 4.0 The project is now open source and available on CodeP ...
  • Jeff Handley oznámil vydání WCF RIA Services v.1.0 SP1 RTM http://bit.ly/gOgckn ke stažení na http://bit.ly/gVAXdK
  • jedna výzva pro Brno. Byl někdo z vás na přednášce o RIA v MS Akvárku? Dejte o sobě vědět. Děkuji
  • také jste uvažovali o tom, že zkusíte na projekt použít Caliburn Micro nebo naopak Prism 4? A co tak obojí, šlo by to nebo ne? Již brzy