I'm working on a cross platform application on Xamarin and more particularly on the UWP side with the HoloLens2.
Since some months ago, some web pages doesn't display (infinite load) with WebView.
From what I understand, the WebView which is use on UWP is based on the Edge pre-Chronium so it can explain why those pages doesn't load anymore.
In order to fix that, I try to change the renderer from Windows.UI.Xaml.Controls.WebView into WebView2 by using Microsoft.UI.Xaml.Controls.
I did some tests on PC and everything works fine but when I tested it on HoloLens2, I had a error while WebView2 initializes CoreWebView2 :
=> Class not registered
Is it possible to have WebView2 working on HoloLens2 with Xamarin ?
There is my code of the renderer for WebView2 on UWP
using System;
using System.ComponentModel;
using SvMobile.CustomControls;
using SvMobile.Lib.UWP.Renderers;
using SvMobile.Pages.WebVue;
using Xamarin.Forms;
using Xamarin.Forms.Platform.UWP;
using System.Collections.Generic;
using Size = Xamarin.Forms.Size;
using WebView2 = Microsoft.UI.Xaml.Controls.WebView2;
using Net.Shared.Helpers;
[assembly: ExportRenderer(typeof(SvWebView), typeof(SvWebViewRenderer))]
namespace SvMobile.Lib.UWP.Renderers
{
/// <summary>
/// Code is partially taken from:
/// https://github.com/XLabs/Xamarin-Forms-Labs/wiki/HybridWebView
/// </summary>
public class SvWebViewRenderer : ViewRenderer<SvWebView, WebView2>
{
private bool disposed = false;
public override SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
{
return new SizeRequest(Size.Zero, Size.Zero);
}
protected async override void OnElementChanged(ElementChangedEventArgs<SvWebView> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
Unbind(e.OldElement);
HandleCleanup();
}
if (e.NewElement == null)
return;
if (Control == null)
{
var webView = new WebView2();
await webView.EnsureCoreWebView2Async();
SetNativeControl(webView);
}
Bind();
}
private void Bind()
{
if (Element == null) return;
if (Element.Uri != null)
{
Load(Element.Uri);
}
else
{
LoadSource();
}
// There should only be one renderer and thus only one event handler registered.
// Otherwise, when Xamarin creates a new renderer, the old one stays attached
// and crashes when called!
Element.JavaScriptLoadRequested = OnInjectRequest;
Element.LoadFromContentRequested = LoadFromContent;
Element.LoadContentRequested = LoadContent;
//Element.Navigating += OnNavigating;
Element.PropertyChanged += OnElementPropertyChanged;
}
private void Unbind(SvWebView oldElement)
{
if (oldElement == null) return;
oldElement.JavaScriptLoadRequested -= OnInjectRequest;
oldElement.LoadFromContentRequested -= LoadFromContent;
oldElement.LoadContentRequested -= LoadContent;
//oldElement.Navigating -= OnNavigating;
oldElement.PropertyChanged -= OnElementPropertyChanged;
}
private void HandleCleanup()
{
if (Control == null)
return;
Messager.LogTrace("[SvWebViewRenderer]", $"Stop Loading");
Control.CoreWebView2.Stop();
//Control.StopLoading();
Control.NavigateToString("about:blank");
Messager.LogTrace("[SvWebViewRenderer]", $"Destroy web view");
//Control.Dispose(); /// JS: ???? --> Control = null ???
}
protected override void Dispose(bool disposing)
{
disposed = true;
/*if (disposing)
{
((SvWebView)Element).Cleanup();
}*/
base.Dispose(disposing);
}
private async void Load(Uri uri, IDictionary<string, string> extraHeaders = null)
{
if (uri == null)
return;
if (Control.CoreWebView2 == null)
await Control.EnsureCoreWebView2Async();
var requestMsg = new Windows.Web.Http.HttpRequestMessage(Windows.Web.Http.HttpMethod.Get, uri);
Control.CoreWebView2.AddWebResourceRequestedFilter(uri.OriginalString, Microsoft.Web.WebView2.Core.CoreWebView2WebResourceContext.All);
if (extraHeaders != null)
{
foreach (var extraHeader in extraHeaders)
{
requestMsg.Headers.Add(extraHeader.Key, extraHeader.Value);
Control.CoreWebView2.WebResourceRequested += (obj, args) =>
{
args.Request.Headers.SetHeader(extraHeader.Key, extraHeader.Value);
};
}
}
Device.BeginInvokeOnMainThread(() =>
{
if (disposed)
return;
//Control?.NavigateWithHttpRequestMessage(requestMsg);
Control?.CoreWebView2.Navigate(uri.OriginalString);
Control?.NavigateToString(uri.OriginalString);
});
}
private void LoadFromString(string html)
{
Device.BeginInvokeOnMainThread(() =>
{
if (disposed)
return;
Control?.NavigateToString(html);
});
}
private void LoadSource()
{
if (Element == null) return;
if (Element.Source is HtmlWebViewSource htmlSource && htmlSource.Html != null)
{
LoadFromString(htmlSource.Html);
return;
}
if (Element.Source is SvUrlWebViewSource svWebViewSource && svWebViewSource.Url != null)
{
Load(new Uri(svWebViewSource.Url), svWebViewSource.Headers);
return;
}
if (!(Element.Source is UrlWebViewSource webViewSource)) return;
Load(new Uri(webViewSource.Url));
}
private void LoadFromContent(object sender, string contentFullName)
{
//Element.Uri = new Uri(NSBundle.MainBundle.BundlePath + "/" + contentFullName);
}
private void LoadContent(object sender, string contentFullName)
{
//Control.LoadHtmlString(new NSString(contentFullName), new NSUrl(NSBundle.MainBundle.BundlePath, true));
}
private void OnInjectRequest(object sender, string script)
{
Inject(script);
}
private void Inject(string script)
{
//Log.Warn("SvWebViewRenderer", "Inject script: {0}", script);
Device.BeginInvokeOnMainThread(() =>
{
if (disposed)
return;
Control?.NavigateToString($"javascript: {script}");
});
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
Messager.LogTrace("[SvWebViewRenderer]", $"OnElementPropertyChanged; Control={Control != null}; Element={Element != null}");
base.OnElementPropertyChanged(sender, e);
// Xamarin will changed the renderer attached to a view so it is possible that
// an old renderer gets a property updated. In this case the Element will be null.
// In that case, try to clear the property event handler and exit.
if (Element == null)
{
if (sender is SvWebView wv) Unbind(wv);
return;
}
if (e.PropertyName == SvWebView.UriProperty.PropertyName)
{
Load(Element.Uri);
}
else if (e.PropertyName == SvWebView.SourceProperty.PropertyName)
{
LoadSource();
}
}
}
}
Thanks in advance and have a good day !