Använda kommandon i en viewmodel

Slutförd

Du såg hur du hämtar data från dina viewmodels till ditt användargränssnitt och hur du kan använda dubbelriktad bindning för att få tillbaka data till dina viewmodels.

Att använda dubbelriktade bindningar på det sättet är det bästa sättet att reagera på ändringar från användargränssnittet när data ändras. Många saker som vi skulle hantera som händelser kan hanteras med hjälp av tvåvägsbindningar och MVVM-mönstret (Model-View-ViewModel). Andra exempel är saker som Switch.IsToggled och Slider.Value, som kan återspeglas i vår viewmodel som ett booleskt värde eller heltalsvärde, utan att behöva använda händelser.

Men det finns vissa saker, till exempel en Button eller MenuItem aktivering, som inte är direkt kopplade till att ändra data. Dessa interaktioner kräver fortfarande händelseliknande hantering. Eftersom dessa gränssnittskomponenter vanligtvis anropar någon form av logik med data, vill vi ha den logiken i viewmodel. Men vi vill inte hantera dem som Clicked och Selected händelser i koden bakom, om möjligt. Vi vill att så mycket som möjligt ska vara i viewmodel, på det sättet är det testbart.

Använd kommandomönstret

Många av .NET MAUI-kontrollerna som har den här typen av interaktion stöder bindning till en egenskap som exponerar ett ICommand gränssnitt. Den här egenskapen heter Commandtroligen . Kontrollen Button är ett exempel:

<Button Text="Give Bonus" Command="{Binding GiveBonusCommand}" />

Kontrollen vet när kommandot ska anropas. En knapp anropar till exempel kommandot när den trycks ned. Kommandot i det här exemplet är bundet GiveBonus till egenskapen viewmodel. Egenskapstypen måste implementera ICommand gränssnittet. Koden skulle se ut ungefär så här:

public class EmployeeViewModel : INotifyPropertyChanged
{
    public ICommand GiveBonusCommand {get; private set;}
    ...
}

Gränssnittet ICommand har en Execute metod som anropas när knappen trycks in. På så sätt ICommand.Execute ersätter direkt händelsehanteringskoden Button.Click .

Det fullständiga ICommand gränssnittet har ytterligare två metoder: CanExecute och CanExecuteChanged som används för att avgöra om en kontroll ska visas aktiverad eller inaktiverad.

En knapp kan till exempel se nedtonad ut om CanExecute den returnerar false.

ICommand Så här ser gränssnittet ut i C#:

public interface ICommand
{
    bool CanExecute(object parameter);
    void Execute(object parameter);
    event EventHandler CanExecuteChanged;
}

Använda klassen Command

Med det här kommandomönstret kan du upprätthålla en ren separation av användargränssnittets beteende från implementeringen av användargränssnittet. Men det kan komplicera koden om du behöver skapa en separat klass för att implementera varje händelsehanterare.

I stället för att skapa flera anpassade klasser som implementerar gränssnittet är det vanligt att använda Command eller Command<T>. Dessa klasser implementerar ICommand men exponerar dess beteende som egenskaper i din vymodell som du kan ange. På så sätt kan vi implementera egenskapen GiveBonus som beskrevs tidigare helt i vår viewmodel-klass:

public class EmployeeViewModel : INotifyPropertyChanged
{
    public ICommand GiveBonusCommand {get; private set;}
    public EmployeeViewModel(Employee model)
    {
        GiveBonusCommand = new Command(GiveBonusExecute, GiveBonusCanExecute)
    }

    void GiveBonusExecute()
    {
        //logic for giving bonus
    }

    bool GiveBonusCanExecute()
    {
        //logic for deciding if "give bonus" button should be enabled.
    }
}

I den här koden tillhandahålls Execute beteendet av metoden GiveBonusExecute. Och CanExecute tillhandahålls av GiveBonusCanExecute. Ombud till dessa metoder skickas till Command konstruktorn. I det här exemplet finns det ingen implementering för CanExecuteChanged.

Förenkla med MVVM Toolkit

MVVM Toolkit-biblioteket innehåller implementeringar av ICommand kända som RelayCommand och AsyncRelayCommand. Den tillhandahåller också källgeneratorer för att förenkla den här koden ytterligare. I följande exempel GiveBonusCommand genereras inställningen både den metod som ska anropas för att köras och för att anropa för att se om den kan köras. Attributet [RelayCommand] används på GiveBonus metoden och genererar GiveBonusCommand. Genom att ange CanExecute egenskapen för attributet till namnet på den metod som vi vill ansluta till CanExecute metoden ICommandför genererar den dessutom koden för att konfigurera detta åt oss.

public partial class EmployeeViewModel : ObservableObject
{
    public EmployeeViewModel(Employee model)
    {
    }

    [RelayCommand(CanExecute = nameof(GiveBonusCanExecute))]
    void GiveBonus()
    {
        //logic for giving bonus
    }

    bool GiveBonusCanExecute()
    {
        //logic for deciding if "give bonus" button should be enabled.
        return true;
    }
}

MVVM Toolkit hanterar async också metoder som är vanliga i .NET-programmering.

Kommandon med parametrar

Gränssnittet ICommand accepterar en object parameter för CanExecute metoderna och Execute . .NET MAUI implementerar det här gränssnittet utan någon typkontroll via Command klassen. De ombud som du bifogar till kommandot måste utföra sin egen typkontroll för att säkerställa att rätt parameter skickas. .NET MAUI tillhandahåller också implementeringen Command<T> där du anger vilken typ av parameter som förväntas. När du skapar ett kommando som accepterar en enskild typ av parameter använder du Command<T>.

.NET MAUI-kontroller som implementerar kommandomönstret tillhandahåller CommandParameter egenskapen. Genom att ange den här egenskapen kan du skicka en parameter till kommandot när du anropar den med Execute, eller när kommandot kontrollerar CanExecute metoden för status.

I det här exemplet skickas strängvärdet "25" till kommandot:

<Button Text="Give Bonus" Command="{Binding GiveBonusCommand}" CommandParameter="25" />

Kommandot skulle behöva tolka och konvertera strängparametern. Det finns många sätt att tillhandahålla en starkt typad parameter.

  • I stället för att använda attributsyntax för att definiera CommandParameteranvänder du XAML-element.

    <Button Text="Give Bonus" Command="{Binding GiveBonusCommand}">
        <Button.CommandParameter>
            <x:Int32>25</x:Int32>
        </Button.CommandParameter>
    </Button>
    
  • CommandParameter Binda till en instans av rätt typ.

  • CommandParameter Om är bunden till den felaktiga typen använder du en konverterare för att konvertera värdet till rätt typ.

Kontrollera dina kunskaper

1.

Vad är syftet med ICommand gränssnittet?

2.

Hur kan klasserna Command eller Command<T> förenkla användningen av ICommand gränssnittet?

3.

Vilken roll CommandParameter har egenskapen i .NET MAUI-kontroller?