Search code examples
c#xamlinheritancewinui-3winui

How do I use [RelayCommand] in base class


Using generics, I have a base class View Model - (MyBaseViewModel<T>) which implements a community toolkit RelayCommand. This is inherited by a ViewModel - MyDataInputControlViewModel : MyBaseViewModel<MyCustomType> supporting a data input user control. The relay command needs to update a database with the data from the user control. How do I pass the data in <MyCustomType to the RelayCommand?

So I am trying to get my head around generics and inheritance and thought that implementing a base viewmodel which handles database CRUD I could save a lot of time repeating commands for all the different data types that need to go into the database. The original ideas came from xamlbrewer but that keeps the relay commands in the user control view model, which I could do but I'd like to get them into the base class.

Code from the base class

public abstract partial class ObservableNavigationAwareViewModelBase<T> : ObservableObject, INavigationAware
{
    readonly DatabaseDAO _databaseDAO;
    private T BaseNewItem;

    public ObservableNavigationAwareViewModelBase(DatabaseDAO databaseDAO) { _databaseDAO = databaseDAO; }

    [RelayCommand]
    private void BaseAddNewRecord() { _databaseDAO.SaveNewRecord(BaseNewItem); }

Code from the user control view model

public partial class AddNewTenantViewModel : ObservableNavigationAwareViewModelBase<Tenant>
{
    DatabaseDAO _databaseDAO;
    public string FirstName { get; set; }

    public AddNewTenantViewModel(DatabaseDAO databaseDAO) : base (databaseDAO)
    {_databaseDAO = databaseDAO;}

    private void CreateNewTenant()
    { BaseNewItem = new Tenant() { FirstName = FirstName, }; }


The Tenant Model

[Table("tenants")]
public partial class Tenant
{
    public string FirstName { get; set; }
}

This is the test xaml that I am using

        <TextBox Text="{x:Bind ViewModel.FirstName, Mode=TwoWay}" Header="First Name" />
        <Button Content="Add" Command="{x:Bind ViewModel.BaseAddNewRecordCommand}" />

Calling on the command in the base class is fine but I cant see how I would create BaseNewItem from there. So as I see it the only place I can create BaseNewItem is in the UserControl ViewModel?? I know that I can access BaseNewItem in the base class and the inherited class but I cant call CreateNewTenant from the base class. So my question is 'How do I get BaseNewItem up to the Base class to be used in the command. Or am I completely missing the point somewhere?


Solution

  • You can use an abstract method:

    public abstract partial class ViewModelBase<T> : ObservableObject
    {
        private readonly DatabaseDAO _databaseDAO = new();
    
        abstract protected object CreateNewItem(string name);
    
        [RelayCommand]
        private void AddNewRecord(string name)
        {
            var record = CreateNewItem(name);
            _databaseDAO.SaveNewRecord(record);
        }
    }
    

    and override it the derived view models:

    public class TenantViewModel : ViewModelBase<Tenant>
    {
        protected override Tenant CreateNewItem(string name)
        {
            return new Tenant(name);
        }
    }
    

    then use the command like this:

    <TextBox x:Name="NameTextBox" />
    <Button
        Command="{x:Bind TenantViewModel.AddNewRecordCommand}"
        CommandParameter="{x:Bind NameTextBox.Text, Mode=OneWay}"
        Content="Save Tenant" />