JordanMarr / ReactiveElmish.Avalonia

Static Avalonia views for Elmish programs
Other
92 stars 8 forks source link

Use for mobile / wasm platforms #2

Closed daz10000 closed 1 year ago

daz10000 commented 1 year ago

I know this is slightly insane given that this is already pushing the envelope, but I like the Elmish pattern and was trying to retrofit this onto the Avalonia Web demo. I ran into obvious problems (build targets) that were fixable and less obvious issues like missing apis CommandManager.RequerySuggested.AddHandler handler . I don't have it working but before I dig further, is this just an insane thing to try, or should it work with a bit more tweaking? (pointers appreciated and happy to share what I've had to change so far)

JordanMarr commented 1 year ago

I don't know anything about Avalonia + WASM, but it sounds like an interesting project! It doesn't sound insane to me. Playing around with it is the best way to push the technology forward. 👍

daz10000 commented 1 year ago

It's mind blowingly cool - I can have a friendly Xaml based UI dev process, and then target mobile, desktop, and now web with roughly the same code. The demo is easy to get going. You can just make a project with the avalonia.xplat template here and it worked for me pretty much out of the box.

The changes I have made so far are mostly cosmetic. See below for changes and the resulting errors building. I'm just a little too unfamiliar with the avalonia ecosystem. If you feel like playing with it, I am happy to put together a quick repo. It's probably something similar. I managed to get avalonia.editor running in the browser last night which shocked me. Everything mostly just works..

Darren

Avalonia.Xplat project hack

$ dotnet build
MSBuild version 17.5.0-preview-23061-01+040e2a90e for .NET
  Determining projects to restore...
  All projects are up-to-date for restore.
  Elmish.Avalonia -> C:\XXX\Elmish.Avalonia\src\Elmish.Avalonia\bin\Debug\net6.0\Elmish.Avalonia.dll
  Could not extract the MVID from "obj\Debug\net7.0\refint\AvWebTest.dll". Are you sure it is a reference assembly?
  AvWebTest -> C:\XXX\AvWebTestBrokenElmish\AvWebTest\bin\Debug\net7.0\AvWebTest.dll
FSC : error FS0193: The module/namespace 'Avalonia.Controls' from compilation unit 'Avalonia.Controls' did not contain the namespace, module or type 'AppBuilderBase`1' [C:\XXX\AvWebTestBrokenElmish\AvWebTest.Web\AvWebTest.Web.fsproj]
C:\XXX\AvWebTestBrokenElmish\AvWebTest.Web\Program.fs(2,14): error FS1109: A reference to the type 'Avalonia.Controls.AppBuilderBase`1' in assembly 'Avalonia.Controls' was found, but the type could not be found in that assembly [C:\XXX\AvWebTestBrokenElmish\AvWebTest.Web\AvWebTest.Web.fsproj]
FSC : error FS0193: The module/namespace 'Avalonia.Controls' from compilation unit 'Avalonia.Controls' did not contain the namespace, module or type 'AppBuilderBase`1' [C:\XXX\AvWebTestBrokenElmish\AvWebTest.Web\AvWebTest.Web.fsproj]
C:\XXX\AvWebTestBrokenElmish\AvWebTest.Web\Program.fs(3,25): error FS1109: A reference to the type 'Avalonia.Controls.AppBuilderBase`1' in assembly 'Avalonia.Controls' was found, but the type could not be found in that assembly [C:\XXX\AvWebTestBrokenElmish\AvWebTest.Web\AvWebTest.Web.fsproj]
FSC : error FS0193: The module/namespace 'Avalonia.Controls' from compilation unit 'Avalonia.Controls' did not contain the namespace, module or type 'AppBuilderBase`1' [C:\XXX\AvWebTestBrokenElmish\AvWebTest.Web\AvWebTest.Web.fsproj]
C:\XXX\AvWebTestBrokenElmish\AvWebTest.Web\Program.fs(4,18): error FS1109: A reference to the type 'Avalonia.Controls.AppBuilderBase`1' in assembly 'Avalonia.Controls' was found, but the type could not be found in that assembly [C:\XXX\AvWebTestBrokenElmish\AvWebTest.Web\AvWebTest.Web.fsproj]
C:\XXX\AvWebTestBrokenElmish\AvWebTest.Web\Program.fs(14,9): error FS0039: The value, namespace, type or module 'AppBuilder' is not defined. Maybe you want one of the following:   TaskBuilder [C:\XXX\AvWebTestBrokenElmish\AvWebTest.Web\AvWebTest.Web.fsproj]
C:\XXX\AvWebTestBrokenElmish\AvWebTest.Web\Program.fs(19,9): error FS0072: Lookup on object of indeterminate type based on information prior to this program point. A type annotation may be needed prior to this program point to constrain the type of the object. This may allow the lookup to be resolved. [C:\XXX\AvWebTestBrokenElmish\AvWebTest.Web\AvWebTest.Web.fsproj]

Build FAILED.

Elmish.Avalonia hack

diff --git a/src/Elmish.Avalonia/Command.fs b/src/Elmish.Avalonia/Command.fs
index 5cc0fc3..8713a3c 100644
--- a/src/Elmish.Avalonia/Command.fs
+++ b/src/Elmish.Avalonia/Command.fs
@@ -19,7 +19,8 @@ type internal Command(execute, canExecute) =
   let mutable _handler = Unchecked.defaultof<EventHandler>
   member this.AddRequeryHandler () =
     let handler = EventHandler(fun _ _ -> this.RaiseCanExecuteChanged())
-    CommandManager.RequerySuggested.AddHandler handler
+    // daz
+    // CommandManager.RequerySuggested.AddHandler handler
     _handler <- handler

   member this.RaiseCanExecuteChanged () = canExecuteChanged.Trigger(this, EventArgs.Empty)
@@ -28,4 +29,4 @@ type internal Command(execute, canExecute) =
     [<CLIEvent>]
     member _.CanExecuteChanged = canExecuteChanged.Publish
     member _.CanExecute p = canExecute p
-    member _.Execute p = execute p
\ No newline at end of file
+    member _.Execute p = execute p
diff --git a/src/Elmish.Avalonia/Elmish.Avalonia.fsproj b/src/Elmish.Avalonia/Elmish.Avalonia.fsproj
index fdbb21f..ed5f036 100644
--- a/src/Elmish.Avalonia/Elmish.Avalonia.fsproj
+++ b/src/Elmish.Avalonia/Elmish.Avalonia.fsproj
@@ -1,13 +1,11 @@
-ï»ż<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
+ï»ż<Project Sdk="Microsoft.NET.Sdk">

   <PropertyGroup Condition="'$(Configuration)' != 'Debug'">
     <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
   </PropertyGroup>

   <PropertyGroup>
-    <TargetFrameworks>net6.0-windows;netcoreapp3.1;net480</TargetFrameworks>
-    <UseWpf>true</UseWpf>
-    <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
+    <TargetFrameworks>net6.0</TargetFrameworks>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
     <PublishRepositoryUrl>true</PublishRepositoryUrl>
     <DebugType>Embedded</DebugType>
@@ -15,7 +13,6 @@
     <Authors>jsacks, cmeeren, TysonMN, JordanMarr</Authors>
     <Description>F# bindings for using Elmish in Avalonia</Description>
     <PackageLicenseExpression>MIT</PackageLicenseExpression>
-    <PackageProjectUrl>https://github.com/JordanMarr/Elmish.Avalonia</PackageProjectUrl>
     <PackageTags>Avalonia F# fsharp Elmish Elm</PackageTags>
     <!--<PackageIcon>elmish-avalonia-logo-128x128.png</PackageIcon>-->
     <Version>1.0.0-alpha-2</Version>
diff --git a/src/Samples/AvaloniaExample/Program.fs b/src/Samples/AvaloniaExample/Program.fs
index ddefa40..0a185a4 100644
--- a/src/Samples/AvaloniaExample/Program.fs
+++ b/src/Samples/AvaloniaExample/Program.fs
@@ -5,10 +5,11 @@ open Avalonia
 open AvaloniaExample
 open Elmish.Avalonia.AppBuilder

+
 module Program =

@@ -18,3 +19,6 @@ module Program =
     [<EntryPoint; STAThread>]
     let main argv =
         buildAvaloniaApp().StartWithClassicDesktopLifetime(argv)
JordanMarr commented 1 year ago

If you can create a repo that would make it super easy for me to try it out. Also, I know @AngelMunoz is interested in Avalonia + WASM as well.

AngelMunoz commented 1 year ago

~I had the web templates working at some point~, take a look at https://github.com/AvaloniaUI/avalonia-dotnet-templates/pull/102 ~It should give you an idea how to set up even with funcui~

EDIT: I thought this was a notification in the FuncUI repo (I'll have to pay more attention when reading notifications from the phone)

AngelMunoz commented 1 year ago

I agree with Jordan a sample repository would be very helpful and I could lend a hand if there are other issues, over all Avalonia works just nicely with WASM workloads, in the PR I linked above it shows at least what dependencies and which versions of the packages we used to make funcui work, since there's no funcui here it should be as simple or even simpler to get it working if we part from there

I'd suggest you can start with a simple counter and then move from there so we can identify missing APIs (like the one you mentioned)

daz10000 commented 1 year ago

Take a look at this - it has a small hack to the Elmish.Avalonia repo mainly to get it to compile with the missing api, and then a fairly out of the box avalonia cross platform template. I'm sure it's something pretty simple (and dumb) but any help appreciated. I don't know if removing the call I removed is critical for example :(

https://github.com/daz10000/ElmishAvaloniaExample

I have a fairly similar dummy app working with plaIn MVVM successfully if you want to compare.

daz10000 commented 1 year ago

I even have CI working on the repo, so it reliably (doesn't build) the way it's not building for me. Thanks in advance for taking a look

e.g.

https://github.com/daz10000/ElmishAvaloniaExample/actions/runs/4350930131

daz10000 commented 1 year ago

I saw your FuncUI posts too @AngelMunoz - I will check those out as well. I'm mainly looking for an F# idiomatic way of getting Avalonia working in the web space, and just a little too worn out by the endless models and views and viewmodels of the MVVM world - "it should have been an function call" would be my high level review.

AngelMunoz commented 1 year ago

@daz10000 I managed to update the template from that FuncUI post to Elmish.Avalonia

https://github.com/AngelMunoz/AvaloniaTestElmish

However, I'm facing a weird build bug in the wasm target.

PS C:\Users\scyth\repos\AvaloniaTestElmish> dotnet run --project AvaloniaTest.Web
Building...
A fatal error was encountered. The library 'hostpolicy.dll' required to execute the application was not found in 'C:\Users\scyth\repos\AvaloniaTestElmish\AvaloniaTest.Web\bin\Debug\net7.0\'.
Failed to run as a self-contained app.
  - The application was run as a self-contained app because 'C:\Users\scyth\repos\AvaloniaTestElmish\AvaloniaTest.Web\bin\Debug\net7.0\AvaloniaTest.Web.runtimeconfig.json' was not found.
  - If this should be a framework-dependent app, add the 'C:\Users\scyth\repos\AvaloniaTestElmish\AvaloniaTest.Web\bin\Debug\net7.0\AvaloniaTest.Web.runtimeconfig.json' file and specify the appropriate framework.
PS C:\Users\scyth\repos\AvaloniaTestElmish> 

I'm not sure if I'm missing something or am I configuring something wrong....

Besides that I tested that template with the desktop target, and it ran completely fine I'm sure once that wasm issue gets away (which I have no idea how to fix) you should be use those as a starter for dotnet templates with Elmish.Avalonia

daz10000 commented 1 year ago

nice! - that's progress. Let me pull your fork and see if I can reproduce that. I have a parallel code base that's similar and built off the MVVM template so maybe I can compare and work out what's happening.

Edit: - does reproduce. Let me try some diffs with a working build.

daz10000 commented 1 year ago

I can get it to compile and run with just this change to AvaloniaTest.Web.fsproj. I get just a blank screen after it finishes loading, but that's kind of progress

-ï»ż<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
+ï»ż<Project Sdk="Microsoft.NET.Sdk">
daz10000 commented 1 year ago

Debugging - it does start up, code runs, seems to complete initialization properly, but nothing getting to screen and might still be failing somewhere. Desktop works. Well down to print statement debugging but the problem seems to be that the top level objects in MainViewModel and CounterViewModel aren't getting executed, so designVM, vm, aren't initialized. Not sure if that's a wasm implementation thing, or if an error is getting thrown somewhere I can't see and initialization stops.

daz10000 commented 1 year ago

Progress!

image

The App OnFrameworkInitializationCompleted needs a case for SingleViewApplicationLifetime - the desktop MainView branch doesn't apply. This gets me to at least a rendered screen but buttons still not working.

override this.OnFrameworkInitializationCompleted() =
        printfn "App.OnFrameworkInit top"
        match this.ApplicationLifetime with
        | :? IClassicDesktopStyleApplicationLifetime as desktop ->
            printfn "App.OnFrameworkInit classic view"
            let view = MainView()
            printfn "App.OnFrameworkInit got view"
            desktop.MainWindow <- view
            printfn "App.OnFrameworkInit starting"
            try
                ViewModels.MainViewModel.vm.Start(view)
            with x ->
                printfn $"Exception: {x.Message} \n {x.StackTrace}"
            printfn "App.OnFrameworkInit started"
        | :? ISingleViewApplicationLifetime as singleViewLifetime ->
            printfn "App.OnFrameworkInit single view lifetime"
            printfn "App.OnFrameworkInit set mainview"
            singleViewLifetime.MainView <- CounterView()

            printfn "App.OnFrameworkInit get vm y"
            let y = ViewModels.CounterViewModel.vm
            printfn "App.OnFrameworkInit get vm x"
            let x = ViewModels.MainViewModel.vm
            printfn "App.OnFrameworkInit start vm"
            x.Start(singleViewLifetime.MainView)
            printfn "App.OnFrameworkInit done"
daz10000 commented 1 year ago

Well, after banging head against this for quite a while, I'm stuck. It does seem to initialize everything but the controls that have bindings (count, and the datatable) never appear, From the logging (I learned how to use that at least), there is a lot of activity initializing and drawing them, but no data ever appears and the buttons are not active. Probably something simple but I can't see it right now My hacked version of your repo @AngelMunoz is here. @JordanMarr - the underlying library looks beautiful and I'm soo close to getting it working for wasm, I want to keep grinding away at this. One thing I could never see (in the desktop or web versions) was any of the binding events in the logging. I put some print statements in the binding functions and they do get called on both platforms. Just nothing appearing for the web. The biggest difference between web and desktop is the main window. The web doesn't really have concept of windows in general, so MainView is just a container for the single CounterView in Angel's example

JordanMarr commented 1 year ago

Nice! This is what I love about OSS. Edit: I just saw your last post. 😅

AngelMunoz commented 1 year ago

It could be something related to trimmed assemblies, both android and wasm do that so we might be trimming something without knowing (like the data grid assembly for example)

Because if it works for desktop it should work in the other platforms as well, I'll keep taking a look during the week or the next weekend and let you know if I figure out something

daz10000 commented 1 year ago

Yeah, I think it's very close - I wonder if there is something dumb in there, like a control not enabled by default in the wasm world. The wasm and desktop code is almost the same, just slightly different initialization and the focus on the actual control being center stage, not the windows as containers. Darker theory - something going wrong during the binding setup that's throwing an error not caught. It's tricky to catch errors - they don't go to stdout by default. I turned on every logging feature I could find and wrapped a bunch of stuff in try/except handlers.

As you just wrote @AngelMunoz - could easily be a missing assembly or something needed at runtime. From the logging, all the interactions with the controls are there. It talks to the dataview etc, measures everything and lays it out (per the logs), but just doesn't display it finally (maybe hiding it because there is no data?) Logs attached

image logs.txt

daz10000 commented 1 year ago

A hell of a lot of print statements later, and comparing the startup of the desktop and web versions, I haven't resolved the problem, but have narrowed it down. TheViewLocator build code never fires in the web version for COunterView. I can't tell if this is the root cause or just related to the counter view not fulling rendering or being responsive. Currently trying to understand how this code gets executed (it works in the desktop build). XXX bits are all my tracing. I suspect root cause is still that for web, we initialize as a singleview application and so I'm skipping creating the mainview (because it's window derived). I might need to restruct the app (this is your test app @AngelMunoz ) so the main "window" is a usercontrol or something for the web.

image

daz10000 commented 1 year ago

Retrying the path where I try to make MainWindow the MainViewModel and it (rightfully!) doesn't want to create a window when it's running in the browser

Exception: Specified method is not supported. [dotnet.js:2165:16](http://localhost:7000/dotnet.js)
    at Avalonia.Browser.BrowserWindowingPlatform.CreateWindow() in /_/src/Browser/Avalonia.Browser/WindowingPlatform.cs:line 17 [dotnet.js:2165:16](http://localhost:7000/dotnet.js)
   at Avalonia.Controls.Platform.PlatformManager.CreateWindow() in /_/src/Avalonia.Controls/Platform/PlatformManager.cs:line 31 [dotnet.js:2165:16](http://localhost:7000/dotnet.js)
   at Avalonia.Controls.Window..ctor() in /_/src/Avalonia.Controls/Window.cs:line 219 [dotnet.js:2165:16](http://localhost:7000/dotnet.js)
   at AvaloniaTest.Views.MainView..ctor() in .../AvaloniaTest\Views\MainView.axaml.fs:line 8 [dotnet.js:2165:16](http://localhost:7000/dotnet.js)
   at AvaloniaTest.App.OnFrameworkInitializationCompleted() in .../AvaloniaTest\App.axaml.fs:line 40
JordanMarr commented 1 year ago

Can you ditch the view locator and manually bind the VM?

daz10000 commented 1 year ago

WOO! - finally, success. So, (unfortunately but logically), the main "window" has to be a usercontrol for the web version. No windows. If I change MainWindow in @AngelMunoz 's AvaloniaTest app to a UserControl, you have to lose a bunch of properties - like icon and title that aren't relevant for a usercontrol. The App.axaml.fs branch for desktop won't work now, since it needs a window, but commenting that out, I can get the web version to start and work!

image

It seems like a cross platform application will need two different top level contains - a MainWindow for desktop and a MainControl for all the other SingleViewLifetime applications. I'll see if I can get both to coexist but do you have a cleaner solution here?

AngelMunoz commented 1 year ago

Ah Yeah the window thing makes sense, with Avalonia.FuncUI we actually provide a Control to the single view lifetime, so perhaps here we need to do the equivalent

EDIT: Just as I was typing this you made it work

daz10000 commented 1 year ago

This is the App startup code with the two branches for Desktop (now broken) and Web (now working)

type App() =
    inherit Application()

    override this.Initialize() =
        // Initialize Avalonia controls from NuGet packages:
        printfn "App.Initialize top"
        let _ = typeof<Avalonia.Controls.DataGrid>
        printfn "App.Initialize loading"

        AvaloniaXamlLoader.Load(this)
        printfn "App.Initialize loaded"

    override this.OnFrameworkInitializationCompleted() =
        printfn "App.OnFrameworkInit top"
        match this.ApplicationLifetime with
        | :? IClassicDesktopStyleApplicationLifetime as desktop ->
            printfn "App.OnFrameworkInit classic view"
            let view = MainView()
            printfn "App.OnFrameworkInit got view"
            // HACKHACK - commented out since this won't work for a usercontrol
            // desktop.MainWindow <- view
            printfn "App.OnFrameworkInit starting"
            try
                ViewModels.MainViewModel.vm.Start(view)
            with x ->
                printfn $"Exception: {x.Message} \n {x.StackTrace}"
            printfn "App.OnFrameworkInit started"
        | :? ISingleViewApplicationLifetime as singleViewLifetime ->
            try
                printfn "App.OnFrameworkInit single view lifetime"
                printfn "App.OnFrameworkInit set mainview"

                let view = MainView()
                singleViewLifetime.MainView <- view

                let x = ViewModels.MainViewModel.vm
                printfn "App.OnFrameworkInit start vm"
                x.Start(view)
                printfn "App.OnFrameworkInit done"
            with x ->
                printfn $"Exception: {x.Message} \n {x.StackTrace}"

        | _ ->
            // leave this here for design view re-renders
            printfn "App.OnFrameworkInit other view"

        printfn "App.OnFrameworkInit mark completed"
        base.OnFrameworkInitializationCompleted()
        printfn "App.OnFrameworkInit done"
AngelMunoz commented 1 year ago

Yeah looks like there has to be a way to use a "content" control for the window or the control itself

For example in the FuncUI templates we do this https://github.com/AngelMunoz/AvaloniaTest/blob/main/AvaloniaTest/Program.fs#L22-L27

        match this.ApplicationLifetime with
        | :? IClassicDesktopStyleApplicationLifetime as desktopLifetime ->
            desktopLifetime.MainWindow <- MainWindow()
        | :? ISingleViewApplicationLifetime as singleViewLifetime ->
            singleViewLifetime.MainView <- Counter.view()
        | _ -> ()

where MainWindow is actually a Window type with the content set to the control this.Content <- Counter.view()

and in the single lifetime we pass the Counter.view() control directly

daz10000 commented 1 year ago

Thanks again for the help, code and just listening to me complain :) It's a little kludgey but I ended up adding a MainView and MainWindow for the two cases. I figure since the desktop version can optionally style icons and titles, it deserves a separate top container anyway. I think I can get back to building an actual app now. The final code (modified version of your demo @AngelMunoz ) is here. If you want to sell me on using FuncUI, I'm open to giving that a try too. I am looking for a relatively simple way to stick to a mostly Elmish pattern for UI building - I've had great experiences maintaining a lot of code with react that way, and it feels like the right pattern for Avalonia, but I'd rather work out the right pattern before diving into writing something large. I'll probably continue to experiment a little. Thanks again, and especially @JordanMarr for putting Elmish.Avalonia out there. Let me know if I can be helpful - it would be nice to have a template that does cross platform out of the box.

JordanMarr commented 1 year ago

it’s super exciting that you got it working and can now continue on to creating an app!

I think it would be a great idea to have it listed in the Samples folder here in the repo, so feel free to submit a PR. I’m sure others would be very interested to benefit from the hard work you guys did to get it working!

As for FuncUI, i would imagine that you should be able to have a hybrid approach where some views are xaml and some are FuncUI. (I’ve been meaning to try that but haven’t gotten around to it yet.) The main advantage to using xaml is that it lets you take advantage of the fantastic xaml preview extension and the designVM. Otoh, the FuncUI style is a lot of fun to use.

daz10000 commented 1 year ago

very happy to submit as a sample. I was thinking you could use some more.  speaking of which, I’m looking for the idiomatic way to get a child component to message the main view to switch views. I have an application with a bunch of distinct models that need to switch back and forth (eg a gallery view then an editor for items on gallery).   Was trying to work out if having main model subscribe to child events was the recommended model ?  suggestions apprecursed and Ill get you a PR with the exampleDarrenAm 3/18/23 um 7:21 PM schrieb Jordan Marr @.***>:ï»ż it’s super exciting that you got it working and can now continue on to creating an app! I think it would be a great idea to have it listed in the Samples folder here in the repo, so feel free to submit a PR. I’m sure others would be very interested to benefit from the hard work you guys did to get it working! As for FuncUI, i would imagine that you should be able to have a hybrid approach where some views are xaml and some are FuncUI. (I’ve been meaning to try that but haven’t gotten around to it yet.) The main advantage to using xaml is that it lets you take advantage of the fantastic xaml preview extension and the designVM. Otoh, the FuncUI style is a lot of fun to use.

—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you were mentioned.Message ID: @.***>

AngelMunoz commented 1 year ago

@daz10000 oh no worries I'm not trying to sell FuncUI here, I just grab it as a reference because that's the one I know it works and have a little bit of experience with.

I'm glad everything is working now, I'm sure there's folks in the community who also like this approach and would like to use it as well!

JordanMarr commented 1 year ago

I’m looking for the idiomatic way to get a child component to message the main view to switch views. I have an application with a bunch of distinct models that need to switch back and forth (eg a gallery view then an editor for items on gallery).   Was trying to work out if having main model subscribe to child events was the recommended model ?  

Are you doing it old school style where you are opening edit views in their own separate Window with their own Elmish loop, or are you doing it more like a SPA where your views are in user controls that are controlled from the main shell view?

Assuming the latter, the Elmish.WPF Samples have some good examples on how to wire up submodel views. There are some really cool abstractions they made (that were copied into this library) that makes it easier to wire up the submodels.

If the former, i think it wouldn’t be cool to use Rx to send a message from one window to another, and you could pipe those in using the Elmish 4 event subscriptions!

daz10000 commented 1 year ago

I haven’t embarked on either of those journeys just yet so open to recommendations.  My main use case is web based so no multiple windows , just none area.  I would prefer to minimally entangle the pieces (not embedding lower component models in the main model and running all the events through a single dispatcher ) but as a practical matter when the user finishes a subtask the child user control needs to let a parent know so the view can switch.  I did notice that you’ve modeled the elmish.wpf functions and they have good docs so that’s helpful. If there is a simple example of child user control messaging a parent that would be helpful. i see support for opening mew “windows” and managing the transition back but in the web and mobile cases there are no windows. darrenAm 3/18/23 um 9:58 PM schrieb Jordan Marr @.***>:ï»ż

I’m looking for the idiomatic way to get a child component to message the main view to switch views. I have an application with a bunch of distinct models that need to switch back and forth (eg a gallery view then an editor for items on gallery).   Was trying to work out if having main model subscribe to child events was the recommended model ?  

Are you doing it old school style where you are opening edit views in their own separate Window with their own Elmish loop, or are you doing it more like a SPA where you are views in user controls that are controlled from the main shell view? Assuming the latter, the Elmish.WPF Samples have some good examples on how to wire up submodel views. There are some really cool abstractions they made (that were copied into this library) that makes it easier to wire up the submodels. If the former, i think it wouldn’t be cool to use Rx to send a message from one window to another, and you could pipe those in using the Elmish 4 event subscriptions!

—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you were mentioned.Message ID: @.***>

JordanMarr commented 1 year ago

I updated the AvaloniaExample project with the following:

daz10000 commented 1 year ago

ooh! thank you. make my Sunday.  will check this out when I have better network connection and try to follow the pattern in the counter app. that sounds like exactly what I need.  Am 3/19/23 um 11:36 AM schrieb Jordan Marr @.***>:ï»ż I updated the AvaloniaExample project with the following:

Renamed IStart / Start to IElmishViewModel / ElmishViewModel, which has method StartElmishLoop. (I think this is way more intuitive) Added a second view, AboutView / AboutViewModel Added nav buttons to the MainView that swap out the content Added a Messaging module that uses Rx to provide pub/sub between view models AboutViewModel now publishes a GoHome message when the Ok button is clicked MainViewModel now subscribes to this event via an Elmish subscription

—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you were mentioned.Message ID: @.***>

daz10000 commented 1 year ago

You should have a PR now with the cross platform example all cleaned up. I can confirm that I was able to easily wire your view swapping solution into the app I'm building and it works like a charm. I also love being able to test the application quickly with desktop with all the simplicity that it gives, and then just build the wasm/web version and see it run the same. It's really quite stunning (the web version is maybe a little slower with event handling but still quite usable). This looks like it will be very productive.

JordanMarr commented 1 year ago

Merged!

JordanMarr commented 1 year ago

A more declarative way of handling navigation would be to use the TabControl with a tab for each page.

https://github.com/AvaloniaUI/XamlControlsGallery/blob/master/XamlControlsGallery/Views/MainView.xaml

Or, if you want to stick with using buttons to do it manually, I was messing around with styling the buttons in the MainView to look more like a tab bar in the sample app:

image

Btw, I think it's really amazing that the Avalonia preview pane extension allows you to interact with the form in design mode. For example, I can actually click on the +/- buttons and the form will actually work. đŸ”„

daz10000 commented 1 year ago

Aside: and I mentioned this in the other issue I just filed.. The TabControl might be a nice way to evolve this sample into a gallery showing off the different controls. I loved the early Silverlight demo that exercised everything and it was a great resource for implementation because you could copy working examples. It's either a gallery or I try to write a useful demo piece of software as my next sample, like a calendar , todo list... or something else. Happy to write down my notes on using Elmish.Avalonia here as documentation in samples. Everything else is going well with my real app.