如何在ViewModel中使用DelegateCommand?

9

有没有F#编写的ViewModel中调用DelegateCommand的示例?

我在这里找到了一个DelegateCommand实现的示例代码(链接)

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)

然而,作为一名对F#不太熟悉的人,我不确定是否应该使用这个实现方式,或者是否有其他方法可以利用。

以下是一个测试,一旦我弄清如何执行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

我的视图模型如下:

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 @>)

我有以下基本视图模型:

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

更新:

我已经修改了F# MVVM项目模板中定义的RelayCommand,修改后如下:

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)

我接下来更新了我的视图模型如下所示:
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))

然而,我不确定如何在提交(DelegateCommand)上调用Execute函数:

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

我知道这很琐碎,但是我真的不知道怎么做。
有什么建议吗?

为什么不使用现成的解决方案?- FSharp.ViewModule - FoggyFinder
你有什么问题?你在哪里遇到了困难? - Mark Seemann
@ Mark Seemann - 我更新了我的问题状态。我现在卡在一个微不足道的任务上,即在我的测试中调用命令的Execute函数。 - Scott Nimrod
1
恭敬地说,我的问题是如何在ViewModel中使用DelegateCommand。我的问题的背景真的改变了吗? - Scott Nimrod
我的错。这个没问题。那天我回答的是另一个问题,我应该让你提出一个新问题。关于在我的视图中修复类型错误的部分确实是一个单独的问题。你很棒。 - Guy Coder
显示剩余2条评论
2个回答

3

提交的实现更改为以下内容:

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

并且这样调用它:

viewModel.Submit.Execute() |> ignore

重要的更改是Submit方法的返回类型变为ICommand,而不是DelegateCommand。这是由于使用了向上转型运算符:>
在F#中,所有接口都是显式实现的。这意味着虽然DelegateCommand实现了ICommand,但ICommand的方法在声明(或推断)为DelegateCommand的对象上不可见。另一方面,当值被声明(或推断)为ICommand类型时,只有ICommand的成员可用。

2

由于我没有使用F#的MVC或MVVM,因此无法给出具体的代码示例。

对于

然而,作为F#的新手,我不确定是否应该使用这种实现,还是应该使用其他方法。

如果您使用Visual Studio,它也有一个可以免费下载的社区版,其中包含MVC和MVVM的模板。

从VS中使用菜单访问模板。

文件 -> 新建 -> 项目

enter image description here

展开在线
展开模板
展开Visual F#

enter image description here

在右上角的“搜索在线模板”中输入诸如MVC或MVVM之类的术语

enter image description here

选择一个或检查几个。

注意:一些模板已经有几年历史了,F#和Windows也在这段时间里发展了很多,因此我倾向于避免使用旧的模板。此外,流行模板的作者通常可以在线找到,甚至有些人在这里,所以可以就具体问题提问。

您还可以使用Internet浏览器在Visual Studio Gallery中查看其他模板、控件和扩展

注意:一些扩展安装后会导致问题,特别是旧的或不太受欢迎的扩展,因此请小心,并且在生产机器上不要安装它们,直到您先在其他机器上测试它们。


感谢程序员Guy。你的贡献/指导在某种程度上回答了我的问题。我已经更新了我的问题状态,关于如何调用一个似乎没有暴露的函数。 - Scott Nimrod

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接