Skip to content

Instantly share code, notes, and snippets.

@mathias-brandewinder
Last active February 19, 2025 04:36
Show Gist options
  • Save mathias-brandewinder/991eab4598acae36f613af797a793302 to your computer and use it in GitHub Desktop.
Save mathias-brandewinder/991eab4598acae36f613af797a793302 to your computer and use it in GitHub Desktop.
Using Cmd.OfAsync in Avalonia FuncUI
// See corresponding blog post:
// https://brandewinder.com/2025/02/19/async-commands-in-avalonia-funcui/
module AsyncOperation =
open System
open Elmish
open Avalonia.Controls
open Avalonia.FuncUI.DSL
open Avalonia.FuncUI.Types
let respondToRequest (request: string) =
$"{DateTime.Now}: Request was {request}"
type State = {
Request: string
Response: string
}
type Msg =
| UpdateRequest of string
| SendRequest
let init (): State * Cmd<Msg> =
{
Request = ""
Response = ""
},
Cmd.none
let update (msg: Msg) (state: State): State * Cmd<Msg> =
match msg with
| UpdateRequest text ->
{ state with Request = text }, Cmd.none
| SendRequest ->
let response = respondToRequest state.Request
{ state with Response = response }, Cmd.none
let view (state: State) (dispatch: Msg -> unit): IView =
StackPanel.create [
StackPanel.children [
TextBox.create [
TextBox.watermark "Type your request"
TextBox.text $"{state.Request}"
TextBox.onTextChanged (fun text ->
text
|> UpdateRequest
|> dispatch
)
]
Button.create [
Button.content "Send Request"
Button.onClick (fun _ ->
SendRequest
|> dispatch
)
]
TextBlock.create [
TextBlock.text state.Response
]
]
]
// See corresponding blog post:
// https://brandewinder.com/2025/02/19/async-commands-in-avalonia-funcui/
module AsyncOperation =
open System
open Elmish
open Avalonia.Controls
open Avalonia.FuncUI.DSL
open Avalonia.FuncUI.Types
let respondToRequest (request: string) =
task {
// Create an artificial delay
do! Async.Sleep 1000
return $"{DateTime.Now}: Request was {request}"
}
type State = {
Request: string
Response: string
}
type Msg =
| UpdateRequest of string
| SendRequest
| ReceivedResponse of string
let init (): State * Cmd<Msg> =
{
Request = ""
Response = ""
},
Cmd.none
let update (msg: Msg) (state: State): State * Cmd<Msg> =
match msg with
| UpdateRequest text ->
{ state with Request = text }, Cmd.none
| SendRequest ->
// for this to work you also need to use
// Program.runWithAvaloniaSyncDispatch ()
// instead of Program.run
let deferredCmd =
Cmd.OfTask.perform
respondToRequest
state.Request
ReceivedResponse
state, deferredCmd
| ReceivedResponse response ->
{ state with Response = response }, Cmd.none
let view (state: State) (dispatch: Msg -> unit): IView =
StackPanel.create [
StackPanel.children [
TextBox.create [
TextBox.watermark "Type your request"
TextBox.text $"{state.Request}"
TextBox.onTextChanged (fun text ->
text
|> UpdateRequest
|> dispatch
)
]
Button.create [
Button.content "Send Request"
Button.onClick (fun _ ->
SendRequest
|> dispatch
)
]
TextBlock.create [
TextBlock.text state.Response
]
]
]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment