feat: initialize WinUI 3 WebView migration
This commit is contained in:
250
MainWindow.xaml.cs
Normal file
250
MainWindow.xaml.cs
Normal file
@@ -0,0 +1,250 @@
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text.Json;
|
||||
using Microsoft.UI.Windowing;
|
||||
using Microsoft.Web.WebView2.Core;
|
||||
using MMoneyCoderSharp.Data;
|
||||
using Windows.Graphics;
|
||||
using Windows.System;
|
||||
using WinRT.Interop;
|
||||
|
||||
namespace MBN_STOCK_WEBVIEW;
|
||||
|
||||
public sealed partial class MainWindow : Window
|
||||
{
|
||||
private const string AppHost = "app.mbn.local";
|
||||
private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web);
|
||||
private bool _webViewReady;
|
||||
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
Root.Loaded += OnRootLoaded;
|
||||
Closed += OnClosed;
|
||||
}
|
||||
|
||||
private async void OnRootLoaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Root.Loaded -= OnRootLoaded;
|
||||
ResizeWindow();
|
||||
await InitializeWebViewAsync();
|
||||
}
|
||||
|
||||
private void ResizeWindow()
|
||||
{
|
||||
try
|
||||
{
|
||||
var windowHandle = WindowNative.GetWindowHandle(this);
|
||||
var windowId = Microsoft.UI.Win32Interop.GetWindowIdFromWindow(windowHandle);
|
||||
AppWindow.GetFromWindowId(windowId).Resize(new SizeInt32(1600, 960));
|
||||
}
|
||||
catch
|
||||
{
|
||||
// The system-selected size is still usable if sizing is unavailable.
|
||||
}
|
||||
}
|
||||
|
||||
private async Task InitializeWebViewAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var webRoot = Path.Combine(AppContext.BaseDirectory, "Web");
|
||||
var startPage = Path.Combine(webRoot, "index.html");
|
||||
|
||||
if (!File.Exists(startPage))
|
||||
{
|
||||
throw new FileNotFoundException("The packaged Web UI was not found.", startPage);
|
||||
}
|
||||
|
||||
await Browser.EnsureCoreWebView2Async();
|
||||
|
||||
Browser.CoreWebView2.SetVirtualHostNameToFolderMapping(
|
||||
AppHost,
|
||||
webRoot,
|
||||
CoreWebView2HostResourceAccessKind.DenyCors);
|
||||
|
||||
Browser.CoreWebView2.Settings.AreDevToolsEnabled = Debugger.IsAttached;
|
||||
Browser.CoreWebView2.Settings.AreDefaultContextMenusEnabled = Debugger.IsAttached;
|
||||
Browser.CoreWebView2.Settings.IsStatusBarEnabled = false;
|
||||
|
||||
Browser.CoreWebView2.NavigationStarting += OnNavigationStarting;
|
||||
Browser.CoreWebView2.NavigationCompleted += OnNavigationCompleted;
|
||||
Browser.CoreWebView2.HistoryChanged += OnHistoryChanged;
|
||||
Browser.CoreWebView2.NewWindowRequested += OnNewWindowRequested;
|
||||
Browser.CoreWebView2.WebMessageReceived += OnWebMessageReceived;
|
||||
Browser.CoreWebView2.ProcessFailed += OnProcessFailed;
|
||||
|
||||
_webViewReady = true;
|
||||
Browser.Source = new Uri($"https://{AppHost}/index.html");
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
ShowError("WebView2 초기화에 실패했습니다.", exception.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnNavigationStarting(object? sender, CoreWebView2NavigationStartingEventArgs args)
|
||||
{
|
||||
if (!Uri.TryCreate(args.Uri, UriKind.Absolute, out var uri))
|
||||
{
|
||||
args.Cancel = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (uri.Host.Equals(AppHost, StringComparison.OrdinalIgnoreCase) || uri.Scheme == "about")
|
||||
{
|
||||
LoadingOverlay.Visibility = Visibility.Visible;
|
||||
return;
|
||||
}
|
||||
|
||||
args.Cancel = true;
|
||||
_ = OpenExternalUriAsync(uri);
|
||||
}
|
||||
|
||||
private void OnNavigationCompleted(object? sender, CoreWebView2NavigationCompletedEventArgs args)
|
||||
{
|
||||
LoadingOverlay.Visibility = Visibility.Collapsed;
|
||||
|
||||
if (!args.IsSuccess)
|
||||
{
|
||||
ShowError("운영 화면을 불러오지 못했습니다.", args.WebErrorStatus.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
private void OnHistoryChanged(object? sender, object args)
|
||||
{
|
||||
BackButton.IsEnabled = Browser.CanGoBack;
|
||||
ForwardButton.IsEnabled = Browser.CanGoForward;
|
||||
}
|
||||
|
||||
private void OnNewWindowRequested(object? sender, CoreWebView2NewWindowRequestedEventArgs args)
|
||||
{
|
||||
args.Handled = true;
|
||||
|
||||
if (Uri.TryCreate(args.Uri, UriKind.Absolute, out var uri))
|
||||
{
|
||||
_ = OpenExternalUriAsync(uri);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnWebMessageReceived(object? sender, CoreWebView2WebMessageReceivedEventArgs args)
|
||||
{
|
||||
try
|
||||
{
|
||||
var request = JsonSerializer.Deserialize<WebRequest>(args.WebMessageAsJson, JsonOptions);
|
||||
|
||||
switch (request?.Type)
|
||||
{
|
||||
case "ready":
|
||||
case "request-app-info":
|
||||
PostAppInfo();
|
||||
break;
|
||||
case "open-external" when request.Payload.ValueKind == JsonValueKind.Object:
|
||||
if (request.Payload.TryGetProperty("url", out var value) &&
|
||||
Uri.TryCreate(value.GetString(), UriKind.Absolute, out var uri))
|
||||
{
|
||||
_ = OpenExternalUriAsync(uri);
|
||||
}
|
||||
break;
|
||||
case "reload":
|
||||
Browser.Reload();
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (JsonException exception)
|
||||
{
|
||||
PostMessage("bridge-error", new { message = exception.Message });
|
||||
}
|
||||
}
|
||||
|
||||
private void PostAppInfo()
|
||||
{
|
||||
var version = Assembly.GetExecutingAssembly().GetName().Version?.ToString(3) ?? "1.0.0";
|
||||
|
||||
PostMessage("app-info", new
|
||||
{
|
||||
name = "MBN Stock WebView",
|
||||
version,
|
||||
framework = ".NET 8 / WinUI 3",
|
||||
windowsAppSdk = "1.8",
|
||||
architecture = RuntimeInformation.ProcessArchitecture.ToString().ToLowerInvariant(),
|
||||
package = "MSIX",
|
||||
migratedQueryFiles = 71,
|
||||
databaseConfigured = DataQueryExecutor.IsConfigured
|
||||
});
|
||||
}
|
||||
|
||||
private void PostMessage(string type, object payload)
|
||||
{
|
||||
if (!_webViewReady)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Browser.CoreWebView2.PostWebMessageAsJson(
|
||||
JsonSerializer.Serialize(new { type, payload }, JsonOptions));
|
||||
}
|
||||
|
||||
private static async Task OpenExternalUriAsync(Uri uri)
|
||||
{
|
||||
if (uri.Scheme is "https" or "http")
|
||||
{
|
||||
await Launcher.LaunchUriAsync(uri);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnProcessFailed(object? sender, CoreWebView2ProcessFailedEventArgs args)
|
||||
{
|
||||
ShowError("WebView2 프로세스가 종료되었습니다.", args.ProcessFailedKind.ToString());
|
||||
}
|
||||
|
||||
private void ShowError(string title, string message)
|
||||
{
|
||||
LoadingOverlay.Visibility = Visibility.Collapsed;
|
||||
ErrorBar.Title = title;
|
||||
ErrorBar.Message = message;
|
||||
ErrorBar.IsOpen = true;
|
||||
}
|
||||
|
||||
private void OnBackClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (Browser.CanGoBack)
|
||||
{
|
||||
Browser.GoBack();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnForwardClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (Browser.CanGoForward)
|
||||
{
|
||||
Browser.GoForward();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnReloadClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_webViewReady)
|
||||
{
|
||||
Browser.Reload();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnClosed(object sender, WindowEventArgs args)
|
||||
{
|
||||
if (!_webViewReady)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Browser.CoreWebView2.NavigationStarting -= OnNavigationStarting;
|
||||
Browser.CoreWebView2.NavigationCompleted -= OnNavigationCompleted;
|
||||
Browser.CoreWebView2.HistoryChanged -= OnHistoryChanged;
|
||||
Browser.CoreWebView2.NewWindowRequested -= OnNewWindowRequested;
|
||||
Browser.CoreWebView2.WebMessageReceived -= OnWebMessageReceived;
|
||||
Browser.CoreWebView2.ProcessFailed -= OnProcessFailed;
|
||||
}
|
||||
|
||||
private sealed record WebRequest(string Type, JsonElement Payload);
|
||||
}
|
||||
Reference in New Issue
Block a user