假设我有一个业务对象(没有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]'
答案 0 :(得分: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 |])
然后我的XAML看起来像这样:
<StackPanel>
<ListBox ItemsSource="{Binding People}"
SelectedItem="{Binding SelectedPerson, Converter={StaticResource OptionsTypeConverter1}}"
DisplayMemberPath="Name" />
<TextBlock Text="{Binding SelectedPerson, Converter={StaticResource OptionsTypeConverter1}}" />
</StackPanel>
可能有一种更简单的方法。该转换器可能已经存在于框架中。可能有更好的实现。
答案 1 :(得分:3)
我认为使用IValueConverter
可能是最干净的方法。
或者,您可以使用选项类型具有Value
成员(返回Some
的值或抛出None
的异常)这一事实。所以,如果你知道总有一个值,或者异常没有破坏任何东西(我还没有尝试过),你可能会写:
<ListBox ItemsSource="{Binding People}"
SelectedItem="{Binding SelectedPerson.Value}"
DisplayMemberPath="Name" />
如果异常有问题,则this past SO question suggests使用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>