2

I've got a TextBox for the user to enter a string, and an "Add" button to perform some tasks with the value. I added a CanExecute method to the DelegateCommand so that the button would only work when there was text in the box.

However, it's disabled all the time - even when the condition is true. Typing in the box does not enable the button.

<TextBox Text="{Binding BuildEntryText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<Button Command="{Binding AddBuildCommand}" Content="Add" />

this.AddBuildCommand = new DelegateCommand(() => this.AddBuild(), () => this.CanAddBuild());

private bool CanAddBuild()
{
    if (this.BuildEntryText != null)
    {
        return true;
    }
    else
    {
        return false;
    }
}

I've had this happen before (not always with the same types of controls), and ordinarily I would just remove the CanExecute and change it to an "if" check inside the action method. This time, though, I actually have to get it working and grey the button out the "proper" way.

Any insight would be greatly appreciated. Thanks in advance.

UPDATE:

I've added RaiseCanExecuteChanged to the set portion of the BuildEntryText property. Now the button starts disabled and becomes enabled when text is first typed. However, it remains enabled even after the text is deleted or the box is cleared by the add function.

public string BuildEntryText
{
    get
    {
        return this.buildEntryText;
    }

    set
    {
        this.SetProperty<string>(ref this.buildEntryText, value);
        this.AddBuildCommand.RaiseCanExecuteChanged();
    }
}
Nightmare Games
  • 2,205
  • 6
  • 28
  • 46
  • 1
    What kind of `DelegateCommand` do you have? It sounds like it's one that doesn't automatically requery CanExecuteChanged when any change notification occurs. The solution is to either raise the CanExecuteChanged manually when needed, [like this](http://stackoverflow.com/a/11582444/302677), or change your command definition – Rachel Mar 07 '15 at 01:17
  • @Rachel: I didn't know DelegateCommand came in different kinds. I added RaiseCanExecuteChanged after the SetProperty call in BuildEntryText, and now the button becomes enabled when text is entered. However, it does not disable when the text is deleted. – Nightmare Games Mar 07 '15 at 01:42
  • @Cricketheads, show your DelegateCommand implementation. If CanExecuteChanged is not implemented properly, it won't notify the control bound to the command. – Thomas Levesque Mar 07 '15 at 02:21
  • @ThomasLevesque: I'm not entirely sure, but it sounds like you're asking about where I added the RaiseCanExecuteChanged, which I've appended to the question above. – Nightmare Games Mar 09 '15 at 18:37
  • @Cricketheads, no, I'm asking about how you implemented the `CanExecuteChanged` event. In typical implementations, you just delegate to the `CommandManager.RequerySuggested` event, which is raised automatically. If you do that, you don't need to call `RaiseCanExecuteChanged` manually. – Thomas Levesque Mar 09 '15 at 19:06
  • @ThomasLevesque: I'm not currently calling CanExecuteChanged anywhere. I believe you've lost me a bit with your phrasing. If I need that, then where would I place the call to it? – Nightmare Games Mar 09 '15 at 20:50
  • @Cricketheads, CanExecuteChanged is an event, so you don't "call" it: you raise it. But anyway, in the typical DelegateCommand implementation, you don't raise it yourself: you just subscribe the handler to CommandManager.RequerySuggested, so that the event is raised automatically. See this example: https://gist.github.com/thomaslevesque/6ef09ff74331862f2a84 – Thomas Levesque Mar 09 '15 at 21:10
  • @ThomasLevesque: Ok, I see you've got a DelegateCommand class in the example. Thanks for the help, by the way. Are you suggesting I modify the actual code for DelegateCommand itself? Isn't that built into WPF? Mine is coming from a dll. – Nightmare Games Mar 09 '15 at 21:46
  • @Cricketheads, no, it's not built into WPF. WPF provides the ICommand interface, but it doesn't provide an implementation. If you don't have the source for the DelegateCommand you're using, you can add your own implementation and use it instead. – Thomas Levesque Mar 10 '15 at 10:06
  • @ThomasLevesque: I added my own DelegateCommand class based on your example, but it's had no effect on the program's behavior, as far as I can tell. – Nightmare Games Mar 10 '15 at 19:16
  • @Cricketheads, you added it, but are you using it? If your code is still using the implementation from the DLL, of course it has no effect... Just to be sure, change its name to something else (e.g. RelayCommand is a common name for this) – Thomas Levesque Mar 10 '15 at 19:41
  • @ThomasLevesque: Intellisense seemed to think the custom class was the one being used, but now I've renamed it to be certain. However, there's no change in the program. Also, the RelayCommand implementation doesn't seem to support CommandParameters (which is something I'll need down the road). – Nightmare Games Mar 10 '15 at 21:23
  • @Cricketheads, not sure why it's not working... I use that implementation all the time. It's pretty easy to add the parameter, you can even make a generic DelegateCommand that takes care of converting the parameter. – Thomas Levesque Mar 11 '15 at 00:23
  • @ThomasLevesque: I figured out it was actually was working all along (since adding the RaiseCanExecuteChanged), but the conditions in my CanExecute were set up wrong. Check out my answer. – Nightmare Games Mar 11 '15 at 23:55
  • @Cricketheads, yes, but my point was that with the implementation I suggested, you don't need to raise CanExecuteChanged manually, it's done automatically. – Thomas Levesque Mar 12 '15 at 00:09

2 Answers2

0

Your delegate command need's to requery to check if the command can execute .The delegate command defines event called CanExecuteChanged which is raised on the UI thread to cause every invoking control to requery to check if the command can execute

This is how Delegate Command looks like(yours could be similar too)

public class DelegateCommand<T> : System.Windows.Input.ICommand
{
   private readonly Predicate<T> _canExecute;
   private readonly Action<T> _execute;

     public DelegateCommand(Action<T> execute)
    : this(execute, null)
     {
     }

    public DelegateCommand(Action<T> execute, Predicate<T> canExecute)
    {
        _execute = execute;
        _canExecute = canExecute;
    }

    public bool CanExecute(object parameter)
    {
        if (_canExecute == null)
            return true;

        return _canExecute((parameter == null) ? default(T) :(T)Convert.ChangeType(parameter, typeof(T)));
    }

    public void Execute(object parameter)
    {
        _execute((parameter == null) ? default(T) : (T)Convert.ChangeType(parameter, typeof(T)));
    }

    public event EventHandler CanExecuteChanged;
    public void RaiseCanExecuteChanged()
    {
        if (CanExecuteChanged != null)
            CanExecuteChanged(this, EventArgs.Empty);
    }
}

You are binding it to the text command of the TextBox

Here is a sample ViewModel.cs

public class ViewModel
{
    private readonly DelegateCommand<string> MyButtonCommand;

    public ViewModel()
    {
        MyButtonCommand= new DelegateCommand<string>(
            (s) => { MessageBox.Show("Command Executed")}, //Execute
            (s) => { return !string.IsNullOrEmpty(_input); } //CanExecute
            );
    }

    public DelegateCommand<string> AddBuildCommand
    {
        get { return MyButtonCommand; }
    }

    private string _input;
    public string BuildEntryText
    {
        get { return _input; }
        set
        {
            _input = value;
            MyButtonCommand.RaiseCanExecuteChanged();
        }
    }
}  

and In your XAML

 <TextBox Text="{Binding BuildEntryText,   UpdateSourceTrigger=PropertyChanged}" />
<Button Command="{Binding AddBuildCommand}" Content="Add" />
Rohit
  • 10,056
  • 7
  • 50
  • 82
  • Kyle, I'm not sure if I follow what you're doing here. With DelegateCommand, are you sending the contents of the text box through the command as a parameter? If so, don't you need a CommandParameter attribute in the button xaml? It also looks like you've got two separate DelegateCommands going. What's the purpose of having one return the other? – Nightmare Games Mar 09 '15 at 18:45
  • Yes it's passing string as parameter which is your Textbox content.In future If you had to bind it to DateTime you can pass that too.Inside delegate command I have used `Anonymous method` which is actually what you are doing it's just a short cut you can say.Why dont you run this code in a seperate WPF application it will make things clear – Rohit Mar 10 '15 at 05:33
0

I figured out this was a two-part problem, and it wasn't actually a problem with my DelegateCommand (at least, not entirely).

1) I needed to call RaiseCanExecuteChanged, as Rachel suggested, or the UI wasn't going to refresh the control's IsEnabled property.

2) I needed to add an additional condition to check the length of the string, instead of only checking if it's null. Apparently, an empty TextBox starts as null, but if you add characters and then delete them, it doesn't go back to being null again. It's a string of length 0.

<TextBox Text="{Binding BuildEntryText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<Button Command="{Binding AddBuildCommand}" Content="Add" />

public DelegateCommand AddBuildCommand { get; private set; }

this.AddBuildCommand = new DelegateCommand(() => this.AddBuild(), () => this.CanAddBuild());

public string BuildEntryText
{
    get
    {
        return this.buildEntryText;
    }

    set
    {
        this.SetProperty<string>(ref this.buildEntryText, value);

        // PART 1:
        this.AddBuildCommand.RaiseCanExecuteChanged();
    }
}

private bool CanAddBuild()
{
    // PART 2:
    if (this.BuildEntryText != null && this.BuildEntryText.Length > 0)
    {
        return true;
    }
    else
    {
        return false;
    }
}
Nightmare Games
  • 2,205
  • 6
  • 28
  • 46