Results 1 to 2 of 2

Thread: MVVM & IsEnabled issue.

  1. #1
    MattP is offline VB.NET Forum All-Mighty
    .NET Framework
    .NET 4.0
    Join Date
    Feb 2008
    Location
    WY, USA
    Posts
    1,206
    Reputation
    618

    MVVM & IsEnabled issue.

    Posting this in the Silverlight forum since that's what I'm using and I don't see an MVVM section. If a mod needs to move it to the MVC section since it's a pattern question please do so.

    I'm having an issue getting a button to be disabled when CanExecute in my ViewModel is set to false.

    Here's the code for my button:

    Code:
                        <Button Content="Remove Collateral" >
                            <i:Interaction.Triggers>
                                <i:EventTrigger EventName="Click">
                                    <i:InvokeCommandAction Command="{Binding Source={StaticResource vm}, Path=DeleteCollateralCommand}" 
                                                       CommandParameter="{Binding DataContext, ElementName=dataGrid}"/>
                                </i:EventTrigger>
                            </i:Interaction.Triggers>
                        </Button>
    Nothing happens when you click on the button because CanExecute is false but the button is still clickable rather than disabled.

    Here's the code for my DelegateCommand class:

    Code:
    Public Class DelegateCommand
        Implements ICommand
    
        Dim _executeAction As Action(Of Object)
        Dim _canExecuteCache As Boolean
        Dim _canExecute As Func(Of Object, Boolean)
    
        ''' <summary>
        ''' Initializes a new instance of the <see cref="DelegateCommand"/> class.
        ''' </summary>
        ''' <param name="executeAction">The execute action.</param>
        ''' <param name="canExecute">Whether it can execute.</param>
        Public Sub New(ByVal executeAction As Action(Of Object), ByVal canExecute As Func(Of Object, Boolean))
    
            _executeAction = executeAction
            _canExecute = canExecute
    
        End Sub
    
        ''' <summary>
        ''' Determines whether the command can execute in its current state.
        ''' </summary>
        ''' <param name="parameter">Data used by the command.</param>
        ''' <returns>True if the command can be executed.</returns>
        Public Function CanExecute(ByVal parameter As Object) As Boolean _
            Implements ICommand.CanExecute
    
            Dim temp = _canExecute(parameter)
            If _canExecuteCache <> temp Then
                _canExecuteCache = temp
                RaiseEvent CanExecuteChanged(Me, New EventArgs)
            End If
            Return _canExecuteCache
    
        End Function
    
        ''' <summary>
        ''' Occurs when changes happen that affects whether the command should execute.
        ''' </summary>
        Public Event CanExecuteChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
            Implements ICommand.CanExecuteChanged
    
        ''' <summary>
        ''' Defines method to call when the command is invoked.
        ''' </summary>
        ''' <param name="parameter">Data used by the command.</param>
        Public Sub Execute(ByVal parameter As Object) _
            Implements ICommand.Execute
    
            _executeAction(parameter)
    
        End Sub
    End Class
    Here are the entities I'm working with in the ViewModel:

    Code:
            Public ReadOnly Property Collateral As EntitySet(Of Collateral)
                Get
                    Return ctx.Collaterals
                End Get
            End Property
    
            Private _selectedCollateral As Collateral
            Public Property SelectedCollateral As Collateral
                Get
                    Return _selectedCollateral
                End Get
                Set(ByVal value As Collateral)
                    _selectedCollateral = value
                    RaisePropertyChanged("SelectedCollateral")
                End Set
            End Property
    Initialization code for my DelegateCommand:

    Code:
    CollateralSelectionChanged = New DelegateCommand(AddressOf OnCollateralSelectionChangedExecute, AddressOf CanChangeCollateralSelection
    Code:
            Public Property DeleteCollateralCommand As DelegateCommand
            Private Sub OnDeleteCollateralExecute(ByVal param As Object)
                DirectCast(param, EntitySet(Of Collateral)).Remove(SelectedCollateral)
                RaisePropertyChanged("Collateral")
            End Sub
            Private Function CanDeleteCollateral() As Boolean
                Return SelectedCollateral IsNot Nothing
            End Function
    From everything I've read this should handle the IsEnabled property on the button.

    To get around this I've created a new property which I'm raising whenever SelectedCollateral is set and then manually bound it to the IsEnabled property on the button.

    Code:
                        <Button Content="Remove Collateral" IsEnabled="{Binding Source={StaticResource vm}, Path=CanDeleteCollateralProp}">
                            <i:Interaction.Triggers>
                                <i:EventTrigger EventName="Click">
                                    <i:InvokeCommandAction Command="{Binding Source={StaticResource vm}, Path=DeleteCollateralCommand}" 
                                                       CommandParameter="{Binding DataContext, ElementName=dataGrid}"/>
                                </i:EventTrigger>
                            </i:Interaction.Triggers>
                        </Button>
    This seems hacky to me and shouldn't be necessary from what I've read about MVVM.

    If anyone could point out my error it would be appreciated. Thanks in advance.

  2. #2
    MattP is offline VB.NET Forum All-Mighty
    .NET Framework
    .NET 4.0
    Join Date
    Feb 2008
    Location
    WY, USA
    Posts
    1,206
    Reputation
    618
    Added these 2 methods to the DelegateCommand class:

    Code:
        Public Sub RaiseCanExecuteChanged()
            OnCanExecuteChanged(EventArgs.Empty)
        End Sub
    
        Protected Overridable Sub OnCanExecuteChanged(ByVal e As EventArgs)
            RaiseEvent CanExecuteChanged(Me, e)
        End Sub
    From there I can call RaiseCanExecuteChanged on whatever DelegateCommand I want to have updated.

    Code:
            Private _selectedCollateral As Collateral
            Public Property SelectedCollateral As Collateral
                Get
                    Return _selectedCollateral
                End Get
                Set(ByVal value As Collateral)
                    _selectedCollateral = value
                    RaisePropertyChanged("SelectedCollateral")
                    DeleteCollateralCommand.RaiseCanExecuteChanged()
                End Set
            End Property

Tags for this Thread

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •