maui

What causes the ICommand of the ContentView to never be invoked?


I have a custom control as follows:

public partial class MyControl : ContentView
{
    public event EventHandler? MyClicked;
    public MyControl()
    {
        InitializeComponent();
        InternalCommand = new Command(
            execute: () =>
            {
                MyClicked?.Invoke(this, EventArgs.Empty);//  breakpoint is added here.
            },
            canExecute: () => true
        );
    }

    public ICommand InternalCommand { get; set; }
}
<ContentView
    ...
    x:Name="This">
    <Button
        Command="{Binding InternalCommand,Source={Reference This}}"
        Text="Click Me" />
</ContentView>

A page subscribe to the control's clicked event as follows:

public partial class MyPage : ContentPage
{
    public MyPage()
    {
        InitializeComponent();
    }

    private void MyControl_MyClicked(object sender, EventArgs e)
    {
       DisplayAlert("MyControl_MyClicked", "Clicked", "Ok");
    }
}
<ContentPage
    ...
    xmlns:loc="clr-namespace:MyProject"
    >
    <loc:MyControl
        MyClicked="MyControl_MyClicked" />
</ContentPage>

In debug mode the breakpoint never gets reached when I click the button. What is the culprit?

Edit

Even though I use BindingContext as follows, it still does not work.

<ContentView
    ...
    x:Name="This">
    <Button
        BindingContext="{Reference This}"
        Command="{Binding InternalCommand}"
        Text="Click Me" />
</ContentView>

However it WORKS if I use ControlTemplate.

<ContentPage
    ...
    xmlns:loc="clr-namespace:MyProject">
    <ContentPage.Resources>
        <ControlTemplate
            x:Key="MyTemplate">
            <Button
                Text="Click Me (templated)"
                Command="{TemplateBinding InternalCommand, x:DataType='loc:MyControl'}" />
        </ControlTemplate>
    </ContentPage.Resources>
    <loc:MyControl
        MyClicked="MyControl_MyClicked" ControlTemplate="{StaticResource MyTemplate}" />
</ContentPage>

Solution

  • Because of the InitializeComponent(); that happens earlier your InternalCommand will appear uninitialize and even though you updated after, it still will not be seen. To correct this, you need to either:

    1. Move the initialization to before the InitializeComponent();, -- OR --
    2. Add OnPropertyChanged(nameof(InitialCommand)); at the end