Search code examples
wpficommand

How exactly do Commands get notified to execute their CanExecute?


Imagine, you have a simple WPF window with just a TextBox and a Button on it. The TextBox's Text property is bound to a property called FileName and the Button's Command property is bound to a property called ImportCommand.

<StackPanel>
    <TextBox Text="{Binding FileName, UpdateSourceTrigger=PropertyChanged}" />
    <Button Content="Import" Command="{Binding ImportCommand}" />
</StackPanel>

The view model is pretty much boiler plate code as well.

Public Class MainViewModel
    Inherits ObservableItem

    Private _fileName As String
    Private _importCommand As ICommand = New RelayCommand(AddressOf Me.Import, AddressOf Me.CanImport)

    Public Sub New()
        Me.FileName = "C:\Temp\temp.dat"
    End Sub

    Public Property FileName As String
        Get
            Return _fileName
        End Get
        Set(value As String)
            MyBase.SetProperty(Of String)(_fileName, value)
        End Set
    End Property

    Public ReadOnly Property ImportCommand As ICommand
        Get
            Return _importCommand
        End Get
    End Property

    Private Sub Import()
        Throw New NotImplementedException
    End Sub

    Private Function CanImport() As Boolean
        Return Not String.IsNullOrEmpty(Me.FileName) AndAlso IO.File.Exists(Me.FileName)
    End Function

End Class

There's no obvious connection between the TextBox and the Button, and there's no obvious connection between the FileName property and the ImportCommand.

So, how does the ImportCommand detect, that I may have changed the FileName property and that this change may affect the enabled state of the Button, the ImportCommand is bound to?

Does WPF call CanExecute on any Command and any PropertyChanged that's happening? Sounds like a lot of unnecessary work to me?


Solution

  • The CanExecuteChanged event from the RelayCommad listens to the CommandManager.RequerySuggested event that is raised by the CommandManager.InvalidateRequerySuggested method.

    The CommandManager again listens to the InputManager KeyUp MouseUp or GotKeyboardFocus or LostKeyboardFocus events and calls InvalidateRequerySuggested to notify the UI to call the CanExecute method of the Command.