Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proper Multi-Window Support #150

Open
AeonSake opened this issue Jan 24, 2025 · 4 comments
Open

Proper Multi-Window Support #150

AeonSake opened this issue Jan 24, 2025 · 4 comments
Labels
All OS help wanted Extra attention is needed

Comments

@AeonSake
Copy link

Is there a way to properly support multi-window applications?

The sample application only initializes additional windows as part of the RegisterWindowCreatedHandler event/callback (i.e., at application launch), and I was unable to get this to work if the the additional window should be created at runtime. The only way I found to get close to this is wrapping the window creation into a separate management class that creates/disposes PhotinoBlazorApp instances but when this is done in separate tasks (which it has to be because PhotinoBlazorApp.Run() blocks), it always results in only loading the first window properly while the second window fails to load (permanently stuck in a white loading screen). I assume there is an issue with the synchronization context and/or the proper UI thread not being used. Also there seems to be a memory leak/issue when creating the service collection outside of the task/context that starts the PhotinoBlazorApp instance.

Furthermore, is it possible to share services or a ServiceCollection between those windows? I believe this is currently not possible since PhotinoBrowser, PhotinoBlazorApp and other photino services are registered as singleton. It would be great to have a solution similar to how Hybrid Blazor is handled: each window is a separate scope, singleton instances are shared across all windows while scoped services are created for each window.

@MikeYeager
Copy link
Collaborator

MikeYeager commented Jan 30, 2025

@AeonSake This approach seems to work. It's very similar to how we handle multiple windows on Photino.NET (without Blazor).
In your Program.cs, add a static method:

        public static void OpenChildwindow()
        {
            var appBuilder = PhotinoBlazorAppBuilder.CreateDefault(new string[0]);
            appBuilder.Services
                .AddLogging();

            // register root component
            appBuilder.RootComponents.Add<App>("app");

            var app = appBuilder.Build();

            // customize window
            app.MainWindow
                .SetIconFile("favicon.ico")
                .SetTitle("Photino Hello World Child");

            AppDomain.CurrentDomain.UnhandledException += (sender, error) =>
            {
                app.MainWindow.ShowMessage("Fatal exception", error.ExceptionObject.ToString());
            };

            app.Run();
        }

Then in your Razor page add a button:

    <button @onclick="OpenChildWindow">Child Window</button>

And the method in the @code section:

    void OpenChildWindow()
    {
        Program.OpenChildwindow();
    }

As far as sharing services, we don't know as we haven't ever tried it. You could certainly expose them as REST services and share them that way.

@MikeYeager MikeYeager added All OS help wanted Extra attention is needed labels Jan 30, 2025
@AeonSake
Copy link
Author

AeonSake commented Feb 2, 2025

@MikeYeager thanks for the reply and example but my main issue with this approach (and with the sample in the repo) is that it always requires interaction with the UI. I'd need to create a window programmatically (and from potentially from a different task/thread) which is where I am currently stuck at. I assume I need the UI/main thread as a synchronization context so that the instance created by the PhotinoBlazorAppBuilder will actually work/render properly. It seems to not work from any other task/thread. Any idea how to get that working?

I can work around the issue with the shared services by creating a state sync/dispatch service and give each Photino app the pre-initialized instance as a singleton service. Though that involves a lot more work in handling state updates across services properly and is not the same as just having the same service instance available to all Photino apps/windows. The only other way around would be to initialize the shared services beforehand and add them to the service collections directly but that would bypass all the DI functionality.

@AeonSake
Copy link
Author

AeonSake commented Feb 2, 2025

In terms of shared services, it might be possible to register all relevant Photino services as scoped services and create a new scope inside the PhotinoBlazorApp.Initialize() method which is then used for all subsequent interactions. But it would probably be a good idea to rework that class slightly if multi-window support gets baked-in so, e.g., that class could be called to create a new window directly; maybe something like PhotinoBlazorApp.CreateWindow() : PhotinoWindow where the PhotinoBlazorApp keeps track of all PhotinoWindow instances and only returns from the Run() method once all windows have closed?

@MikeYeager
Copy link
Collaborator

@AeonSake We're unclear what your scenario is. In the sample above, we wired up the call to create the new window from a button in JavaScript, but you can call it from anywhere you like, including your C# code. It's just a call to a static method. It doesn't require user interaction. It just has to be called on the UI thread.

In Win32 there is a single UI thread, and all windows run in the same message handler loop. All windows must run on that UI thread. the only way around it is to fire up a separate process. In that case, you can't share anything across processes.

The multi-windows sample shows how to configure multiple windows on the same UI thread and create them all at once, on startup. The sample above shows how to create additional windows on the same UI thread as the main window after the main window has started. All child windows are passed a handle to the main window when created to link child windows to the main window.

What are we missing?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
All OS help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

2 participants