tags:

views:

186

answers:

2

I'm having trouble getting the RelayCommand to enable/disable the attached control properly.

I've got an EventToCommand element attached to a button. The command is databound to the ViewModel. Initially, the button is disabled (expected behavior), but I cannot seem to get the CanExecute logic to check it's value. When CurrentConfigFile is set and exists, the button should be enabled. I've executed code and checked the file's value in debug to make sure it's set, but the control is still disabled. I've tried CommandManager.InvalidateRequerySuggested() and command.RaiseCanExecuteChanged(), but it will not enable.

I've wondered if lambdas don't work correctly for the CanExecute behavior (even though the examples use them) or that the CanExecute behavior needs to be databound to another element.

Here's my code:

// The FileInfo being checked for existence before the button should be enabled
public const string CurrentConfigFilePN = "CurrentConfigFile";
public FileInfo CurrentConfigFile
{
    get
    {
        return _currentConfigFile;
    }

    set
    {
        if (_currentConfigFile == value)
        {
            return;
        }

        var oldValue = _currentConfigFile;
        _currentConfigFile = value;

        // Update bindings, no broadcast
        RaisePropertyChanged(CurrentConfigFilePN);
    }
}

public MainViewModel()
{
    // snip //

    SaveCommand = new RelayCommand(SaveConfiguration, 
        () => CurrentConfigFile != null && CurrentConfigFile.Exists);
    }

private void SaveConfiguration()
{

    // export model information to xml document
    ExportXMLConfiguration(CurrentConfigFile);

}

and markup

<Button x:Name="SaveButton" Content="Save" Width="75" Margin="20,5">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Click">
            <GalaSoft:EventToCommand x:Name="SaveETC" 
                Command="{Binding SaveCommand}" 
                MustToggleIsEnabledValue="true" />
        </i:EventTrigger>
    </i:Interaction.Triggers>
</Button>

Update:

As per Isak Savo's suggestion, I bound the RelayCommand directly to the button with

<Button x:Name="SaveButton" Content="Save" Width="75" Margin="20,5" 
Command="{Binding SaveCommand}"/>

and it started disabled and correctly enabled when the FileInfo was set. Guess I should remember not to fix what isn't broken!

+1  A: 

In msdn is written:

When first called, FileInfo calls Refresh and caches information about the file. On subsequent calls, you must call Refresh to get the latest copy of the information.

However, I would not do such a check in the CanExecute-handler. This may slow down your UI because CanExecute is called a lot of times and I could imagine that such IO-checks can become slow, for example if the file lies on a network share.

HCL
Interesting point, and I'm glad you brought it up (mostly because I didn't know that). However, even after I added the refresh to the getter (what you suggested not to do due to performance concerns) the behavior didn't change. The button doesn't enable even though `Exists` is true.
liquidrogue
How your relay-command handles the CanExecuteChanged-event? How is this event integrated? Maybe the error is to be found there.
HCL
I'm using the MVVM-light framework, so the `RelayCommand` class is built into the framework. (Un?)fortunately, Laurent Bugnion has decided to crowdsource support for the framework here on SO, which is why I'm asking the question here. I'll try looking at the source code for MVVM-light for more insight.
liquidrogue
+1  A: 

Why don't you just bind to the Command directly from the Button?

<Button Command="{Binding SaveCommand}" Content="Save" />

Maybe the EventToCommand thing you are using is messing things up with the command's CanExecute notification.

And regarding the CanExecute problem - are you sure that your CanExecute handler is called after the CurrentConfigFile property is set? I've found that even though WPF mostly does a good job of requerying CanExecute, I still sometimes need to force a requery through the CommandManager.

EDIT: As pointed out in the comments, the OP has already tried the command manager approach.

Isak Savo
@Isak Savo: He wrote in his question that he called CommandManager.InvalidateRequerySuggested(). That's why I think he should take a look at the RelayCommand.
HCL
@HCL: Silly me, I should read the original question more carefully :-)
Isak Savo
I'll take a look at that. The `EventToCommand` element is part of MVVM-light (tagged) and that's why I'm trying to use it. It's very useful for keeping MVVM-centric, except for what appears to be a rather glaring issue. Thanks for the input! I'll keep trying.
liquidrogue
My point here is that it adds absolutely nothing. The Command property on the button reacts (internally) on click so you gain nothing from using EventToCommand. The reason it exists is when a control doesn't have a command property, or you want to execute a command on some other event
Isak Savo
@Isak Savo - Sorry for the late response. I see what you mean, how the `EventToCommand` control doesn't add anything that the `ButtonBase` doesn't handle internally. I'll try removing the `EventToCommand` and see if it acts differently. Thanks for pointing that out!
liquidrogue