5

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

type Person(name: string) =
    member val Name = name
    override x.ToString() = name

还有一个视图模型,可选择设置所选人员。

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

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

如果我这样做...

<StackPanel>
    <ListBox ItemsSource="{Binding People}"
             SelectedItem="{Binding SelectedPerson}"
             DisplayMemberPath="Name" />
    <TextBlock Text="{Binding SelectedPerson}" />
</StackPanel>

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

4

2 回答 2

10

我目前采用的方法是编写自己的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 |])

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

<StackPanel>
    <ListBox ItemsSource="{Binding People}"
             SelectedItem="{Binding SelectedPerson, Converter={StaticResource OptionsTypeConverter1}}"
             DisplayMemberPath="Name" />
    <TextBlock Text="{Binding SelectedPerson, Converter={StaticResource OptionsTypeConverter1}}" />
</StackPanel>

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

于 2013-07-09T19:38:14.573 回答
3

我认为使用 anIValueConverter可能是最干净的方法。

或者,您可以使用选项类型具有成员的事实Value(返回值Some或抛出异常None)。所以,如果你知道总是有一个值,或者如果异常没有破坏任何东西(我没有尝试过),你也许可以这样写:

<ListBox ItemsSource="{Binding People}"
         SelectedItem="{Binding SelectedPerson.Value}"
         DisplayMemberPath="Name" />

如果异常是一个问题,那么这个过去的 SO 问题建议使用PriorityBinding

<ListBox ItemsSource="{Binding People}" DisplayMemberPath="Name">
  <ListBox.SelectedItem>
    <PriorityBinding>
        <Binding Path="SelectedPerson.Value" />
        <Binding Source="{x:Null}" />  <!-- or some other default... -->
    </PriorityBinding>
  </ListBox.SelectedItem>
</ListBox>
于 2013-07-09T20:04:04.957 回答