Associate a WPF Control with the F # Option

Suppose I have a business object (without AllowNullLiteralAttribute ).

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

And a presentation model, with a selected person, possibly installed.

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

What is the best way to bind a WPF SelectedItem control property to an F # property property (without using AllowNullLiteralAttribute )?

If I do this ...

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

... this leads to an error. Cannot convert "George" from type "Man" to type "Microsoft.FSharp.Core.FSharpOption`1 [Person]"

+4
source share
2 answers

The approach I'm taking right now is to write my own 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 |]) 

And then my XAML looks like this:

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

Maybe easier. This converter may already exist within the framework. There may be better implementations.

+8
source

I think using IValueConverter is probably the cleanest approach.

Alternatively, you can use the fact that the parameter type has a Value member (which returns a value for Some or throws an exception for None ). So, if you know that there is always a value, or if the exception does not break anything (I have not tried), you could write:

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

If exception is the problem, then in this previous SO question, it is suggested to use 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> 
+3
source

Source: https://habr.com/ru/post/1490554/


All Articles