Tehdään WPF-sovellus - 22 - Kantaluokka ViewModeleille

- 5 mins

Kantaluokka vastaa päivitysten ilmoittamisesta näkymälle

Näkymämme ei osaa päivittyä automaattisesti, ellemme kerro sille eksplisiittisesti, että “nyt tämä ja tämä tieto pitäisi päivittää”.

WPF:n näkymän bindaukset osaavat kuunnella INotifyPropertyChanged-rajapinnan ilmoituksia propertyjen muutoksista. Emme halua kuitenkaan implementoida kyseistä rajapintaa jokaisella ViewModelillamme erikseen, joten luodaan tätä varten oma kantaluokka, josta muut ViewModelimme voivat periä rajapinnan toteutuksen automaattisesti.

Lisätään Solution-tasolle uusi kansio “MVVM” ja luodaan tänne uusi tiedosto/luokka ViewModel

namespace Kettunen.BMICalculator.WPFClient.MVVM
{
    public abstract class ViewModel
    {

    }
}
ViewModel.cs

Tehdään luokasta public ja abstrakti kantaluokka, koska haluamme aina käyttää ViewModelista jotain konkreettista toteutusta.

Lisätään INotifyPropertyChanged-rajapinnan toteutus ViewModel-luokallemme:

-public abstract class ViewModel
+public abstract class ViewModel : INotifyPropertyChanged
ViewModel.cs

Hoveroimalla aaltoviivalla alleviivatun “INotifyPropertyChanged”-sanan alla VS tarjoaa kätevän hehkulamppupainikkeen, jolla voi nopeasti ottaa käyttöön tarvitsemansa nimiavaruuden.

Hehkulamppu auttaa nimiavaruuksien kanssa.

Rajapinnan alla on edelleen aaltoviivaa, joten voimme klikata kursorin sen kohdalle, painaa Ctrl + . ja valita “Implement interface”, jolloin VS lisää rajapinnan toteutuksen vaatimat funktiot ja/tai propertyt luokalle automaattisesti.

using System.ComponentModel;

namespace Kettunen.BMICalculator.WPFClient.MVVM
{
    public abstract class ViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
    }
}
ViewModel.cs

Näkymä osaa kuunnella juuri lisäämäämme PropertyChanged-tapahtumaa, mutta meiltä puuttuu vielä tapa kutsua sitä.

Tavoitteenamme on tehdä tänne kantaluokalle mahdollisimman yksinkertainen tapa saada ammuttua PropertyChanged-tapahtuma, kun perivällä luokalla propertyn arvo muuttuu.

Lisätään funktio OnPropertyChanged, jonka tehtävänä on ampua PropertyChanged-ilmoitus ilmoille:

protected virtual void OnPropertyChanged(string propertyName = null)
    => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
ViewModel.cs

Propertyn arvon päivittäminen SetPropertyllä yksinkertaiseksi

Perusidea propertyjen päivittämisessä on, että kun propertylle annetaan uusi arvo - eli kutsutaan sen setteriä - niin tällöin tästä ammuttaisiin PropertyChanged-tapahtuma automaattisesti.

Jos emme lisäisi enää mitään kantaluokalle, niin perivien luokkien tulisi tehdä jotain tämän tapaista, jos haluttaisiin ilmoittaa muutoksista vain silloin, kun propertyn arvo on oikeasti muuttunut:

private string _nimi;
public string Nimi
{
    get
    {
        return _nimi;
    }
    set
    {
        // Varmistetaan, että muutos tehdään vain arvon oikeasti muuttuessa
        if(EqualityComparer<string>.Default.Equals(_nimi, value))
        {
            _nimi = value;

            // Laukaistaan muutoksen tapahtuma
            OnPropertyChanged(nameof(Nimi));
        }
    }
}
Mielivaltaisen ViewModelin property

Tässä olisi aika paljon kirjoitettavaa, jos tämä pitäisi tehdä jokaisen propertyn kohdalla!

Ratkaistaan tämä ongelma lisäämällä kätevä SetProperty-funktio kantaluokallemme, joka hoitaa kaiken tuon seremonian meille automaattisesti taustalla:

protected virtual bool SetProperty<T>(ref T field, T value, [CallerMemberName] string propertyName = "")
{
    if (EqualityComparer<T>.Default.Equals(field, value))
    {
        return false;
    }

    field = value;
    OnPropertyChanged(propertyName);
    return true;
}
ViewModel.cs

Tässä listaus funktiossa käytetyistä konsepteista:

== -operaattori ei toimi genericsien kanssa.

ViewModel-kantaluokka on nyt valmis tarpeisiimme!

Tähän lopuksi vielä käydään periyttämässä MainViewModel ViewModelistamme, jolloin MainViewModel.cs tulee näyttämään tältä:

using System.Windows.Input;
using Kettunen.BMICalculator.WPFClient.MVVM;

namespace Kettunen.BMICalculator.WPFClient
{
    public class MainViewModel : ViewModel
    {
        public ICommand Navigate { get; }

        public string NavigateText => "CALCULATE";
    }
}
MainViewModel.cs

Seuraavassa osassa luodaan toteutus ICommand-rajapinnalle, jotta pääsisimme navigoimaan piakkoin tulosnäkymään painiketta painamalla.

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