MVVM – f#async / await in view model
My ViewModel in F #
I'm trying to use f# instead of c# to implement my ViewModel I'm following this article (by the way, do you have any new or better suggestions?)
So, suppose I have my view model library implementation (MVVM. ViewModel, which is in C #, but I can refer to it from F #) and a simple status attribute
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)
Everything worked as expected and has been good so far (but if you find any conceptual errors, or if you find a more common version of the above code, please let me know)
Introduce async / await mode
This is where I went wrong: I know how to do this in C #, but I'm not good at f #
I've tried the following
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")
The problem is - when I run a full WPF application - I don't see the expected delay: the final state is output directly to the output
If I put the above async Change startimmediate to async Runsynchronously (task), of course I see the ongoing delay, but the application is frozen, so this is not what I want
If I rearrange them as
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)
I received an error
Editing (Continued)
Finally, I tried this
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."))
However, argumentexception occurs when the application crashes
stack trace
Edit 2 – problems found
I have to use the following - more simply - the overload of onpropertychanged (they are implemented according to this source code and work in c#)
member this.Status with get() = status and set(value) = status <- value base.OnPropertyChanged("Status")
Solution
The reason for the exception is that f#'s function cannot be expressed as 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); }
In the debugger, you will see the exception actually obtained - "the body must be a member expression"
Your first code:
member this.RunSetStatus() = status <- "Reset @" + DateTime.Now.ToString "yy.MM.dd hh:mm:ss" base.OnPropertyChanged("Status")
Because you do not use the property setter state, it works normally
So you need to use different overloads