canton7 / Stylet

A very lightweight but powerful ViewModel-First MVVM framework for WPF for .NET Framework and .NET Core, inspired by Caliburn.Micro.
MIT License
988 stars 143 forks source link

Button not enabling when its CanX property returns true #148

Closed trustieee closed 3 years ago

trustieee commented 4 years ago

Reproduced in this repo here: https://github.com/mariocatch/stylet-foo

I'm using the default Stylet template with dotnet core 3.1. I simply added a property/method for reproducing the issue.

<TextBox Text="{Binding Username, 
                Mode=TwoWay, 
                UpdateSourceTrigger=PropertyChanged}" 
         Width="100" Height="100"/>

<Button Command="{s:Action Do}">Foo</Button>
public class ShellViewModel : Screen
{
    public string Username { get; set; }

    public void Do() { }

    public bool CanDo => !string.IsNullOrWhiteSpace(Username);
}

Breakpoint on CanDo property is hit once and never hit again. Changing the value in the TextBox, while calling set, does not retrigger the CanDo property.

I've also tried using backing fields with SetAndNotify, to no avail:

public string Username { get => username; set => SetAndNotify(ref username, value, nameof(Username)); }
trustieee commented 4 years ago

I realize I am missing property changed notifications. Through all of the examples I saw I assumed this was handled implicitly by Stylet for me.

After changing my property setters to the following, it works as expected.

public string Username
{
    get => _username;
    set
    {
        SetAndNotify(ref _username, value);
        NotifyOfPropertyChange(() => CanLogin);
    }
}

If there's a more intended way to handle this please let me know - I'll close this in a bit.

canton7 commented 4 years ago

Yes, you'll need to raise an INPC event for CanLogin whenever Username changes. I'll make sure the wiki makes this clear.

I normally wire things together in the constructor:

Bind(s => s.Username, (o, e) => NotifyOfPropertyChange(nameof(CanLogin)));
canton7 commented 4 years ago

Re-opening as a reminder to improve the docs

haven1433 commented 4 years ago

I'd be happy to handle the wiki change if you'd like

canton7 commented 4 years ago

Sure, please go ahead!

haven1433 commented 4 years ago

Updated https://github.com/canton7/Stylet/wiki/PropertyChangedBase examples to include the "wire in constructor" version and replaced the update-in-method examples with update-in-property examples to make it more clear. If you're happy with the changes, please close this issue.

ShemJM commented 4 years ago

Hi, I have a question regarding Guard Properties. Wasn't sure where to ask but this thread seems kind of related.

How can I pass the CommandParameter into a guard property? So a button would be enabled/disabled depending on the state of the bound model.

canton7 commented 4 years ago

@haven1433 Thank you!

@ShemJM That's a limitation of guard properties, and a reason why guard methods are suggested in #147 (although it's not straightforward). It's probably easiest to bind that thing to another property in the VM, and inspect it from your guard property's getter. Make sure you raise the appropriate INPC events.

ShemJM commented 4 years ago

@canton7 Thanks. I'll give it a go and thanks for Stylet!

canton7 commented 3 years ago

The wiki page on guards now uses INPC throughout