dotnet / maui

.NET MAUI is the .NET Multi-platform App UI, a framework for building native device applications spanning mobile, tablet, and desktop.
https://dot.net/maui
MIT License
22.02k stars 1.73k forks source link

CanExecute can not change Button clickability #22652

Closed CodingOctocat closed 3 months ago

CodingOctocat commented 4 months ago

Description

This issue has been mentioned but not resolved. Similar issues, See:

  1. 9753

  2. 5364

  3. 6297

In my case I use Android/MvvmToolkit, [RelayCommand(CanExecute = nameof(IsValidForm))], if IsValidForm(ObservableProperty) is initially true, the button is clickable, if false, the button is not clickable, and changes to the value of IsValidForm don't change the state of the button (not the visual style, but the clickability).

Steps to Reproduce

No response

Link to public reproduction project repository

No response

Version with bug

8.0.40 SR5

Is this a regression from previous behavior?

Yes, this used to work in .NET MAUI

Last version that worked well

Unknown/Other

Affected platforms

Android, I was not able test on other platforms

Affected platform versions

No response

Did you find any workaround?

No response

Relevant log output

No response

github-actions[bot] commented 4 months ago

Hi I'm an AI powered bot that finds similar issues based off the issue title.

Please view the issues below to see if they solve your problem, and if the issue describes your problem please consider closing this one and thumbs upping the other issue to help us prioritize it. Thank you!

Closed similar issues:

Note: You can give me feedback by thumbs upping or thumbs downing this comment.

RoiChen001 commented 4 months ago

Can repro this issue at Android platform on the latest 17.11.0 Preview 1.0(8.0.7/8.0.10/8.0.21). BindingBreaksStyle.zip

mattleibow commented 4 months ago

This looks related: https://github.com/dotnet/maui/issues/9753

CodingOctocat commented 4 months ago

This looks related: #9753

Not quite the same, #9753 case is that the visual of the button has not changed, but in my case the button cannot be enabled/disabled based on the CanExecute change.

drasticactions commented 4 months ago

FWIW I was able to get it working just fine

https://github.com/drasticactions/MauiRepros/tree/main/MvvmTestCanExecute

スクリーンショット 2024-05-28 16 08 48 スクリーンショット 2024-05-28 16 08 59

It may be that you didn't set up your model correctly? Maybe the library you are using is at fault? If you make a sample showing what you're doing that could help, or maybe run what I wrote above and see if it doesn't work for you?

CodingOctocat commented 3 months ago

@drasticactions Hi, I made a sample, I use Mvvm Toolkit, and the [RelayCommand] feature.

public partial class MainViewModel : ObservableObject
{
    [ObservableProperty]
    private bool _canDoWork;  // binding to Switch button

    [RelayCommand(CanExecute = nameof(CanDoWork))]
    private static void DoWork()  // binding to 'Click me' button
    {
        Debug.WriteLine("DoWork");
    }
}

动画

MauiIssue22652.zip

drasticactions commented 3 months ago

Thank you for the sample! I believe you set up your view model incorrectly.

First, in your app page, your View Model isn't set as the BindingContext, it's null, so it will never work.

Then, in your ViewModel,

public partial class MainViewModel : ObservableObject
{
    [ObservableProperty]
    private bool _canDoWork;

    [RelayCommand(CanExecute = nameof(CanDoWork))]
    private static void DoWork()
    {
        Debug.WriteLine("DoWork");
    }
}

I am not well versed in the CommunityToolkit MVVM, but I believe you didn't set it up correctly. CanExecute gets called whenever someone calls for it to be raised, it's not done automatically. So when CanDoWork gets updated, you then need to raise the event on DoWork to be able to call CanExecute.

Changing it to this:

public partial class MainViewModel : ObservableObject
{
    [ObservableProperty]
    private bool _canDoWork;

    partial void OnCanDoWorkChanged(bool value)
    {
       this.DoWorkCommand.NotifyCanExecuteChanged();
    }

    [RelayCommand(CanExecute = nameof(CanDoWork))]
    private async void DoWork()
    {
        Debug.WriteLine("DoWork");
    }
}

Allows it to work. Likewise, DoWork can't be static, or else you can't call on the command for NotifyCanExecuteChanged. I would encourage you to read their documentation for more about it, but I do not believe this is a MAUI bug.

スクリーンショット 2024-05-28 17 23 59 スクリーンショット 2024-05-28 17 23 53
CodingOctocat commented 3 months ago

@drasticactions Thanks! In my sample I did forget to set BindingContext, I didn't actually forget this in my actual project, the main problem was that I forgot to add [NotifyCanExecuteChangedFor(nameof(DoWorkCommand))].

drasticactions commented 3 months ago

I'm glad you solved it! I'm also pleased that NotifyCanExecuteChangedFor exists; since my solution was kind of janky, that one makes more sense.