0
votes

I have been working through some of the examples of MVVM & WPF and while doing some debugging I find that the RelayCommand associated with a button on my view is constantly firing (executing the associated ImportHoursCommand) as soon as the program starts.

Here are the code snippets:

View

<Button x:Name="ImportHoursButton" Content="Import Hours" 
        Command="{Binding ImportHoursCommand}" 
        Height="25" Width="100" Margin="10"
        VerticalAlignment="Bottom" HorizontalAlignment="Right"                
        Grid.Row="1" />

ViewModel

        private RelayCommand _importHoursCommand;
        public ICommand ImportHoursCommand
        {
            get
            {
                if (_importHoursCommand == null)
                {
                    _importHoursCommand = new RelayCommand(param => this.ImportHoursCommandExecute(), 
                                                            param => this.ImportHoursCommandCanExecute);
                }
                return _importHoursCommand;
            }
        }

        void ImportHoursCommandExecute()
        {
            MessageBox.Show("Import Hours",
                            "Hours have been imported!",
                            MessageBoxButton.OK);
        }

        bool ImportHoursCommandCanExecute
        {
            get
            {
                string userProfile = System.Environment.GetEnvironmentVariable("USERPROFILE");
                string currentFile = @userProfile + "\\download\\test.txt";
                if (!File.Exists(currentFile))
                {
                    MessageBox.Show("File Not Found", 
                                    "The file " + currentFile + " was not found!", 
                                    MessageBoxButton.OK);
                    return false;
                }
                return true;
            }
        }

If I put a breakpoint on the 'string userProfile = ...' line and run the program, Visual Studio will stop on the breakpoint and continue to stop on the breakpoint everytime I click the debug 'Continue' button. If I don't have a breakpoint the program appears to run OK but should this command always be checking if it can execute?

I am using the RelayCommand from Josh Smith's article here.

2

2 Answers

6
votes

If a Button is bound to a Command, the CanExecute() determines if the Button is enabled or not. This means the CanExecute() is run anytime the button needs to check it's enabled value, such as when it gets drawn on the screen.

Since you're using a breakpoint in VS, I am guessing that the application is getting hidden when VS gains focus, and it is re-drawing the button when you hit the Continue button. When it re-draws the button, it is evaluating CanExecute() again, which goes into the endless cycle you're seeing

One way to know for sure is to change your breakpoint to a Debug.WriteLine, and watch your output window when your application is running.

As a side note, you could also change your RelayCommand to Microsoft Prism's DelegateCommand. I haven't looked at the differences too closely, however I know RelayCommands automatically raise the CanExecuteChanged() event when certain conditions are met (properties change, visual invalidated, etc) while DelegateCommands will only raise this event when you specifically tell it to. This means CanExecute() only evaluates when you specifically tell it to, not automatically, which can be good or bad depending on your situation.

3
votes

That's perfectly normal; WPF reevaluates whether the command can be executed very often, for instance when the focus control changes, or when the window takes focus. Every time you click "Continue", the window takes the focus again, which reevaluates the CanExecute of your command, so your breakpoint is hit again.