视图模型

时间:2016-10-27 11:23:02

标签: mvvm f# async-await fsharp.viewmodule

我在F#中的ViewModel

我正在尝试使用F#而不是C#来实现我的ViewModel。 我正在关注article(顺便问一下,有什么新的或更好的建议吗?)。

所以让我说我有我的视图模型库实现(MVVM.ViewModel,它在C#中,但我可以从F#引用它)和一个简单的Status属性。

namespace FuncViewModel
open MVVM.ViewModel
open System

    type MyFuncViewModel() = 
        inherit ViewModelBase()

        let mutable status=""

        member this.RunSetStatus() =
            status <- "Reset @" + DateTime.Now.ToString "yy.MM.dd hh:mm:ss"
            base.OnPropertyChanged("Status")

        member this.SetStatus = new DelegateCommand(fun _ -> this.RunSetStatus() )


    member this.Status 
        with get() =
            status
        and set(value) =
             status <- value
             base.OnPropertyChanged(fun () -> this.Status)

一切都按预期工作,到目前为止一直很好(但如果您发现任何概念错误,或者如果您发现上述代码的更惯用版本,请告诉我)

介绍async / await模式

这就是我出错的地方:我知道如何在C#中做到这一点,但我在F#中并不擅长。

我尝试了以下内容。

member this.RunSetStatus() =
    status <- "Start resetting @" + DateTime.Now.ToString "yy.MM.dd hh:mm:ss"
    base.OnPropertyChanged("Status")
    let task = async {

        do! Async.Sleep (30 * 1000) 

    }
    Async.StartImmediate(task)
    status <- "Reset done @" + DateTime.Now.ToString "yy.MM.dd hh:mm:ss"
    base.OnPropertyChanged("Status")

问题在于 - 当我运行完整的WPF应用程序时 - 我看不到预期的延迟:最终状态直接输出到输出。

如果我将上述Async.StartImmediate(task)更改为Async.RunSynchronously(task),我当然会看到进度延迟,但应用程序会被冻结,所以这不是我想要的。

如果我将其重新排列为

member this.RunSetStatus() =
    status <- "Start resetting @" + DateTime.Now.ToString "yy.MM.dd hh:mm:ss"
    base.OnPropertyChanged("Status")
    let task = async {

        do! Async.Sleep (30 * 1000) 

        status <- "Reset done @" + DateTime.Now.ToString "yy.MM.dd hh:mm:ss"
        base.OnPropertyChanged("Status")

    }
    Async.StartImmediate(task)

我收到错误

  

成员或对象构造函数'OnPropertyChanged'不是   无障碍。私人会员只能从内部访问   声明类型。受保护的成员只能从一个访问   扩展类型,无法从内部lambda表达式访问。

编辑(续)

最后,我也试过了这个

member this.RunSetStatus() =
    status <- "Start resetting @" + DateTime.Now.ToString "yy.MM.dd hh:mm:ss"
    base.OnPropertyChanged("Status")
    let task = async {

        do! Async.Sleep (30 * 1000) 

    }
    Async.StartWithContinuations(task, 
        (fun _ -> this.Status <- "Reset done @" + DateTime.Now.ToString "yy.MM.dd hh:mm:ss"),
        (fun _ -> this.Status <- "Operation failed."),
        (fun _ -> this.Status <- "Operation canceled."))

但应用程序崩溃时出现ArgumentException

堆栈跟踪

  

应用程序:MyFuncWPF.exe Framework版本:v4.0.30319描述:   由于未处理的异常,该过程终止。例外   信息:System.ArgumentException Stack:at   MVVM.ViewModel.ViewModelBase.OnPropertyChanged [系统.__佳能,   mscorlib,版本= 4.0.0.0,文化=中性,   公钥= b77a5c561934e089]   在FuncViewModel.MyFuncViewModel.set_Status(System.String)at   。$ MyVMLib + RunSetStatus @ 20.Invoke(Microsoft.FSharp.Core.Unit)   在   Microsoft.FSharp.Control.CancellationTokenOps+StartWithContinuations@1274 [系统.__佳能,   mscorlib,版本= 4.0.0.0,文化=中性,   PublicKeyToken = b77a5c561934e089]]。调用(System .__ Canon)at   。$ @ Control.loop 430-52(Microsoft.FSharp.Control.Trampoline,   Microsoft.FSharp.Core.FSharpFunc 2<Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Control.FakeUnitValue>) at Microsoft.FSharp.Control.Trampoline.ExecuteAction(Microsoft.FSharp.Core.FSharpFunc 2)   at。$ Control + -ctor @ 507.Invoke(System.Object)   在   System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate,   System.Object的

编辑2 - 发现问题

我必须使用以下内容 - 更简单 - OnPropertyChanged的重载(它们都是按照source code实现并在C#中工作)

    member this.Status 
        with get() =
            status
        and set(value) =
             status <- value
             base.OnPropertyChanged("Status")

1 个答案:

答案 0 :(得分:0)

异常的原因是F#的功能无法表示为MemberExpression

    protected virtual void OnPropertyChanged<T>(Expression<Func<T>> selectorExpression)
    {
        if (selectorExpression == null)
            throw new ArgumentNullException("selectorExpression");
        MemberExpression body = selectorExpression.Body as MemberExpression;
        if (body == null)
            throw new ArgumentException("The body must be a member expression");
        OnPropertyChanged(body.Member.Name);
    }

在调试器中,您会看到实际获得的异常 - “正文必须是成员表达式”。

您的第一个代码:

member this.RunSetStatus() =
    status <- "Reset @" + DateTime.Now.ToString "yy.MM.dd hh:mm:ss"
    base.OnPropertyChanged("Status")

有效,因为您没有使用属性Status的设置器。

所以你需要使用不同的重载。