Skip to content

Releases: fsprojects/Avalonia.FuncUI

1.0.0 RC 1.1.0

20 Jun 18:48
9647d23
Compare
Choose a tag to compare

What's Changed

New Contributors

Full Changelog: 0.6.0-preview8...1.0.0-RC-1.1.0

0.6.0-preview8

28 Feb 14:15
2631f2d
Compare
Choose a tag to compare

What's Changed

  • added StaticComponent for creating XAML like components with bindings
  • added Control.init to run code once after control instantiation
  • updated bindings

Full Changelog: 0.6.0-preview7...0.6.0-preview8

0.6.0-preview7

26 Jan 15:56
ec9bcbc
Compare
Choose a tag to compare
0.6.0-preview7 Pre-release
Pre-release

This pre-release contains:

A new overload added to useElmish hook

  • initArg no longer needs to be specified when initArg is unit.

So this:

let model, dispatch = ctx.useElmish(init, update, ())

Can also be written as this:

let model, dispatch = ctx.useElmish(init, update)

A performance optimization

0.6.0-preview6

22 Jan 21:18
7bd1ff0
Compare
Choose a tag to compare
0.6.0-preview6 Pre-release
Pre-release

This pre-release version includes:

  • Upgraded to Elmish v4
  • Completely reworked useElmish hook

Upgraded to Elmish v4 (Breaking)

1) Breaking Changes to UI Thread Sync

  • Program.withSyncDispatch has been removed in Elmish v4 because the platform specific Program.runWith___ functions should instead be provided by the library authors.

Additional info
the purpose of the "sync dispatch" is to ensure that UI changes coming from non-UI threads can be properly synchronized via the underlying platform specific dispatcher to avoid race conditions.
Previously, FuncUI automatically configured this within the Program.withHost function, on behalf of the user, to guard against race conditions that could occur if they called dispatch from an async handler.

Avalonia.FuncUI upgrade path
A new Program.runWithAvaloniaSyncDispatch function has been added that conforms to the new expectation that this should be handled by the library. If you are calling dispatch from within async blocks, you will need to use this instead of Program.run. Otherwise, you will get exceptions, which will force you to use this new function.

Old Elmish v3 Sample with async blocks

Program.mkProgram init update view
|> Program.withHost this
|> Program.run

New Elmish v4 Sample with async blocks

Program.mkProgram init update view
|> Program.withHost this
|> Program.runWithAvaloniaSyncDispatch ()

2) Breaking Changes to Subscriptions

  • Cmd.ofSub has been removed
  • Program.withSubscription now takes in the state/model, and returns a list of named subscriptions.
  • Subscription functions must now return IDisposable so they can be automatically disposed

The above changes will cause breakage in any FuncUI apps that make use of Program.withSubscription.

Avalonia.FuncUI upgrade path
Any FuncUI app that was previously using Program.withSubscription will have to resolve a build error since the function inputs/outputs have changed.

Old Elmish v3 Sample with Multiple Subscriptions

        let timer (_state: Clock.State) =
            let sub (dispatch: Clock.Msg -> unit) =
                let invoke() =
                    DateTime.Now |> Clock.Msg.Tick |> dispatch
                    true
                    
                DispatcherTimer.Run(Func<bool>(invoke), TimeSpan.FromMilliseconds 1000.0) |> ignore
                
            Cmd.ofSub sub
        
        // Save state when host window is Closed
        let onClosedSub (state: Clock.State) = 
            Cmd.ofSub (fun _ -> 
                this.Closed.Subscribe(fun e -> 
                    printfn "Saving state: %A" state
                )
                |> ignore
            )

        Program.mkSimple (fun () -> Clock.init) Clock.update Clock.view
        |> Program.withHost this
        |> Program.withSubscription timer
        |> Program.withSubscription onClosedSub
        |> Program.withConsoleTrace
        |> Program.run

New Elmish v4 Sample with Multiple Subscriptions

        let subscriptions (_state: Clock.State) : Sub<Clock.Msg> =
            let timerSub (dispatch: Clock.Msg -> unit) =
                let invoke() =
                    DateTime.Now |> Clock.Msg.Tick |> dispatch
                    true
                    
                DispatcherTimer.Run(Func<bool>(invoke), TimeSpan.FromMilliseconds 1000.0) // <- IDisposable

            let onClosedSub (dispatch: Clock.Msg -> unit) =
                this.Closed.Subscribe(fun e -> printfn "The window has been closed.")

            // Notes: 
            // - Each subscription has a unique identifier
            // - Subscriptions can be conditionally add/removed from the list based on the passed in model
            // - Each subscription returns IDisposable, and is automatically disposed if conditionally removed from the list
            [ 
                [ nameof timerSub ], timerSub
                [ nameof onClosedSub ], onClosedSub
            ]
        
        Program.mkSimple Clock.init Clock.update Clock.view
        |> Program.withHost this
        |> Program.withSubscription subscriptions
        |> Program.withConsoleTrace
        |> Program.run

Completely reworked useElmish hook (Breaking)

useElmish now actually uses Elmish 4 under the hood! 🎉
This means that useElmish can now take advantage of all the great Elmish features like subscriptions and async commands!

It has two main variations of overloads:

  • Two that take init, update, initArg and an optional mapProgram (for adding subsriptions or other Elmish extensions)
  • Two that take writableModel, update and an optional mapProgram (for adding subsriptions or other Elmish extensions)

Here is an example from the ChordParserView sample that uses subscriptions:

let private subscriptions (model: Model) : Sub<Msg> =
    let timerSub (dispatch: Msg -> unit) =
        let invoke() = dispatch Msg.SetTime; true
        DispatcherTimer.Run(invoke, TimeSpan.FromMilliseconds 1000.0)

    [ 
        // Dynamically start or stop (Dispose) subscription
        if model.Transpose = 0 then 
            [ nameof timerSub ], timerSub
    ]

let view () = Component (fun ctx ->
    let model, dispatch = ctx.useElmish(init, update, (), Program.withSubscription subscriptions)
    //let model, dispatch = ctx.useElmish(init, update, ()) // if no subscriptions are needed
    
    Grid.create [

Thanks to @alfonsogcnunez for paving the way as I relied heavily on his Fable.React.UseElmish implementation!

0.6.0-preview3

25 Oct 23:52
a0ca5bd
Compare
Choose a tag to compare
0.6.0-preview3 Pre-release
Pre-release

This pre-release version includes:

  • createText functions in Run, Bold, Italic and Underline elements to easily create text-only inline elements with no other attributes.
  • MaskedTextBox now correctly accepts its attributes and not only TextBox attributes.

0.6.0-preview2

15 Oct 16:42
Compare
Choose a tag to compare
0.6.0-preview2 Pre-release
Pre-release

This pre-release version includes:

  • Bindings for RichTextBlock and all inline elements. You can take a look at an example using them here.
  • Support for v11 preview 2 of Avalonia.

0.6.0-preview1

09 Sep 12:12
Compare
Choose a tag to compare
0.6.0-preview1 Pre-release
Pre-release

This pre-release version includes:

  • Support for v11 preview 1 of Avalonia.

Avalonia.FuncUI v0.5

18 Mar 09:39
c2ec8e6
Compare
Choose a tag to compare

This release includes a bunch of new stuff:

0.5.0-beta

22 Dec 16:42
Compare
Choose a tag to compare
0.5.0-beta Pre-release
Pre-release
0.5.0-beta

0.4.0

22 Dec 16:41
56e67de
Compare
Choose a tag to compare
0.4.0