Tehdään WPF-sovellus - 24 - Lisää ViewModeleita

- 5 mins

Infra ei häiritse ViewModelien luontia

Olemme tehneet nyt suurimman osan taustatyöstä, jotta pääsisimme toteuttamaan näkymien taustalla olevia ViewModel-luokkia kätevästi huolehtimatta niitä ympäröivästä infrastruktuurista.

Emme voi kovin paljoa tehdä pääikkunan ViewModelin puolella ennen kuin meillä on navigoitavien näkymien ViewModelit käsissämme.

Aloitetaan siis lisäämällä InputViewModel ja ResultViewModel, jotka perivät ViewModel-kantaluokkamme.

InputViewModel

InputViewModel on pääikkunalla näytettävä painon ja pituuden syötekenttien näkymä. Tarvitsemme siis näitä syötekenttiä vastaavat propertyt tälle ViewModelille.

Määritellään ensin luokka uuteen tiedostoon:

using Kettunen.BMICalculator.WPFClient.MVVM;

namespace Kettunen.BMICalculator.WPFClient
{
    public class InputViewModel : ViewModel
    {

    }
}

InputViewModel.cs

Lisätään tämän jälkeen backing fieldit ja itse property tähän tapaan sekä painolle että pituudelle:

private double _weight;
public double Weight
{
    get => _weight;
    set => SetProperty(ref _weight, value);
}
InputViewModel.cs

Tee sama Height-arvolle ja lopputuloksen pitäisi olla tämä:

using Kettunen.BMICalculator.WPFClient.MVVM;

namespace Kettunen.BMICalculator.WPFClient
{
    public class InputViewModel : ViewModel
    {
        private double _weight;
        public double Weight
        {
            get => _weight;
            set => SetProperty(ref _weight, value);
        }

        private double _height;
        public double Height
        {
            get => _height;
            set => SetProperty(ref _height, value);
        }
    }
}
InputViewModel.cs

ResultViewModel

ResultViewModel ei paljoa vaadi, joten näytän sen vain tässä kokonaisuutena ilman höpinöitä:

using Kettunen.BMICalculator.WPFClient.MVVM;

namespace Kettunen.BMICalculator.WPFClient
{
    public class ResultViewModel : ViewModel
    {
        private double _result;
        public double Result 
        {
            get => _result;
            set => SetProperty(ref _result, value);
        }
    }
}
InputViewModel.cs

Viewien DataContextit kuntoon

Koska meillä on nyt käsissämme kahdelle näkymälle ViewModelit, niin käydään myös päivittämässä näiden näkymien d:DataContext-arvot kohdilleen:

<UserControl ...
             d:DataContext="{d:DesignInstance Type={x:Type local:InputViewModel}"
             ...
InputView.xaml
<UserControl ...
             d:DataContext="{d:DesignInstance Type={x:Type local:ResultViewModel},
                                              IsDesignTimeCreatable=True}"
             ...
ResultView.xaml

Tuloksen bindaus ViewModelin propertyyn

Olemme aiemmin asettaneet tulosnäkymämme arvon oletuksena “123”. Jos laskurimme antaisi jokaiselle syötetylle arvolle saman tuloksen, olisi kyseessä vain aika huono laskuri.

Bindataan siis tuloksen tekstikenttä ViewModelin Result-arvoon:

<TextBlock Grid.Row="1"
           Text="{Binding Result}" />
ResultView.xaml

Design-näkymämme arvo on vaan ehkä hieman ankeasti nyt “0”. Tälle täytyy tehdä jotain!

Design-näkymä - ResultView.xaml.

Design-arvon voi määrittää myös ViewModelin puolella

Aiemmin annoimme d:Text-attribuutille arvon, joka näytettäisiin suunnittelun aikaisena tietona näkymällä (kts. InputView.xaml).

Voimme kuitenkin toteuttaa tämän toisellakin tapaa: suunnittelunaikainen data on myös saatavilla ViewModelin kautta, koska Designer-näkymä alustaa ihan oikean instanssin ViewModelista latautuessaan, jos sen IsDesignTimeCreatable=True.

Meidän olisi syytä kuitenkin varmistua jotenkin siitä, ettei Designer-näkymä yritä esimerkiksi käynnistää tietokantayhteyksiä, tai suorittaa jotain kauan kestäviä operaatioita ihan vain sen takia, että se yrittää vain ladata ViewModeliamme esittääkseen sen.

ViewModelille uusi property

Käydään lisäämässä ViewModel-kantaluokallemme uusi property IsInDesignMode. Tämän propertyn tarkoituksena on kertoa, että suoritetaanko koodia Designerin toimesta, vai ihan oikeasti sovellusta suorittamalla.

using System.Windows;
    ...
    public abstract class ViewModel : INotifyPropertyChanged
    {
        public static bool IsInDesignMode => DesignerProperties.GetIsInDesignMode(new DependencyObject());
    ...
ViewModel.cs

Nyt voimme käydä lisäämässä ResultViewModelille rakentajan, jossa pääsemme hyödyntämään uutta propertyamme:

public ResultViewModel()
{
    if (IsInDesignMode)
    {
        Result = 123;
    }
}
ResultViewModel.cs

Kun olet kääntänyt ohjelman pitäisi Design-näkymässä näkyä antamasi arvo.

Seuraavaksi toteutetaan navigointi

Voimme seuraavassa osassa keskittyä toteuttamaan navigointia näiden näkymien välillä nyt kun työkalupakistamme löytyy sopiva määrä ViewModeleita ja Viewejä.

Anssi Kettunen

Anssi Kettunen

Ohjelmistokehittäjä suorittamassa tehtävää 🦊

rss facebook twitter github gitlab youtube mail spotify lastfm instagram linkedin google google-plus pinterest medium vimeo stackoverflow reddit quora quora