Files
speckle-sharp-connectors/DUI3/Speckle.Connectors.DUI.WebView/DUI3ControlWebView.xaml.cs
T
Jedd Morgan 3028e9e29d Asyicify ExecuteScriptAsync calls (#240)
* First pass adding async

* Updated Idle manager to work with async Tasks

* CA1506

* missing async

* Fixed tests

* Really fixed tests

* CA2201

* Really fixed the tests

* remaining awaits

* Async everywhere!

* Fixed merge errors

* fix: Missing imports

* IProgress sync used

* rename progress

* format

* Remove empty files

* update sdk

* convert progress

* use FireAndForget and readonly struct

* revert revit to use RevitTask

* don't use sync to UI thread for progress

* Fixes autocad loop to update UI with Task.Delay instead of Task.Yield

* format

* merge fixes

* update nugets

* fix imports for exceptions

* fix import

---------

Co-authored-by: Alan Rynne <alan@speckle.systems>
Co-authored-by: Adam Hathcock <adam@hathcock.uk>
2024-10-09 16:10:13 +01:00

111 lines
3.3 KiB
C#

using System.Windows.Controls;
using System.Windows.Threading;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Web.WebView2.Core;
using Speckle.Connectors.DUI.Bindings;
using Speckle.Connectors.DUI.Bridge;
namespace Speckle.Connectors.DUI.WebView;
public sealed partial class DUI3ControlWebView : UserControl, IBrowserScriptExecutor, IDisposable
{
private readonly IServiceProvider _serviceProvider;
public DUI3ControlWebView(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
InitializeComponent();
Browser.CoreWebView2InitializationCompleted += (sender, args) =>
_serviceProvider
.GetRequiredService<ITopLevelExceptionHandler>()
.CatchUnhandled(() => OnInitialized(sender, args));
}
public bool IsBrowserInitialized => Browser.IsInitialized;
public object BrowserElement => Browser;
// {
// if (!Browser.IsInitialized)
// {
// throw new InvalidOperationException("Failed to execute script, Webview2 is not initialized yet.");
// }
//
// var t = Browser.Dispatcher.Invoke(
// async () =>
// {
// var res = await Browser.ExecuteScriptAsync(script).ConfigureAwait(true);
// await Task.Delay(100).ConfigureAwait(true);
// return res;
// },
// DispatcherPriority.Background
// );
//
// _ = t.IsCompleted;
// bool isAlreadyMainThread = Browser.Dispatcher.CheckAccess();
// if (isAlreadyMainThread)
// {
// Browser.ExecuteScriptAsync(script);
// }
// else
// {
// Browser.Dispatcher.Invoke(
// () =>
// {
// return Browser.ExecuteScriptAsync(script);
// },
// DispatcherPriority.Background
// );
// }
// }
public async Task ExecuteScriptAsyncMethod(string script, CancellationToken cancellationToken)
{
if (!Browser.IsInitialized)
{
throw new InvalidOperationException("Failed to execute script, Webview2 is not initialized yet.");
}
var callbackTask = await Browser
.Dispatcher.InvokeAsync(
async () => await Browser.ExecuteScriptAsync(script).ConfigureAwait(false),
DispatcherPriority.Background,
cancellationToken
)
.Task.ConfigureAwait(false);
_ = await callbackTask.ConfigureAwait(false);
}
private void OnInitialized(object? sender, CoreWebView2InitializationCompletedEventArgs e)
{
if (!e.IsSuccess)
{
throw new InvalidOperationException("Webview Failed to initialize", e.InitializationException);
}
// We use Lazy here to delay creating the binding until after the Browser is fully initialized.
// Otherwise the Browser cannot respond to any requests to ExecuteScriptAsyncMethod
foreach (var binding in _serviceProvider.GetRequiredService<IEnumerable<IBinding>>())
{
SetupBinding(binding);
}
}
/// <remark>
/// This must be called on the Main thread
/// </remark>
private void SetupBinding(IBinding binding)
{
binding.Parent.AssociateWithBinding(binding);
Browser.CoreWebView2.AddHostObjectToScript(binding.Name, binding.Parent);
}
public void ShowDevTools() => Browser.CoreWebView2.OpenDevToolsWindow();
//https://github.com/MicrosoftEdge/WebView2Feedback/issues/2161
public void Dispose() => Browser.Dispatcher.Invoke(() => Browser.Dispose(), DispatcherPriority.Send);
}