heoo.lib
1.3.1
dotnet add package heoo.lib --version 1.3.1
NuGet\Install-Package heoo.lib -Version 1.3.1
<PackageReference Include="heoo.lib" Version="1.3.1" />
paket add heoo.lib --version 1.3.1
#r "nuget: heoo.lib, 1.3.1"
// Install heoo.lib as a Cake Addin
#addin nuget:?package=heoo.lib&version=1.3.1
// Install heoo.lib as a Cake Tool
#tool nuget:?package=heoo.lib&version=1.3.1
half-elmish-oo
I want to create elmish program loops (message processing loops) with a simple callback.
Example
Step1 - Design your program
open heoo.lib
open System
//Elmish programs need a model a message and update function.
type Model = { Count: int; SomeText:string }
type Message =
| Increase
| Decrease
| Reset
| SetText of string
let Update (model:Model) (msg:Message) =
match msg with
| Increase -> { model with Count = model.Count + 1 }
| Decrease -> { model with Count = model.Count - 1 }
| Reset -> { model with Count = 0 }
| SetText text -> { model with SomeText = text }
Step2 - Create your viewmodel
type MyVm(initialModel,messageCallback) =
inherit ViewModelBase.T<Model>(initialModel)
//Remember that this is async.
//Wait for InotifyPropertyChanged until getter is properly updated.
member this.GetSetSomeText
with get() =
this.getPropertyError(
fun m ->
match m.SomeText with
| "" -> "SomeText cannot be empty"
| _ -> ""
)//empty string is no error
this.getPropertyValue(fun m -> m.SomeText)
and set v = v |> SetText |> messageCallback
member this.GetCounter
with get() = this.getPropertyValue(fun m -> m.Count)
member this.IncreaseCmd =
CommandBase.AlwaysExecutableCommand(fun _ -> messageCallback Increase)
member this.DecreaseCmd =
CommandBase.AlwaysExecutableCommand(fun _ -> messageCallback Decrease)
member this.ResetCmd =
let canExecute = fun _ m -> m.Count <> 0 //commandParameter -> model -> bool
let execute = fun _ -> messageCallback Reset //commandParameter -> unit
this.getCommandBaseT(canExecute,execute)
member this.GetAllErrorMessages
//ignore the keys (propertynames) and just get the values in an array
with get():string array = this.getPropertyValue(
fun model ->
//Errors are stored as key,value pairs.
//Where value is a function that returns a string from given model.
this.GetErrorsArray()
|> Array.map(fun (_,errorFunc) -> errorFunc model)
)
Step3 - Instantiate your program loop and viewmodel
let initialModel = { Count = 0; SomeText = "Hello World" }
let program = new ElmishProgramAsync.T<Model,Message>(initialModel,Update)
//WARNING!, once you call (IDisposable)Dispose() on the program loop, you can't use it anymore.
//like this: program.AsIDisposable().Dispose()
let viewModelInstance = MyVm(initialModel,program.PostMessage)
Step3.1 - wire OnModelUpdated to viewmodel
//Remember to wire your programs OnModelUpdated action to your viewmodel
//It might be a good idea to not write this here and move it into your gui application instead.
program.OnModelUpdated <-
Action<Model>(
fun m ->
//if this is a gui application, thread synchronization is (usually) needed.
//This might be a good place for it to happen.
//...
//example: Dispatcher.Invoke(viewMModelInstance.updateModel m)
viewModelInstance.updateModel m
)
|> Some
Step4 - Consume your viewmodel
Consume your viewmodel in your application (wpf, Winforms, Avalonia, MAUI.net,Uno, Unity3D etc) As you would with any other INotifyPropertyChanged,INotifyDataErrorInfo, IDataErrorInfo, ICommand implementation.
Additional comments
It should be no problem to create more elmishprograms to avoid the whole monolithic approach.
It's also possible to have multiple viewmodels from one program.
I'm trying to leave the door open for more advanced scenarios.
Where you as a consumer is in control.
Don't mistake my absence for abandonment.
This library is complete and stable and will stand the test of time.
heoo.lib isnt about running fast or optimizing, its only a layer between logic and ui.
I use it in my own products which are planned to run for 10+ years.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
.NET Core | netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
.NET Standard | netstandard2.1 is compatible. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | tizen60 was computed. |
Xamarin.iOS | xamarinios was computed. |
Xamarin.Mac | xamarinmac was computed. |
Xamarin.TVOS | xamarintvos was computed. |
Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.1
- FSharp.Core (>= 7.0.0)
-
net6.0
- FSharp.Core (>= 7.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated | |
---|---|---|---|
1.3.1 | 258 | 3/1/2023 | |
1.3.0 | 219 | 2/15/2023 | |
1.2.5 | 237 | 2/7/2023 | |
1.2.4 | 469 | 10/12/2022 | |
1.2.3 | 433 | 10/6/2022 | |
1.2.2 | 431 | 10/5/2022 | |
1.2.1 | 452 | 9/27/2022 | |
1.2.0 | 457 | 9/25/2022 | |
1.1.3-rc | 264 | 9/15/2022 | |
1.1.2-rc | 483 | 9/14/2022 | |
1.1.1-rc | 116 | 9/13/2022 | |
1.1.0-rc | 134 | 9/13/2022 | |
1.0.1-rc | 115 | 9/6/2022 | |
1.0.0-rc | 113 | 9/5/2022 |
Optional parameter added for ElmishProgramAsync constructor. It is now possible to send updated model between messages or after all messages have been processed. Standard and recommended is AfterAllMessages, at least when viewmodels are the consumers.