Tehdään WPF-sovellus - 26 - Korjataan navigoinnin puutteet

- 4 mins

ViewModel päättää painikkeella näytettävän tekstin

Edellisessä osassa listasimme neljä ongelmakohtaa. Yksi näistä oli se, että painikkeen teksti ei muutu, vaikka navigoimmekin toiseen näkymään. Laitetaan korjauslasit päähän ja käydään hommiin!

MainViewModel.CurrentViewModel-propertyn arvo vaihtuu siihen ViewModeliin, joka näytetään käyttäjälle. Käytetään tätä tietoa hyväksemme päivittämällä NavigateText-property oikeaan arvoon aina kun CurrentViewModel vaihtuu.

public class MainViewModel : ViewModel
{
    // ...
    
    public string NavigateText => CurrentViewModel is InputViewModel
            ? "CALCULATE"
            : "BACK";
    
    // ...
MainViewModel.cs

Tässä kohtaa herää kysymys, että olisiko meidän sittenkin pitänyt antaa eri näkymien vastuulle koko ikkunan toiminnot, jotta olisimme voineet välttää tällaiset is-avainsanan käyttämiset. Käytännössä InputViewModel olisi voinut tarjota oman NavigateText-propertynsä, jonka myötä voisimme asettaa NavigateText-propertyn näin:

public string NavigateText => CurrentViewModel.NavigateText;
MainViewModel.cs

Nyt jos koitat suorittaa ja navigoida seuraavaan näkymään, niin painikkeen teksti ei ole kuitenkaan muuttunut mihinkään. Emme ole kertoneet käyttöliittymälle, että sen tulisi päivittää NavigateText-arvoa, kun CurrentViewModel muuttuu.

Käytetään hyödyksemme SetProperty:n bool-paluuarvoa. Tämä kertoo siis sen, että onko arvo oikeasti muuttunut. Voimme tämän perusteella kertoa UI:lle, että tässä tilanteessa pitäisi päivittää myös NavigateText-propertyn arvo:

    // ...

    if (SetProperty(ref _currentViewModel, value))
    {
        OnPropertyChanged(nameof(NavigateText));
    }

    // ...
MainViewModel.cs

Siistiä, nyt toimii! Napataan listalta seuraava ongelmakohta: tulosnäkymältä ei pääse palaamaan takaisin syötekenttien näkymälle.

Komennolle voi Bindata parametrin

Nykyisellään Navigate-komentomme ei tiedä, että mille näkymälle sen tulisi navigoida. Olemme asettaneet komennon navigoimaan aina tulosnäkymälle. Voimme bindata komennolle parametrin syötettäväksi XAML-puolelta. Käytännössä tämä tarkoittaa sitä, että syötämme omavalinnaisen parametrimme suoraan execute-toiminnolle.

Tehdään tämä siten, että komento ottaa aina nykyisen CurrentViewModel:n arvon sisäänsä ja päättelee siitä, että minne sen tulisi ohjata käyttäjä seuraavaksi. Bindataan painikkeen CommandParameter-arvoksi siis CurrentViewModel päänäkymällä:

<Button ...
        CommandParameter="{Binding CurrentViewModel}">
    ...
</Button>
MainView.xaml

Suorita ohjelma ja pistä breakpoint riville, jossa komento kutsuu CurrentViewModel = _resultViewModel;. Tarkastelemalla lambdamme parametria (toistaiseksi vielä “_”) voimme huomata, että parametrin bindaus on onnistunut hienosti:

Komennon parametri vastaanotettu onnistuneesti!

Koska nyt vastaanotamme parametrin, niin muutetaan ensitöiksemme _ -> x:

x =>
{
    CurrentViewModel = _resultViewModel;
},
MainViewModel.cs

x on tyypiltään object. Tämä johtuu siitä, että aiemmin määrittelemämme DelegateCommand sanoo rakentajassaan näin:

public DelegateCommand(Action<object> execute, Predicate<object> canExecute)
DelegateCommand.cs

Emme ole siis hyödyntäneet tässä samaa strategiaa kuin SetProperty<T>-funktion kanssa.

Lisätään seuraava logiikka näkymän muuttamiseksi execute-lambdaamme hyödyntäen is-avainsanaa:

x =>
{
    if (x is InputViewModel input)
    {
        CurrentViewModel = _resultViewModel;
    }
    else
    {
        CurrentViewModel = _inputViewModel;
    }
},
MainViewModel.cs

Tarkistamme siis, että onko vastaanottamamme parametri (näkymä, jossa olemme tällä hetkellä) painon ja pituuden syötenäkymä. Jos on, niin vaihdamme näkymää asettamalla CurrentViewModel-propertyyn tulosten ViewModelin. Jos ei ole, niin olemme oletettavasti tulosnäkymällä ja voimme vaihtaa takaisin syötenäkymälle.

Muutoksemme vaikuttavat lupaavilta

Navigointi toiminnassa.

Tämän perusteella on aika selvää, että mitä meidän tulee korjata seuraavaksi: painoindeksin laskenta.

Navigointi onnistuu myös virheellisesti tulosnäkymälle siten, että emme ole syöttäneet painolle tai pituudelle arvoja ollenkaan!

Korjataan nämä ongelmat seuraavassa osassa.

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