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

Používáme MVVM - jednoduché View

Napsáno pro WPF od Jarda Jirava  [09.02.2010]

Z minulého článku ze seriálu o prezentačním návrhovém vzoru Model-View-ViewModel již víme, jakým způsobem vytvořit jednoduchý ViewModel a jak nám tento ViewModel adaptuje Model pro View. Co zatím nevíme, ale předpokládám, že většina již tuší, jak vlastně takové View má vypadat, nebo spíše jaké konstrukce použijeme, abychom View napojili na ViewModel.

Deklarace View

Nebudu se nyní zabývat tím, aby naše View bylo uživatelsky přívětivé a krásné, na to určitě ještě někdy přijde řada. Co pro nás v tuto chvíli je podstatné, že potřebujeme uživateli zpřístupnit textové pole, do kterého může zadat název měřící stanice, tlačítko, pomocí něhož se přihlásí k odběru měření ze zadané stanice a dva seznamy, ve kterých se budou zobrazovat jednotlivá provedená měření a ve druhém se poté bude zobrazovat seznam přihlášených měřících stanic.

Pro alespoň základní rozvržení prvků použiji panelů, jak jsem již uvedl v dřívějším článku, nejčastější je použití Gridu, který taktéž využiji.

Pojďme tedy rovnou k deklaraci View pomocí XAML jazyka:

<Window x:Class="MVVMDemo.App.MeasureWindow"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  Title="Station data" 
  Height="600" 
  Width="600" 
  WindowStartupLocation="CenterScreen"
    xmlns:local="clr-namespace:MVVMDemo.App"
    xmlns:model="clr-namespace:MVVMDemo.Infrastructure.Models;assembly=MVVMDemo.Infrastructure"
    >
  <Grid>        
        <Grid.RowDefinitions>
            <RowDefinition Height="60"/>
            <RowDefinition Height="1*"/>
            <RowDefinition Height="5*"/>
        </Grid.RowDefinitions>
       <Border>
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="30"/>
                    <RowDefinition Height="30"/>
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*"/>
                    <ColumnDefinition Width="100"/>
                </Grid.ColumnDefinitions>
                <!-- Input controls -->
                <TextBlock Text="Station name:" Grid.Column="0" Grid.Row="0" />
            <TextBox Grid.Column="0" Grid.Row="1"  />
                <Button Content="Add station" Grid.RowSpan="2" Grid.Column="1" />
                <!-- Input controls -->
            </Grid>
       </Border>
        <Border Grid.Row="1">
            <!-- View control -->
            <ListBox />
            <!-- View control -->
        </Border>
        <Border Grid.Row="2">
            <ListView>
                <ListView.View>
                    <GridView>
                        <GridViewColumn Header="Station" DisplayMemberBinding="{Binding Station}" Width="100"/>
                        <GridViewColumn Header="Date" DisplayMemberBinding="{Binding Date}"  Width="160"/>
                        <GridViewColumn Header="Value" DisplayMemberBinding="{Binding Value}" Width="100" />
                        <GridViewColumn Header="PreviousValue" DisplayMemberBinding="{Binding PreviousValue}" Width="100" />
                        <GridViewColumn Header="Kind" DisplayMemberBinding="{Binding Kind}" Width="60" />
                    </GridView>
                </ListView.View>
            </ListView>
        </Border>
    </Grid>
</Window>

Takto vypadá deklarace pro View, která nemá téměř žádné ponětí o nějakých datech. Tím téměř je možné si odmyslet zpřístupnění vlastností v jednotlivých sloupcích GridView, které jsou nabindovány na třídu poskytující údaje o měření. Takto nějak bychom nejspíše postupovali i v případě, že nepoužijeme návrhový vzor Model-View-ViewModel. Pokud bychom nyní doplnili všude atributy x:Name a v code behind provedli přiřazení na vlastnosti ViewModelu, mohli bychom mít stejně funkční aplikaci.

Proč se však zbavit možnosti využítí obousměrného DataBindingu.

Úprava deklarace View pro použití DataBindingu

Jak jsme si řekli minule, náš jednoduchý ViewModel publikuje pro View několik vlastností, které můžeme napojit na zobrazovací prvky. Pojďme si tedy nyní provést toto napojení. Začneme u jednoduchých zobrazovacích prvků jako je TextBox, kde si chceme přečíst hodnotu z vlastnosti Text. Binding provedeme následovně:

Text="{Binding Station, UpdateSourceTrigger=PropertyChanged}"

Jak vidíte, bindujeme na vlastnost Station a zároveň jsem ještě doplnil další atribut UpdateSourceTrigger, který říká, že se hodnota vlasnosti zobrazovacího prvku má synchronizovat s datovým zdrojem okamžitě při změně. Výchozí chování je totiž až při opuštění prvku. Této vlastnosti využijeme příště, až si budeme povídat něco o Commandech a jejich zpracování.

Další vlastnosti, které máme zpřístupnění ve ViewModelu jsou seznamy. První seznam nabízí přehled o přihlášených měřících stanicích, takže provedeme jeho přiřazení do ListBoxu, který se hodí právě pro tyto jednoduché seznamy:

ItemsSource="{Binding Stations}"

Obdobně pak budeme postupovat i u ListView, které má stejně nazvanou vlastnost ItemsSource, kam přiřadíme náš seznam s provedeným měřením:

ItemsSource="{Binding Measures}"

V deklaraci pak už máme uvedeno, do kterého sloupečku GridView se bude bindovat která hodnota třídy měření.

Úprava code behind

Tím jsme dokončili práci na deklaraci View. Nebojte se, vím, že nemáme zatím obslouženo kliknutí na tlačítko, a naše aplikace tak nemůže být funkční. Jak jsem však již naznačil, zpracováním Commandu se budu zabývat v příštím článku, neboť tato problematika je o něco složitější a zaslouží si samostatného vysvětlení.

Co však určitě musíme udělat, abychom vůbec propojili naše View s ViewModelem je instancovat ViewModel a přiřadit jej nějakým způsobem do View. Přepneme se tedy do code behindu (.cs souboru) a zde zapíšeme následující kód, který vytvoří novou instanci ViewModelu.

Nejdříve si deklarujeme proměnnou na úrovni třídy, která bude uchovávat instanci ViewModelu:

private readonly MeasureViewModel _vm;

Dále v konstruktoru vytvoříme instanci jak pro servisní vrstvu tak i ViewModel a tuto instanci přiřadíme do vlastnosti DataContext okna:

public MeasureWindow() {
IMeasureService _service = new RandomMeasureService();
    InitializeComponent();
    _vm = new MeasureViewModel(_service);
    this.DataContext = _vm;
}

Těmito řádky mám zajištěno vytvoření a napojení ViewModelu o ostatní záležitosti se pak postará mechanismus WPF, takže dojde k napojení vlastností na jednotlivé zobrazovací prvky a přihlášení se k notifikacím o změnách v napojených objektech.

Závěrem

V dnešním článku jsme si ukázali, jak vytvořit jednoduché View a jak jej napojit na vytvořený ViewModel. Pro fungování celé aplikace tak ještě zbývá napojení na příkazy, které iniciuje uživatel, to bude náplní příštího článku.

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