Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use the DelegateCommand on a viewmodel?

Tags:

f#

Is there any example on how to invoke a DelegateCommand on a viewmodel written in F#?

I have found the following code for a DelegateCommand implementation here:

type DelegateCommand<a>(execute : 'a -> unit, ?canExecute : 'a  ->  bool) =
  let event = Event<_,_>()
  interface ICommand with
    member this.CanExecute(param : obj) =
      if canExecute.IsSome then
        canExecute.Value(param :> 'a)
      else true
    member this.Execute (param : obj) =
      execute(param :> 'a)

However, being new to F#, I'm not sure if I should use this implementation or if there is some other approach I should be leveraging.

Here's a test that I would like to execute once I figure out how to execute a DelegateCommand:

module CreateProfileViewModel.Tests

open FsUnit
open NUnit.Framework
open CreateProfile.UILogic

[<Test>]
let ``create profile`` () =

    // Setup
    let viewModel = ViewModel()
    viewModel.FirstName <- "Scott"
    viewModel.LastName <- "Nimrod"

    // Test
    viewModel.Submit.Execute();

    // Verify
    let expected = false // TODO - implement some state-change
    expected |> should equal true

My viewmodel is the following:

module CreateProfile

open Core.UILogic

type ViewModel() =
    inherit ViewModelBase()
    let mutable firstName = ""
    let mutable lastName = ""

    member this.FirstName
        with get() = firstName 
        and set(value) =
            firstName <- value
            base.NotifyPropertyChanged(<@ this.FirstName @>)

    member this.LastName
        with get() = lastName 
        and set(value) =
            lastName <- value
            base.NotifyPropertyChanged(<@ this.LastName @>)

I have the following base-viewmodel:

module Core.UILogic

open System.ComponentModel
open Microsoft.FSharp.Quotations.Patterns

type ViewModelBase () =
    let propertyChanged = 
        Event<PropertyChangedEventHandler,PropertyChangedEventArgs>()

    let getPropertyName = function 
        | PropertyGet(_,pi,_) -> pi.Name
        | _ -> invalidOp "Expecting property getter expression"

    interface INotifyPropertyChanged with
        [<CLIEvent>]
        member this.PropertyChanged = propertyChanged.Publish

    member private this.NotifyPropertyChanged propertyName = 
        propertyChanged.Trigger(this,PropertyChangedEventArgs(propertyName))

    member this.NotifyPropertyChanged quotation = 
        quotation |> getPropertyName |> this.NotifyPropertyChanged

Update:

I have modified the RelayCommand defined in the MVVM project template for F# to the following:

module UILogic.Interaction

open System
open System.Windows
open System.Windows.Input
open System.ComponentModel

type DelegateCommand (action:(obj -> unit), canExecute:(obj -> bool)) =
    let event = new DelegateEvent<EventHandler>()
    interface ICommand with
        [<CLIEvent>]
        member this.CanExecuteChanged = event.Publish
        member this.CanExecute arg = canExecute(arg)
        member this.Execute arg = action(arg)

I then updated my viewmodel as follows:

module CreateProfile.UILogic

open UILogic.State
open UILogic.Interaction

type ViewModel() =
    inherit ViewModelBase()

    let mutable _firstName = ""
    let mutable _lastName = ""
    let mutable _submitted = false

    member this.FirstName
        with get() = _firstName 
        and set(value) =
            _firstName <- value
            base.NotifyPropertyChanged(<@ this.FirstName @>)

    member this.LastName
        with get() = _lastName 
        and set(value) =
            _lastName <- value
            base.NotifyPropertyChanged(<@ this.LastName @>)

    member this.IsSubmitted
        with get() = _submitted
        and set(value) = _submitted <- value

    member this.Submit =
        new DelegateCommand ((fun _ -> this.IsSubmitted <- true), (fun _ -> true))

However, I am not sure how to invoke the Execute function on the Submit (DelegateCommand):

module CreateProfileViewModel.Tests

open FsUnit
open NUnit.Framework
open UILogic.State
open CreateProfile.UILogic

[<Test>]
let ``create profile`` () =

    // Setup
    let viewModel = ViewModel()
    viewModel.FirstName <- "Scott"
    viewModel.LastName <- "Nimrod"

    // Test
    ignore viewModel.Submit.Execute(); // How to invoke execute?

    // Verify
    viewModel.IsSubmitted |> should equal true

I know this is trivial, but I honestly have no clue how to do this.

Any suggestions?

like image 804
Scott Nimrod Avatar asked Jan 09 '16 12:01

Scott Nimrod


People also ask

How do you use DelegateCommand?

A delegate command calls methods (delegates) that you assigned to the command when the command's Execute and CanExecute logic is invoked. The following delegate commands are available: DelegateCommand<T> - Specifies a command whose Execute and CanExecute delegates accept a single parameter of type T.

What is DelegateCommand?

DelegateCommand(Action<Object>) Initializes a new instance of the DelegateCommand class. Using this constructor to initialize the DelegateCommand results in a command that can always execute. DelegateCommand(Action<Object>, Predicate<Object>) Initializes a new instance of the DelegateCommand class.

What is ICommand C#?

The ICommand interface is the code contract for commands that are written in . NET for Windows Runtime apps. These commands provide the commanding behavior for UI elements such as a Windows Runtime XAML Button and in particular an AppBarButton .

What is RelayCommand WPF?

The RelayCommand and RelayCommand<T> are ICommand implementations that can expose a method or delegate to the view. These types act as a way to bind commands between the viewmodel and UI elements.


2 Answers

Change the Submit implementation to this:

member this.Submit =
    DelegateCommand ((fun _ -> this.IsSubmitted <- true), (fun _ -> true)) :> ICommand

and call it like this:

viewModel.Submit.Execute() |> ignore

The important change is that the Submit method's return type is now ICommand instead of DelegateCommand. This is due to the use of the upcast operator :>.

In F#, all interfaces are explicitly implemented. This means that although DelegateCommand implements ICommand, the methods of ICommand aren't visible on objects declared (or inferred) as DelegateCommand. When the value is declared (or inferred) to be of the type ICommand, on the other hand, only the members of ICommand are available.

like image 144
Mark Seemann Avatar answered Oct 03 '22 05:10

Mark Seemann


Since I have not used MVC or MVVM with F# I cannot give a specific code example.

For

However, being new to F#, I'm not sure if I should use this implementation or if there is some other approach I should be leveraging.

If you use Visual Studio, which also has a community edition that can be download for free, there are templates for MVC and MVVM.

To access the templates from VS using the menu.

File -> New -> Project

enter image description here

Expand Online
Expand Templates
Expand Visual F#

enter image description here

In the upper right for "Search Online Templates" enter a term such as MVC or MVVM

enter image description here

Pick one or examine a few.

Note: Some of the templates are a few years old and F# and Windows has progressed much along the way so I would tend to avoid the older ones. Also the authors of the popular ones can typically be found online and even some are here so ask questions about specifics.

You can also view other templates, controls and extensions to VS at the Visual Studio Gallery using an Internet browser.

Caution: Some of the extensions will cause problems when installed, especially the older or less popular ones, so be careful and don't install them on a production machine until you test them on a different machine first.

like image 45
Guy Coder Avatar answered Oct 03 '22 06:10

Guy Coder