将WPF的控件绑定到F#选项

Wal*_*lly 4 data-binding wpf f#

假设我有一个业务对象(没有AllowNullLiteralAttribute).

type Person(name: string) =
    member val Name = name
    override x.ToString() = name
Run Code Online (Sandbox Code Playgroud)

和视图模型,可选择设置所选人员.

type MainWindowModel() =

    let mutable selectedPerson: Person option = None
        :
    member val People = ObservableCollection<Person>()
    member x.SelectedPerson
        with get() = selectedPerson
        and set(v) =
            if selectedPerson <> v then
                selectedPerson <- v
                x.RaisePropertyChanged("SelectedPerson")
Run Code Online (Sandbox Code Playgroud)

将WPF控件的SelectedItem属性绑定到F#选项属性(不使用AllowNullLiteralAttribute)的最佳方法是什么?

如果我这样做......

<StackPanel>
    <ListBox ItemsSource="{Binding People}"
             SelectedItem="{Binding SelectedPerson}"
             DisplayMemberPath="Name" />
    <TextBlock Text="{Binding SelectedPerson}" />
</StackPanel>
Run Code Online (Sandbox Code Playgroud)

...导致错误,无法将'George'从类型'Person'转换为'Microsoft.FSharp.Core.FSharpOption`1 [Person]'

Wal*_*lly 8

我目前采取的方法是写我自己的方法IValueConverter.

open System
open System.Globalization
open System.Windows.Data

type OptionsTypeConverter() =

    // from http://stackoverflow.com/questions/6289761
    let (|SomeObj|_|) =
      let ty = typedefof<option<_>>
      fun (a:obj) ->
        let aty = a.GetType()
        let v = aty.GetProperty("Value")
        if aty.IsGenericType && aty.GetGenericTypeDefinition() = ty then
          if a = null then None
          else Some(v.GetValue(a, [| |]))
        else None

    interface IValueConverter with

        member x.Convert(value: obj, targetType: Type, parameter: obj, culture: CultureInfo) =
            match value with
            | null -> null
            | SomeObj(v) -> v
            | _ -> value

        member x.ConvertBack(value: obj, targetType: Type, parameter: obj, culture: CultureInfo) =
            match value with
            | null -> None :> obj
            | x -> Activator.CreateInstance(targetType, [| x |])
Run Code Online (Sandbox Code Playgroud)

然后我的XAML看起来像这样:

<StackPanel>
    <ListBox ItemsSource="{Binding People}"
             SelectedItem="{Binding SelectedPerson, Converter={StaticResource OptionsTypeConverter1}}"
             DisplayMemberPath="Name" />
    <TextBlock Text="{Binding SelectedPerson, Converter={StaticResource OptionsTypeConverter1}}" />
</StackPanel>
Run Code Online (Sandbox Code Playgroud)

可能有一种更简单的方法.该转换器可能已经存在于框架中.那里可能有更好的实现.