简体   繁体   中英

Implementing RelayCommand (MVVM) in VB.NET: Syntax problems

For a project I have to use VB.NET. I am going to use MVVM, so I have to implement a RelayCommand class.

In C#, the class looks like this:

class ActionCommand : ICommand
{
    private Action<object> execute;
    private Predicate<object> canExecute;
    private event EventHandler CanExecuteChangedInternal;

    public ActionCommand(Action<object> execute) : this(execute, DefaultCanExecute) { }

    public ActionCommand(Action<object> exec, Predicate<object> canExec)
    {
        if (exec == null)
            throw new ArgumentNullException("execute");
        if (canExec == null)
            throw new ArgumentNullException("canExecute");
        execute = exec;
        canExecute = canExec;
    }

    public event EventHandler CanExecuteChanged
    {
        add { CanExecuteChangedInternal += value; }
        remove { CanExecuteChangedInternal -= value; }
    }

    public bool CanExecute(object param)
    {
        return canExecute != null && canExecute(param);
    }

    public void Execute(object param)
    {
        execute(param);
    }

    public void OnCanExecuteChanged()
    {
        EventHandler handler = CanExecuteChangedInternal;
        if (handler != null)
            handler.Invoke(this, EventArgs.Empty);
    }

    public void Destroy()
    {
        canExecute = _ => false;
        this.execute = _ => { return; };
    }

    private static bool DefaultCanExecute(object param)
    {
        return true;
    }  
}

My version in VB.NET looks like this:

Public Class RelayCommand : Implements ICommand
Dim _execute As Action(Of Object)
Dim _canExecute As Predicate(Of Object)
Dim canExecuteChangedInternal As EventHandler

Sub New(ByVal execute)
    Me.New(execute, DefaultCanExecute)
End Sub

Sub New(ByVal execute As Action(Of Object), ByVal canExec As Predicate(Of Object))
    If execute = Nothing Then
        Throw New ArgumentException("execute")
    End If
    If canExec = Nothing Then
        Throw New ArgumentException("canExec")
    End If
    _execute = execute
    _canExecute = canExec
End Sub

Public Function CanExecute(ByRef param As Object) As Boolean
    Return _canExecute <> Nothing And _canExecute(param)
End Function


Public Sub Execute(ByVal obj As Object)
    _execute(obj)
End Sub

Public Custom Event CanExecuteChanged As EventHandler
    AddHandler(ByVal value As EventHandler)
        AddHandler canExecuteChangedInternal, AddressOf value
    End AddHandler
    RemoveHandler(value As EventHandler)
        RemoveHandler canExecuteChangedInternal, AddressOf value
    End RemoveHandler
    RaiseEvent(sender As Object, e As EventArgs)

    End RaiseEvent
End Event

Public Sub OnCanExecuteChanged(ByVal param As Object)
    Dim handler As EventHandler = canExecuteChangedInternal
    If handler <> Nothing Then
        handler.Invoke(Me, EventArgs.Empty)
    End If
End Sub

Public Sub Destroy()
    _canExecute = Function(param As Object)
                      Return False
                  End Function
    Me._execute = Sub()
                      Return
                  End Sub

End Sub

Private Shared Function DefaultCanExecute(ByVal param As Object) As Boolean
    Return True
End Function

End Class

Now I have the following problems in VB:

  • In my constructor DefaultCanExecute is not accepted as a parameter for the other constructor, but in C# it works fine.
  • Adding and removing the EventHandlers does not work. The error message is: "value is not a name of a method". But it's an event handler, isn't it? The C# version works here, too.

Thanks to Dave, I was able to get everything sorted out. If anyone is interested in the final version of the class, here it is:

Public Class RelayCommand : Implements ICommand
Private _execute As Action(Of Object)
Private _canExecute As Predicate(Of Object)
Private Event canExecuteChangedInternal As EventHandler

Public Sub New(ByVal execute As Object)
    Me.New(execute, AddressOf DefaultCanExecute)
End Sub

Public Sub New(ByVal execute As Action(Of Object), ByVal canExec As Predicate(Of Object))
    If execute Is Nothing Then
        Throw New ArgumentException("execute")
    End If
    If canExec Is Nothing Then
        Throw New ArgumentException("canExec")
    End If
    _execute = execute
    _canExecute = canExec
End Sub

Public Function CanExecute(ByVal param As Object) As Boolean Implements ICommand.CanExecute
    Return _canExecute IsNot Nothing AndAlso _canExecute(param)
End Function


Public Sub Execute(ByVal obj As Object) Implements ICommand.Execute
    _execute(obj)
End Sub

Public Custom Event CanExecuteChanged As EventHandler Implements ICommand.CanExecuteChanged
    AddHandler(ByVal value As EventHandler)
        AddHandler canExecuteChangedInternal, value
    End AddHandler
    RemoveHandler(value As EventHandler)
        RemoveHandler canExecuteChangedInternal, value
    End RemoveHandler
    RaiseEvent(ByVal sender As Object, ByVal e As EventArgs)
        RaiseEvent canExecuteChangedInternal(sender, e)
    End RaiseEvent
End Event

Public Sub OnCanExecuteChanged(ByVal param As Object)
    Dim handler As EventHandler = canExecuteChangedInternalEvent
    If handler IsNot Nothing Then
        handler.Invoke(Me, EventArgs.Empty)
    End If
End Sub

Public Sub Destroy()
    _canExecute = Function(unused) False
    Me._execute = Sub(unused)
                      Return
                  End Sub
End Sub

Private Shared Function DefaultCanExecute(ByVal param As Object) As Boolean
    Return True
End Function

End Class

There are a number of things that are odd about your conversion: 1. You need to use 'Is' and 'IsNot' for reference types. 2. You omitted the type for your first constructor. 3. You omitted a parameter for the second lambda in the 'Destroy' method.

Try the following:

Friend Class ActionCommand
    Implements ICommand

    Private _execute As Action(Of Object)
    Private _canExecute As Predicate(Of Object)
    Private Event CanExecuteChangedInternal As EventHandler

    Public Sub New(ByVal execute As Action(Of Object))
        Me.New(execute, AddressOf DefaultCanExecute)
    End Sub

    Public Sub New(ByVal exec As Action(Of Object), ByVal canExec As Predicate(Of Object))
        If exec Is Nothing Then
            Throw New ArgumentNullException("execute")
        End If
        If canExec Is Nothing Then
            Throw New ArgumentNullException("canExecute")
        End If
        _execute = exec
        _canExecute = canExec
    End Sub

    Public Custom Event CanExecuteChanged As EventHandler
        AddHandler(ByVal value As EventHandler)
            AddHandler CanExecuteChangedInternal, value
        End AddHandler
        RemoveHandler(ByVal value As EventHandler)
            RemoveHandler CanExecuteChangedInternal, value
        End RemoveHandler
        RaiseEvent(ByVal sender As System.Object, ByVal e As System.EventArgs)
            RaiseEvent CanExecuteChangedInternal(sender, e)
        End RaiseEvent
    End Event

    Public Function CanExecute(ByVal param As Object) As Boolean
        Return _canExecute IsNot Nothing AndAlso canExecute(param)
    End Function

    Public Sub Execute(ByVal param As Object)
        execute(param)
    End Sub

    Public Sub OnCanExecuteChanged()
        'referencing the hidden "Event" field of the CanExecuteChangedInternal event:
        Dim handler As EventHandler = CanExecuteChangedInternalEvent
        If handler IsNot Nothing Then
            handler.Invoke(Me, EventArgs.Empty)
        End If
    End Sub

    Public Sub Destroy()
        _canExecute = Function(underscore) False
        Me._execute = Sub(underscore)
            Return
        End Sub
    End Sub

    Private Shared Function DefaultCanExecute(ByVal param As Object) As Boolean
        Return True
    End Function
End Class

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM