Creating bindings for Avalonia controls is fairly easy. You just need to know a little bit about the source of the control and its public properties/events.
You can create bindings for any public styled/direct properties for example the IsPressedProperty of Button.cs
In the case of an event, the event should be a RoutedEvent for example the ClickEvent of Button.cs
/// <summary>/// Defines the <seecref="Click"/> event./// </summary>publicstaticreadonlyRoutedEvent<RoutedEventArgs> ClickEvent =RoutedEvent.Register<Button,RoutedEventArgs>(nameof(Click),RoutingStrategies.Bubble);
That's all we need to know before we start creating a binding. To create bindings like Avalonia.FuncUI ones, we need to define a module with the name of the control and add two things, a create function and augment the existing control with some static methods.
Let's check Button.fs in Avalonia.FuncUI.source code
[<AutoOpen>]module Button =(* omitting the other existing open statements for clarity *)open Avalonia.Controlsopen Avalonia.FuncUI.Builderopen Avalonia.FuncUI.Types(* omitting the other existing open statements for clarity *)let create(attrs:IAttr<Button> list):IView<Button>= ViewBuilder.Create<Button>(attrs)typeButtonwith(* omitting the other existing bindings for clarity *)static member isPressed<'twhen't:>Button>(value:bool):IAttr<'t>= AttrBuilder<'t>.CreateProperty<bool>(Button.IsPressedProperty, value, ValueNone)static member onClick<'twhen't:>Button>(func:RoutedEventArgs ->unit,?subPatchOptions)= AttrBuilder<'t>.CreateSubscription<RoutedEventArgs>(Button.ClickEvent, func, ?subPatchOptions = subPatchOptions)(* omitting the other existing bindings for clarity *)
Please note that in the case of events, there is an optional value subPatchOptions that is provided for performance reasons
type[<Struct>]SubPatchOptions=/// Always updates the subscription. This should be used if you can't explicitly express your outer dependencies.| Always/// Never updates the subscription. This should be used most of the time. Use this if you don't depend on outer dependencies.| Never/// Update if 't changed. This is useful if you're using some state ('t) and need to update the subscription if that state changed.| OnChangeOf ofobj
this property will indicate to Avalonia.FuncUI when to update a subscription.
You can also provide overloaded methods to improve the API surface of a control for example in Textblock.fs we provide two background functions, one takes an IBrush and the other one takes a string.
[<AutoOpen>]module TextBlock =open Avaloniaopen Avalonia.Controlsopen Avalonia.Mediaopen Avalonia.Media.Immutableopen Avalonia.FuncUI.Builderopen Avalonia.FuncUI.Typeslet create (attrs:IAttr<TextBlock> list):IView<TextBlock>= ViewBuilder.Create<TextBlock>(attrs)typeTextBlockwith(* omitting other bindings for clarity *)static member background<'twhen't:>TextBlock>(value:IBrush):IAttr<'t>= AttrBuilder<'t>.CreateProperty<IBrush>(TextBlock.BackgroundProperty, value, ValueNone)static member background<'twhen't:>TextBlock>(color:string):IAttr<'t>= color |> Color.Parse |> ImmutableSolidColorBrush |> TextBlock.background(* omitting other bindings for clarity *)
If you wanted you could also add a Color overload to ease developer's time if it were the case
static member background<'twhen't:>TextBlock>(color:Color):IAttr<'t>= color |> ImmutableSolidColorBrush |> TextBlock.background