Search code examples
c#.netmvvmmaui

How to provide Command-Property for custom control in MVVM (.NET MAUI)


I'm trying to build a custom control in .NET MAUI, that should provide an BindableProperty of ICommand for its parent. Here is a basic example for what i try to achieve.

MainPage View (MainPage.xaml)

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:views="clr-namespace:SampleApp.Views"
             x:Class="SampleApp.MainPage">

    <views:MyCustomControl DoSomething="{Binding DoSomethingCommand}"></views:MyCustomControl>
</ContentPage>

MainPage View Class (MainPage.xaml.cs)

public partial class MainPage : ContentPage
{
    public MainPage()
    {
        InitializeComponent();
        BindingContext = new MainPageViewModel();
    }
}

MainPage View Model (MainPageViewModel.cs)

public class MainPageViewModel : ObservableObject
{
    public ICommand ProcessNewScoreCommand { get; }

    public MainPageViewModel()
    {
        ProcessNewScoreCommand = new Command(DoSomething);
    }

    private void DoSomething()
    {
        // Do something
    }
}

MyCustomControl View Class (MyCustomControl.xaml.cs)

public partial class MyCustomControl : ContentView
{
    public static readonly BindableProperty DoSomethingProperty =
        BindableProperty.Create(nameof(DoSomething), typeof(ICommand), typeof(MyCustomControl));

    public ICommand DoSomething
    {
        get => (ICommand)GetValue(DoSomethingProperty);
        set => SetValue(DoSomethingProperty, value);
    }

    public MyCustomControl()
    {
        InitializeComponent();
        BindingContext = new MyCustomControlViewModel(DoSomething);
    }
}

MyCustomControl View-Model (MyCustomControlViewModel.cs)

public class MyCustomControlViewModel : ObservableObject
{
    public ICommand DoSomething { get; }

    public MyCustomControlViewModel(ICommand doSomethingCommand)
    {
        DoSomething = doSomethingCommand;
    }

    private void PerformSomeLogic()
    {
        // Any calulations/logic
        // ...

        if (DoSomething.CanExecute(null))
        {
            // Execute command, so that parent component gets informed and can act.
            DoSomething.Execute(null);
        }
    }
}

While debugging, the Property DoSomething of class MyCustomControl.xaml.cs is always null. Also it seems, that its setter isn't called at any time. What am I doing wrong?


Solution

  • The reason it's not working is that you are creating a VM for your custom control i.e. the view then assigning it a context and then later on in your custom control you are changing its BindingContext all over again, Custom Controls should not have their own predefined BindingContext.

    Steps to fix this:

    • Remove your MyCustomControlViewModel class.

    • Now your CustomControl.xaml.cs would look like this:

      public partial class MyCustomControl : ContentView
      {
      
         public static readonly BindableProperty DoSomethingProperty =
                      BindableProperty.Create(nameof(DoSomething), typeof(ICommand), typeof(MyCustomControl));
      
         public ICommand DoSomething
         {
            get => (ICommand)GetValue(DoSomethingProperty);
            set => SetValue(DoSomethingProperty, value);
         }
      
         public MyCustomControl()
         {
            InitializeComponent();
         }
      }
      
    • Now your View's VM should be the one that handles the logic so you don't have multiple BindingContext's confusing and the compiler(By searching it in the wrong place):

      public class MainPageViewModel : ObservableObject
      {
          public ICommand DoSomething { get; }
      
          public MainPageViewModel()
          {
            DoSomething = new Command(DoSomething);
          }
      

    Good luck, let me know if there is some other questions you have.

    A basic guide to MVVM with XF and MAUI: https://learn.microsoft.com/en-us/xamarin/xamarin-forms/xaml/xaml-basics/data-bindings-to-mvvm