将视图模型从 C# 转换为 F#

Convert viewmodel from C# to F#

F# 中的以下 ViewModel 是什么样子的?

namespace MVVMExample
{
    public class ViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private string _firstName;    
        public string FirstName
        {
            get { return _firstName; }
            set
            {
                _firstName = value;
                RaisePropertyChanged("FirstName");
            }
        }

        private string _lastName;
        public string LastName
        {
            get { return _lastName; }
            set
            {
                _lastName = value;
                RaisePropertyChanged("LastName");
            }
        }

        public string GetFullName()
        {
            return string.Format("{0} {1}", FirstName, LastName);
        }

        protected void RaisePropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}

注意:

我是 F# 的新手,真的很想了解如何从 C# 简化代码。

我更喜欢使用基础 ViewModel 来继承和公开使用引号的接口,因此我不需要手动提供字符串 属性 名称(这对代码不友好重构)。

请注意,根据已接受的问题答案:Is it possible to use CallerMemberNameAttribute in f# F# 不支持 [<CallerMemberName>] 属性,因此我使用引号来执行此操作。

如果您在 C# 中定义基础 ViewModel class 并从 F# 中的 class 继承,您仍然可以获得 [<CallerMemberName>] 的好处。不过,出于此答案的目的,我将向您展示一个纯 F# 解决方案。

type ViewModel () =
    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

然后从中得出您的特定 ViewModel

type NimrodViewModel() =
    inherit ViewModel()
    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 @>)

    member this.GetFullName() = 
        sprintf "%s %s" (this.FirstName) (this.LastName)

使用FSharp.Desktop.UI,它看起来像这样:

[<AbstractClass>]
type ViewModel() = 
    inherit Model()

    abstract FirstName: string with get, set
    abstract LastName: string with get, set

    [<DerivedProperty>]
    member this.FullName = sprintf "%s %s" this.FirstName this.LastName

然后 let model: MyViewModel = ViewModel.Create() 将创建 ViewModel 的实例。

通过使 FullName 成为 属性 并用 DerivedProperty 属性装饰它,任何绑定到它的控件都会在它依赖的任何属性被更改时自动获得更改通知改变了。

我知道您没有要求使用库的示例,但我想展示在使用正确的社区库时 F# 可以多么简洁。