Search code examples
c#wpfmvvmcanexecute

Notify user that command could not be executed


I have a TextBox that is tied to a command like this:

<TextBox Text="{Binding Path=TextContent, UpdateSourceTrigger=PropertyChanged}">
        <TextBox.InputBindings>
            <KeyBinding Command="{Binding Path=MyCommand}" Key="Enter" />
        </TextBox.InputBindings>
</TextBox>

The property TextContent is a string defined in the ViewModel. The command MyCommand is also defined in the ViewModel. The ViewModel does not know the View.

The command will be called whenever the TextBox has focus and the enter key is hit. Unfortunately, if CanExecute returns false, the user cannot see (visually) that the command was not executed, because there is no visual change in the TextBox.

I am looking for advice on how to show the user that the command could not be executed after he had pressed enter.

My ideas (and my doubts about them):

  • Disabling the TextBox when CanExecute returns false: This is no option because the return value of CanExecute can change everytime a letter is typed/changed (the text in the TextBox influences the outcome of CanExecute). When it is disabled for the first time, the user cannot type into it any more, so it will stay disabled forever.

  • Show a message box saying that the command was not executed: Remember, the ViewModel does not know the View. Is it even possible to open a message box from the ViewModel? Furthermore, where should I put the call to opening a message box? Not inside CanExecute because I only want to get the message box after hitting enter, not everytime CanExecute returns false. Maybe make CanExecute always return true and do the checks inside Execute: If checks are okay, do the command stuff, if not, show some message to the user. But then, the point of having CanExecute is missed entirely...

I want to keep MVVM, but some codebehind for redirecting stuff to the ViewModel seems okay for me.


Solution

  • I suggest the following solution.

    Here's an example on how to notify the user which I'm working on at the moment.

    I want the user to type in a data limit which is of type int, double or string. It want to check so the user type in the correct type. I use a property ValidateLimits which checks the string MyLimits which in your case is TextContent.

    Everytime the user type in anything in the TextBox, ValidateLimits will check the string. If it is not a valid string in the textbox then return false otherwise return true. If false then highlight it with the DataTrigger by setting some Properties on the TextBox which in my case is some Border and Foreground colors, also a ToolTip.

    Also in your case you want to call your Validate method in your CanExecute method.

    If you already have a function for checking that the command is OK then just add it to the DataTrigger binding.

    <TextBox Text="{Binding MyLimit1, UpdateSourceTrigger=PropertyChanged}" Margin="-6,0,-6,0">
      <TextBox.Style>
        <Style TargetType="TextBox">
         <!-- Properties that needs to be changed with the data trigger cannot be set outside the style. Default values needs to be set inside the style -->
         <Setter Property="ToolTip" Value="{Binding FriendlyCompareRule}"/>
           <Style.Triggers>
             <DataTrigger Binding="{Binding ValidateLimits}" Value="false">
               <Setter Property="Foreground" Value="Red"/>
               <Setter Property="BorderBrush" Value="Red"/>
               <Setter Property="BorderThickness" Value="2"/>
               <Setter Property="ToolTip" Value="Cannot parse value to correct data type"/>
            </DataTrigger>
          </Style.Triggers>
        </Style>
      </TextBox.Style>
    

    public bool ValidateLimits
    {
      get
      {
        // Check if MyLimit1 is correct data type
        return true/false;
      }
    }