1

I've looked at some examples of implementing ICommand and to me the following way is the simplest:

class Command : ICommand {
    public Func<object, bool> CanDo { get; set; }
    public Action<object> Do { get; set; }

    public event EventHandler CanExecuteChanged {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public Command(Func<object, bool> CanDo, Action<object> Do) {
        this.CanDo = CanDo;
        this.Do = Do;
    }

    public bool CanExecute(object parameter) => CanDo(parameter);
    public void Execute(object parameter) => Do(parameter);
}

and that's how I've implemented in my test app. In addition to the Command class I've the following class:

class Person : INotifyPropertyChanged {

    string firstName, lastName, enabled;

    public string FirstName {
        get => firstName;
        set { firstName = value; Changed();}
    }

    public string LastName {
        get => lastName;
        set { lastName = value; Changed(); }
    }

    public string Enabled {
        get => enabled;
        set { enabled = value;  Changed(); }
    }

    public Command MyCommand { get; set; }
    public event PropertyChangedEventHandler PropertyChanged;

    public Person() {
        MyCommand = new Command(CanDo, Do);
    }

    void Changed(string name = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));

    bool CanDo(object para) {
        if (FirstName == "test" && LastName == "test") {
            Enabled = "true";
            //Changed("Enabled");
            return true;
        }
        else {
            Enabled = "false";
            //Changed("Enabled");
            return false;
        }
    }

    void Do(object para) {
        FirstName = "first";
        LastName = "last";
    }
}

and in xaml I've these:

<Window ...>
    <Window.Resources>
        <local:Person x:Key="person"/>
    </Window.Resources>      
    <Grid DataContext="{StaticResource person}">
        <StackPanel>
            <TextBox Text="{Binding FirstName}"/>
            <TextBox Text="{Binding LastName}"/>
            <TextBlock Text="{Binding FirstName}"/>
            <TextBlock Text="{Binding LastName}"/>

            <Button Content="Click" Command="{Binding Path=MyCommand}"/>
            <Label Content="{Binding Enabled}"/>
        </StackPanel>         
    </Grid>
</Window>

After launching the app, whatever I try to type in those TextBox gets deleted instantly if I call Changed() in the setter of Enabled. If I comment out the Changed() in setter and uncomment two Changed("Enabled") in bool CanDo(object para) it works as expected!

Shouldn't calling the Changed() once in setter be equivalent to those two Changed("Enabled") call in bool CanDo(object para)?

1 Answers1

0

You are missing the CallerMemberNameAttribute in Changed:

void Changed([System.Runtime.CompilerServices.CallerMemberName]string name = "") => 
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));

This obtains the calling property name so you don't have to call Changed("Enabled") but simply Changed() in the setter.

mm8
  • 163,881
  • 10
  • 57
  • 88
  • If I take out the `Label` in `xaml` and remove the `Enabled` property, I don't need `[CallerMemberName]` there! I'd that initially, then without the `Label` I tried removing that and it worked perfectly. –  Oct 11 '19 at 08:21
  • 1
    @EmonHaque: What is your question? – mm8 Oct 11 '19 at 08:22
  • How did it work without that attribute when I didn't have that Label and associated property? –  Oct 11 '19 at 08:23
  • I could type in those text boxes normally and the button got enabled or disabled with the result of `CanDo` –  Oct 11 '19 at 08:26
  • @EmonHaque: `new PropertyChangedEventArgs("")` indicates that all properties should be refreshed. – mm8 Oct 11 '19 at 08:30
  • that's why my textboxes kept getting refreshed on key press and I couldn't type! Thanks, it works as expected with that attribute. –  Oct 11 '19 at 08:34