diff --git a/README.md b/README.md index de33735..cbf457b 100644 --- a/README.md +++ b/README.md @@ -119,8 +119,8 @@ It is also possible to configure these values through `appsettings.json` like so } ``` > [!NOTE] -> The `Window` object itself is also made available inside of the DI container by injecting `BlazorDesktopWindow`, so you can access all properties on it by using the inject Razor keyword or requesting it through the constructor of a class added as a service. -> The `BlazorDesktopWindow` inherits from the WPF `Window` class, as such you use WPF apis to manipulate it. WPF documentation for the Window class can be found [here](https://learn.microsoft.com/en-us/dotnet/api/system.windows.window?view=windowsdesktop-9.0). +> The main window can be accessed through the `IWindowManager` service available in the DI container. Use `IWindowManager.MainWindow` to get a handle to it. +> The underlying `BlazorDesktopWindow` inherits from the WPF `Window` class, as such you use WPF apis to manipulate it. WPF documentation for the Window class can be found [here](https://learn.microsoft.com/en-us/dotnet/api/system.windows.window?view=windowsdesktop-9.0). > Examples of usage can be found below. ## Custom Window Chrome & Draggable Regions @@ -153,14 +153,14 @@ Using the base template, if you were to edit `MainLayout.razor` and add a `-webk ``` The top bar becomes draggable, applying the `-webkit-app-region: drag;` property to anything will make it able to be used to drag the window. -In terms of handling things such as the close button, you can inject the Window into any page and interact from it there. +In terms of handling things such as the close button, you can inject `IWindowManager` into any page and interact with the main window from there. Here is an example changing `MainLayout.razor`: ```razor -@using BlazorDesktop.Wpf +@using BlazorDesktop.Services @inherits LayoutComponentBase -@inject BlazorDesktopWindow window +@inject IWindowManager WindowManager
+ +
diff --git a/src/BlazorDesktop.Sample/Components/Layout/NavMenu.razor.css b/src/BlazorDesktop.Sample/Components/Layout/NavMenu.razor.css index 54d5f7b..03bb5f0 100644 --- a/src/BlazorDesktop.Sample/Components/Layout/NavMenu.razor.css +++ b/src/BlazorDesktop.Sample/Components/Layout/NavMenu.razor.css @@ -50,6 +50,10 @@ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E"); } +.bi-window-nav-menu { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' viewBox='0 0 16 16'%3E%3Cpath d='M14 2a1 1 0 0 1 1 1v10a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V3a1 1 0 0 1 1-1zM2 1a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2z'/%3E%3Cpath d='M3 5.5a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 0 1h-9a.5.5 0 0 1-.5-.5M11 2a1 1 0 0 1 1 1v10a1 1 0 0 1-1 1V2z'/%3E%3C/svg%3E"); +} + .nav-item { font-size: 0.9rem; padding-bottom: 0.5rem; diff --git a/src/BlazorDesktop.Sample/Components/Pages/ChildWindowContent.razor b/src/BlazorDesktop.Sample/Components/Pages/ChildWindowContent.razor new file mode 100644 index 0000000..b2ecc1e --- /dev/null +++ b/src/BlazorDesktop.Sample/Components/Pages/ChildWindowContent.razor @@ -0,0 +1,30 @@ +@* Licensed to the .NET Extension Contributors under one or more agreements. *@ +@* The .NET Extension Contributors licenses this file to you under the MIT license. *@ +@* See the LICENSE file in the project root for more information. *@ + +
+

Child Window

+

This is running in its own window with an independent Blazor component tree.

+ +
+ +

Counter

+

Current count: @currentCount

+ + +
+ +

+ Window opened at @openedAt.ToString("HH:mm:ss") +

+
+ +@code { + private int currentCount = 0; + private DateTime openedAt = DateTime.Now; + + private void IncrementCount() + { + currentCount++; + } +} diff --git a/src/BlazorDesktop.Sample/Components/Pages/MultiWindow.razor b/src/BlazorDesktop.Sample/Components/Pages/MultiWindow.razor new file mode 100644 index 0000000..b09bbfe --- /dev/null +++ b/src/BlazorDesktop.Sample/Components/Pages/MultiWindow.razor @@ -0,0 +1,59 @@ +@* Licensed to the .NET Extension Contributors under one or more agreements. *@ +@* The .NET Extension Contributors licenses this file to you under the MIT license. *@ +@* See the LICENSE file in the project root for more information. *@ + +@page "/multiwindow" +@using BlazorDesktop.Components +@using BlazorDesktop.Services +@inject IWindowManager WindowManager + +

Multi-Window Demo

+ +

This page demonstrates opening child windows using both the service-based and component-based APIs.

+ +

Service-based API

+

Open windows programmatically via IWindowManager.

+ + +
+ +

Component-based API

+

Toggle a window declaratively with the <DesktopWindow> component.

+ + +@if (_showComponentWindow) +{ + +} + +
+ +

Open Windows

+

Currently tracking @WindowManager.Windows.Count window(s).

+ +@code { + private bool _showComponentWindow; + + private async Task OpenServiceWindow() + { + var handle = await WindowManager.OpenAsync(options => + { + options.Title = "Service Window"; + options.Width = 500; + options.Height = 350; + }); + + handle.Closed += (_, _) => InvokeAsync(StateHasChanged); + + StateHasChanged(); + } + + private void ToggleComponentWindow() + { + _showComponentWindow = !_showComponentWindow; + } +} diff --git a/src/BlazorDesktop/Components/DesktopWindow.cs b/src/BlazorDesktop/Components/DesktopWindow.cs new file mode 100644 index 0000000..2891f31 --- /dev/null +++ b/src/BlazorDesktop/Components/DesktopWindow.cs @@ -0,0 +1,164 @@ +// Licensed to the .NET Extension Contributors under one or more agreements. +// The .NET Extension Contributors licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using BlazorDesktop.Hosting; +using BlazorDesktop.Services; +using Microsoft.AspNetCore.Components; + +namespace BlazorDesktop.Components; + +/// +/// A Blazor component that manages a desktop window declaratively. +/// When rendered, opens a new window. When removed from the render tree, closes it. +/// +public sealed class DesktopWindow : ComponentBase, IAsyncDisposable +{ + [Inject] + private IWindowManager WindowManager { get; set; } = default!; + + /// + /// Gets or sets the root component type for the window. + /// + [Parameter, EditorRequired] + public Type ComponentType { get; set; } = default!; + + /// + /// Gets or sets the window title. + /// + [Parameter] + public string? Title { get; set; } + + /// + /// Gets or sets the window width. + /// + [Parameter] + public int? Width { get; set; } + + /// + /// Gets or sets the window height. + /// + [Parameter] + public int? Height { get; set; } + + /// + /// Gets or sets the minimum window width. + /// + [Parameter] + public int? MinWidth { get; set; } + + /// + /// Gets or sets the minimum window height. + /// + [Parameter] + public int? MinHeight { get; set; } + + /// + /// Gets or sets the maximum window width. + /// + [Parameter] + public int? MaxWidth { get; set; } + + /// + /// Gets or sets the maximum window height. + /// + [Parameter] + public int? MaxHeight { get; set; } + + /// + /// Gets or sets whether the window uses a standard frame. + /// + [Parameter] + public bool? Frame { get; set; } + + /// + /// Gets or sets whether the window is resizable. + /// + [Parameter] + public bool? Resizable { get; set; } + + /// + /// Gets or sets the window icon path. + /// + [Parameter] + public string? Icon { get; set; } + + /// + /// Gets or sets optional parameters to pass to the root component. + /// + [Parameter] + public IDictionary? Parameters { get; set; } + + /// + /// Called when the window is closed (either programmatically or by the user). + /// + [Parameter] + public EventCallback OnClosed { get; set; } + + private DesktopWindowHandle? _handle; + private bool _disposed; + + /// + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (!firstRender) + { + return; + } + + _handle = await WindowManager.OpenAsync(ComponentType, options => + { + options.Title = Title; + options.Width = Width; + options.Height = Height; + options.MinWidth = MinWidth; + options.MinHeight = MinHeight; + options.MaxWidth = MaxWidth; + options.MaxHeight = MaxHeight; + options.Frame = Frame; + options.Resizable = Resizable; + options.Icon = Icon; + }, Parameters); + + _handle.Closed += OnHandleClosed; + } + + private async void OnHandleClosed(object? sender, EventArgs e) + { + if (_handle is not null) + { + _handle.Closed -= OnHandleClosed; + } + + await InvokeAsync(async () => + { + if (OnClosed.HasDelegate) + { + await OnClosed.InvokeAsync(); + } + }); + } + + /// + public async ValueTask DisposeAsync() + { + if (_disposed) + { + return; + } + + _disposed = true; + + if (_handle is not null) + { + _handle.Closed -= OnHandleClosed; + + if (_handle.NativeWindow is not null) + { + await WindowManager.CloseAsync(_handle); + } + + _handle = null; + } + } +} diff --git a/src/BlazorDesktop/Hosting/BlazorDesktopHostBuilder.cs b/src/BlazorDesktop/Hosting/BlazorDesktopHostBuilder.cs index 4fb1c5e..4c6acb2 100644 --- a/src/BlazorDesktop/Hosting/BlazorDesktopHostBuilder.cs +++ b/src/BlazorDesktop/Hosting/BlazorDesktopHostBuilder.cs @@ -4,7 +4,6 @@ using System.Windows; using BlazorDesktop.Services; -using BlazorDesktop.Wpf; namespace BlazorDesktop.Hosting; @@ -123,7 +122,7 @@ private void InitializeDefaultServices() Services.AddWpfBlazorWebView(); Services.AddSingleton(); Services.AddSingleton(); - Services.AddSingleton(); + Services.AddSingleton(); Services.AddHostedService(); } diff --git a/src/BlazorDesktop/Hosting/DesktopWindowHandle.cs b/src/BlazorDesktop/Hosting/DesktopWindowHandle.cs new file mode 100644 index 0000000..cb7951d --- /dev/null +++ b/src/BlazorDesktop/Hosting/DesktopWindowHandle.cs @@ -0,0 +1,52 @@ +// Licensed to the .NET Extension Contributors under one or more agreements. +// The .NET Extension Contributors licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using BlazorDesktop.Wpf; + +namespace BlazorDesktop.Hosting; + +/// +/// A lightweight, thread-safe handle to a managed desktop window. +/// +public sealed class DesktopWindowHandle +{ + /// + /// Gets the unique identifier for this window. + /// + public string Id { get; } + + /// + /// Gets whether this is the main application window. + /// + public bool IsMainWindow { get; } + + /// + /// Gets the native WPF window. Internal use only. + /// + internal BlazorDesktopWindow? NativeWindow { get; set; } + + /// + /// Occurs when the window is closed. + /// + public event EventHandler? Closed; + + /// + /// Creates a new . + /// + /// The unique window identifier. + /// Whether this is the main window. + internal DesktopWindowHandle(string id, bool isMainWindow) + { + Id = id; + IsMainWindow = isMainWindow; + } + + /// + /// Raises the event. + /// + internal void OnClosed() + { + Closed?.Invoke(this, EventArgs.Empty); + } +} diff --git a/src/BlazorDesktop/Hosting/WindowOptions.cs b/src/BlazorDesktop/Hosting/WindowOptions.cs new file mode 100644 index 0000000..dca2728 --- /dev/null +++ b/src/BlazorDesktop/Hosting/WindowOptions.cs @@ -0,0 +1,83 @@ +// Licensed to the .NET Extension Contributors under one or more agreements. +// The .NET Extension Contributors licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace BlazorDesktop.Hosting; + +/// +/// Per-window configuration options. +/// +public class WindowOptions +{ + /// + /// Gets or sets the window title. + /// + public string? Title { get; set; } + + /// + /// Gets or sets the window height. + /// + public int? Height { get; set; } + + /// + /// Gets or sets the window width. + /// + public int? Width { get; set; } + + /// + /// Gets or sets the minimum window height. + /// + public int? MinHeight { get; set; } + + /// + /// Gets or sets the minimum window width. + /// + public int? MinWidth { get; set; } + + /// + /// Gets or sets the maximum window height. + /// + public int? MaxHeight { get; set; } + + /// + /// Gets or sets the maximum window width. + /// + public int? MaxWidth { get; set; } + + /// + /// Gets or sets whether the window uses a standard frame. + /// + public bool? Frame { get; set; } + + /// + /// Gets or sets whether the window is resizable. + /// + public bool? Resizable { get; set; } + + /// + /// Gets or sets the window icon path. + /// + public string? Icon { get; set; } + + /// + /// Creates a from the existing keys. + /// + /// The configuration. + /// A populated . + public static WindowOptions FromConfiguration(IConfiguration config) + { + return new WindowOptions + { + Title = config.GetValue(WindowDefaults.Title), + Height = config.GetValue(WindowDefaults.Height), + Width = config.GetValue(WindowDefaults.Width), + MinHeight = config.GetValue(WindowDefaults.MinHeight), + MinWidth = config.GetValue(WindowDefaults.MinWidth), + MaxHeight = config.GetValue(WindowDefaults.MaxHeight), + MaxWidth = config.GetValue(WindowDefaults.MaxWidth), + Frame = config.GetValue(WindowDefaults.Frame), + Resizable = config.GetValue(WindowDefaults.Resizable), + Icon = config.GetValue(WindowDefaults.Icon) + }; + } +} diff --git a/src/BlazorDesktop/Services/BlazorDesktopService.cs b/src/BlazorDesktop/Services/BlazorDesktopService.cs index 1e81107..d8a7002 100644 --- a/src/BlazorDesktop/Services/BlazorDesktopService.cs +++ b/src/BlazorDesktop/Services/BlazorDesktopService.cs @@ -2,6 +2,7 @@ // The .NET Extension Contributors licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using BlazorDesktop.Hosting; using BlazorDesktop.Wpf; using System.Diagnostics.CodeAnalysis; using System.Reflection; @@ -69,7 +70,15 @@ public Task StopAsync(CancellationToken cancellationToken) private void ApplicationThread() { var app = _services.GetRequiredService(); - var mainWindow = _services.GetRequiredService(); + var config = _services.GetRequiredService(); + var environment = _services.GetRequiredService(); + var rootComponents = _services.GetRequiredService(); + var options = WindowOptions.FromConfiguration(config); + + var mainWindow = new BlazorDesktopWindow(_services, environment, options, rootComponents); + + var windowManager = (WindowManager)_services.GetRequiredService(); + windowManager.RegisterMainWindow(mainWindow, app.Dispatcher); app.Startup += OnApplicationStartup; app.Exit += OnApplicationExit; diff --git a/src/BlazorDesktop/Services/IWindowManager.cs b/src/BlazorDesktop/Services/IWindowManager.cs new file mode 100644 index 0000000..7a15b7b --- /dev/null +++ b/src/BlazorDesktop/Services/IWindowManager.cs @@ -0,0 +1,58 @@ +// Licensed to the .NET Extension Contributors under one or more agreements. +// The .NET Extension Contributors licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using BlazorDesktop.Hosting; +using Microsoft.AspNetCore.Components; + +namespace BlazorDesktop.Services; + +/// +/// Manages multiple desktop windows. Safe to call from any thread. +/// +public interface IWindowManager +{ + /// + /// Gets the main application window handle. + /// + DesktopWindowHandle MainWindow { get; } + + /// + /// Gets all currently open window handles. + /// + IReadOnlyList Windows { get; } + + /// + /// Opens a new window hosting the specified component. + /// + /// The root Blazor component for the window. + /// Optional configuration for window options. + /// Optional parameters to pass to the root component. + /// A handle to the opened window. + Task OpenAsync(Action? configure = null, IDictionary? parameters = null) where TComponent : IComponent; + + /// + /// Opens a new window hosting the specified component type. + /// + /// The root Blazor component type for the window. + /// Optional configuration for window options. + /// Optional parameters to pass to the root component. + /// A handle to the opened window. + Task OpenAsync(Type componentType, Action? configure = null, IDictionary? parameters = null); + + /// + /// Closes the specified window. + /// + /// The window handle to close. + Task CloseAsync(DesktopWindowHandle handle); + + /// + /// Occurs when a window is opened. + /// + event EventHandler? WindowOpened; + + /// + /// Occurs when a window is closed. + /// + event EventHandler? WindowClosed; +} diff --git a/src/BlazorDesktop/Services/WindowManager.cs b/src/BlazorDesktop/Services/WindowManager.cs new file mode 100644 index 0000000..59d4df6 --- /dev/null +++ b/src/BlazorDesktop/Services/WindowManager.cs @@ -0,0 +1,138 @@ +// Licensed to the .NET Extension Contributors under one or more agreements. +// The .NET Extension Contributors licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Concurrent; +using BlazorDesktop.Hosting; +using BlazorDesktop.Wpf; +using Microsoft.AspNetCore.Components; +using WpfDispatcher = System.Windows.Threading.Dispatcher; + +namespace BlazorDesktop.Services; + +/// +/// Internal implementation of . +/// +internal sealed class WindowManager : IWindowManager +{ + private readonly ConcurrentDictionary _windows = new(); + private readonly IServiceProvider _services; + private readonly IWebHostEnvironment _environment; + private DesktopWindowHandle? _mainWindow; + private WpfDispatcher? _dispatcher; + + /// + public DesktopWindowHandle MainWindow => _mainWindow ?? throw new InvalidOperationException("The main window has not been registered yet."); + + /// + public IReadOnlyList Windows => _windows.Values.ToList().AsReadOnly(); + + /// + public event EventHandler? WindowOpened; + + /// + public event EventHandler? WindowClosed; + + public WindowManager(IServiceProvider services, IWebHostEnvironment environment) + { + _services = services; + _environment = environment; + } + + /// + public Task OpenAsync(Action? configure = null, IDictionary? parameters = null) where TComponent : IComponent + { + return OpenAsync(typeof(TComponent), configure, parameters); + } + + /// + public async Task OpenAsync(Type componentType, Action? configure = null, IDictionary? parameters = null) + { + ArgumentNullException.ThrowIfNull(componentType); + + if (!typeof(IComponent).IsAssignableFrom(componentType)) + { + throw new ArgumentException($"The type '{componentType.Name}' must implement {nameof(IComponent)} to be used as a root component.", nameof(componentType)); + } + + var dispatcher = _dispatcher ?? throw new InvalidOperationException("The WPF dispatcher has not been set. Ensure the main window is registered first."); + + var options = new WindowOptions(); + configure?.Invoke(options); + + var handle = new DesktopWindowHandle(Guid.NewGuid().ToString("N"), isMainWindow: false); + + var rootComponents = new RootComponentMappingCollection(); + if (parameters is not null) + { + rootComponents.Add(componentType, "#app", ParameterView.FromDictionary(parameters)); + } + else + { + rootComponents.Add(componentType, "#app"); + } + + await dispatcher.InvokeAsync(() => + { + var window = new BlazorDesktopWindow(_services, _environment, options, rootComponents); + + if (_mainWindow?.NativeWindow is not null) + { + window.Owner = _mainWindow.NativeWindow; + } + + handle.NativeWindow = window; + + window.Closed += (_, _) => OnChildWindowClosed(handle); + + window.Show(); + }); + + _windows.TryAdd(handle.Id, handle); + WindowOpened?.Invoke(this, handle); + + return handle; + } + + /// + public async Task CloseAsync(DesktopWindowHandle handle) + { + ArgumentNullException.ThrowIfNull(handle); + + if (handle.IsMainWindow) + { + throw new InvalidOperationException("The main window cannot be closed via IWindowManager. Use application shutdown instead."); + } + + var dispatcher = _dispatcher ?? throw new InvalidOperationException("The WPF dispatcher has not been set."); + + await dispatcher.InvokeAsync(() => + { + handle.NativeWindow?.Close(); + }); + } + + /// + /// Registers the main window and stores the WPF dispatcher. Called by . + /// + internal void RegisterMainWindow(BlazorDesktopWindow window, WpfDispatcher dispatcher) + { + _dispatcher = dispatcher; + + var handle = new DesktopWindowHandle("main", isMainWindow: true) + { + NativeWindow = window + }; + + _mainWindow = handle; + _windows.TryAdd(handle.Id, handle); + } + + private void OnChildWindowClosed(DesktopWindowHandle handle) + { + _windows.TryRemove(handle.Id, out _); + handle.NativeWindow = null; + handle.OnClosed(); + WindowClosed?.Invoke(this, handle); + } +} diff --git a/src/BlazorDesktop/Wpf/BlazorDesktopWindow.cs b/src/BlazorDesktop/Wpf/BlazorDesktopWindow.cs index a7da917..607d981 100644 --- a/src/BlazorDesktop/Wpf/BlazorDesktopWindow.cs +++ b/src/BlazorDesktop/Wpf/BlazorDesktopWindow.cs @@ -45,8 +45,9 @@ public partial class BlazorDesktopWindow : Window private WindowState _fullscreenStoredState = WindowState.Normal; private readonly IServiceProvider _services; - private readonly IConfiguration _config; private readonly IWebHostEnvironment _environment; + private readonly WindowOptions _options; + private readonly RootComponentMappingCollection _rootComponents; private readonly UISettings _uiSettings; private readonly double[] _zoomSizes = [5, 4, 3, 2.5, 2, 1.75, 1.5, 1.25, 1.1, 1, 0.9, 0.8, 0.75, 0.66, 0.5, 0.33, 0.25]; @@ -67,18 +68,31 @@ public partial class BlazorDesktopWindow : Window "; /// - /// Creates a instance. + /// Creates a instance from configuration. /// /// The services. /// The configuration. /// The hosting environment. public BlazorDesktopWindow(IServiceProvider services, IConfiguration config, IWebHostEnvironment environment) + : this(services, environment, WindowOptions.FromConfiguration(config), services.GetRequiredService()) + { + } + + /// + /// Creates a instance with explicit options and root components. + /// + /// The services. + /// The hosting environment. + /// The window options. + /// The root component mappings. + internal BlazorDesktopWindow(IServiceProvider services, IWebHostEnvironment environment, WindowOptions options, RootComponentMappingCollection rootComponents) { WebView = new BlazorWebView(); WebViewBorder = new Border(); _services = services; - _config = config; _environment = environment; + _options = options; + _rootComponents = rootComponents; _uiSettings = new UISettings(); InitializeWindow(); @@ -92,12 +106,14 @@ public BlazorDesktopWindow(IServiceProvider services, IConfiguration config, IWe /// public void ToggleFullScreen() { + var useFrame = _options.Frame ?? true; + if (WindowStyle == WindowStyle.SingleBorderWindow) { IsFullscreen = true; _fullscreenStoredState = WindowState; - UseFrame(_config.GetValue(WindowDefaults.Frame) ?? true); + UseFrame(useFrame); WindowStyle = WindowStyle.None; if (WindowState == WindowState.Maximized) @@ -113,7 +129,7 @@ public void ToggleFullScreen() { IsFullscreen = false; - UseFrame(_config.GetValue(WindowDefaults.Frame) ?? true); + UseFrame(useFrame); WindowStyle = WindowStyle.SingleBorderWindow; WindowState = _fullscreenStoredState; @@ -171,14 +187,14 @@ public void ZoomOut() private void InitializeWindow() { - var height = _config.GetValue(WindowDefaults.Height) ?? 768; - var width = _config.GetValue(WindowDefaults.Width) ?? 1366; - var minHeight = _config.GetValue(WindowDefaults.MinHeight) ?? 0; - var minWidth = _config.GetValue(WindowDefaults.MinWidth) ?? 0; - var maxHeight = _config.GetValue(WindowDefaults.MaxHeight) ?? double.PositiveInfinity; - var maxWidth = _config.GetValue(WindowDefaults.MaxWidth) ?? double.PositiveInfinity; + var height = _options.Height ?? 768; + var width = _options.Width ?? 1366; + var minHeight = _options.MinHeight ?? 0; + var minWidth = _options.MinWidth ?? 0; + var maxHeight = (double?)_options.MaxHeight ?? double.PositiveInfinity; + var maxWidth = (double?)_options.MaxWidth ?? double.PositiveInfinity; - var useFrame = _config.GetValue(WindowDefaults.Frame) ?? true; + var useFrame = _options.Frame ?? true; if (useFrame) { @@ -232,7 +248,7 @@ private void InitializeWindow() } Name = "BlazorDesktopWindow"; - Title = _config.GetValue(WindowDefaults.Title) ?? _environment.ApplicationName; + Title = _options.Title ?? _environment.ApplicationName; Height = height; Width = width; MinHeight = minHeight; @@ -240,8 +256,8 @@ private void InitializeWindow() MaxHeight = maxHeight; MaxWidth = maxWidth; UseFrame(useFrame); - ResizeMode = (_config.GetValue(WindowDefaults.Resizable) ?? true) ? ResizeMode.CanResize : ResizeMode.NoResize; - UseIcon(_config.GetValue(WindowDefaults.Icon) ?? string.Empty); + ResizeMode = (_options.Resizable ?? true) ? ResizeMode.CanResize : ResizeMode.NoResize; + UseIcon(_options.Icon ?? string.Empty); Content = WebViewBorder; StateChanged += WindowStateChanged; KeyDown += WindowKeyDown; @@ -261,7 +277,7 @@ private void InitializeWebView() WebView.HostPage = Path.Combine(_environment.WebRootPath, "index.html"); WebView.Services = _services; - foreach (var rootComponent in _services.GetRequiredService()) + foreach (var rootComponent in _rootComponents) { WebView.RootComponents.Add(new() { @@ -305,7 +321,7 @@ private void ThemeChanged(UISettings sender, object args) private void UpdateWebViewBorderThickness() { - var useFrame = _config.GetValue(WindowDefaults.Frame) ?? true; + var useFrame = _options.Frame ?? true; WebViewBorder.BorderThickness = new Thickness(20, 20, 20, 20);