Skip to content

Instantly share code, notes, and snippets.

@Andreas-Hjortland
Last active February 25, 2020 08:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Andreas-Hjortland/94e55b1ce95cc78693b99e7f53c34366 to your computer and use it in GitHub Desktop.
Save Andreas-Hjortland/94e55b1ce95cc78693b99e7f53c34366 to your computer and use it in GitHub Desktop.
A Web View control that will work on older windows versions as well as with windows 10 in administrator mode by falling back to using a WebBrowser control (IE). You use it the same way you are using `WebViewCompatible`, but I have added `ExampleUsage.xaml` to show how it can be used.
using System;
using System.Reflection;
using System.Security.Principal;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using Microsoft.Toolkit.UI.Controls;
using Microsoft.Toolkit.Win32.UI.Controls.Interop.WinRT;
using Microsoft.Toolkit.Wpf.UI.Controls;
namespace Custom
{
/// <summary>
/// This is based on the implementation of <c>WebViewCompatible.cs</c>
/// (see <see cref="https://github.com/windows-toolkit/Microsoft.Toolkit.Win32/blob/master/Microsoft.Toolkit.Wpf.UI.Controls.WebView/WebViewCompatible.cs"/>).
/// We're using reflection to get to the internal types so that we can keep
/// as true to the original implementation as possible without cloning the
/// whole repository and working in it, but if we are unable to resolve the
/// issue (<see cref="https://github.com/windows-toolkit/Microsoft.Toolkit.Win32/issues/13"/>)
/// this might be a good enough workaround that is preferable to the
/// application not working when running with administrator privileges.
/// </summary>
/// <remarks>
/// Since we are using reflection, there is a small performance penalty to
/// using this class, but we are caching the reflected types and values, so
/// the cost of reflection should be negligible for most applications
/// </remarks>
public class AdminWebViewCompatible : UserControl, IWebViewCompatible, IDisposable
{
private static bool IsAdministrator()
{
var identity = WindowsIdentity.GetCurrent();
var principal = new WindowsPrincipal(identity);
return principal.IsInRole(WindowsBuiltInRole.Administrator);
}
private readonly static Lazy<bool> WebViewIsSupported = new Lazy<bool>(() =>
{
if (IsAdministrator())
{
return false;
}
var type = typeof(ApiInformationExtensions).Assembly.GetType("Microsoft.Toolkit.Win32.UI.Controls.Interop.WinRT.WebViewControlHost");
var isSupported = (bool)type.GetProperty("IsSupported", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null);
return isSupported;
});
private static Lazy<Func<IWebViewCompatibleAdapter>> BuildCompatibilityAdapter(string name)
{
return new Lazy<Func<IWebViewCompatibleAdapter>>(() => {
var type = typeof(IWebViewCompatibleAdapter).Assembly.GetType(name);
ConstructorInfo ctor = type.GetConstructor(new Type[0]);
return () => (IWebViewCompatibleAdapter)ctor.Invoke(new object[0]);
});
}
private readonly static Lazy<Func<IWebViewCompatibleAdapter>> WebViewCompatibilityAdapter = BuildCompatibilityAdapter("Microsoft.Toolkit.Wpf.UI.Controls.WebViewCompatibilityAdapter");
private readonly static Lazy<Func<IWebViewCompatibleAdapter>> WebBrowserCompatibilityAdapter = BuildCompatibilityAdapter("Microsoft.Toolkit.Wpf.UI.Controls.WebBrowserCompatibilityAdapter");
public static DependencyProperty SourceProperty { get; } = DependencyProperty.Register(nameof(Source), typeof(Uri), typeof(AdminWebViewCompatible));
public AdminWebViewCompatible() : base()
{
if (WebViewIsSupported.Value)
{
_implementation = WebViewCompatibilityAdapter.Value();
}
else
{
_implementation = WebBrowserCompatibilityAdapter.Value();
}
_implementation.Initialize();
AddChild(_implementation.View);
_implementation.View.BeginInit();
_implementation.View.EndInit();
var binder = new Binding()
{
Source = _implementation,
Path = new PropertyPath(nameof(Source)),
Mode = BindingMode.TwoWay
};
BindingOperations.SetBinding(this, SourceProperty, binder);
}
#region Copy/paste from https://github.com/windows-toolkit/Microsoft.Toolkit.Win32/blob/master/Microsoft.Toolkit.Wpf.UI.Controls.WebView/WebViewCompatible.cs
~AdminWebViewCompatible()
{
Dispose(false);
}
private IWebViewCompatibleAdapter _implementation;
public Uri Source { get => (Uri)GetValue(SourceProperty); set => SetValue(SourceProperty, value); }
public bool CanGoBack => _implementation.CanGoBack;
public bool CanGoForward => _implementation.CanGoForward;
public FrameworkElement View { get => _implementation.View; }
public event EventHandler<WebViewControlNavigationStartingEventArgs> NavigationStarting { add => _implementation.NavigationStarting += value; remove => _implementation.NavigationStarting -= value; }
public event EventHandler<WebViewControlContentLoadingEventArgs> ContentLoading { add => _implementation.ContentLoading += value; remove => _implementation.ContentLoading -= value; }
public event EventHandler<WebViewControlNavigationCompletedEventArgs> NavigationCompleted { add => _implementation.NavigationCompleted += value; remove => _implementation.NavigationCompleted -= value; }
public bool GoBack() => _implementation.GoBack();
public bool GoForward() => _implementation.GoForward();
public void Navigate(Uri url) => _implementation.Navigate(url);
#pragma warning disable CA2234 // Pass system uri objects instead of strings
public void Navigate(string url) => _implementation.Navigate(url);
#pragma warning restore CA2234 // Pass system uri objects instead of strings
public void NavigateToString(string text) => _implementation.NavigateToString(text);
public void Refresh() => _implementation.Refresh();
public void Stop() => _implementation.Stop();
public string InvokeScript(string scriptName) => _implementation.InvokeScript(scriptName);
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool isDisposing)
{
if (isDisposing)
{
if (_implementation is IDisposable disposable)
{
disposable.Dispose();
}
}
}
#endregion
}
}
<Window
...
xmlns:local="clr-namespace:Custom"
...
>
...
<local:AdminWebViewCompatible Source="{Binding source}" />
...
</Window>
@Andreas-Hjortland
Copy link
Author

Note that I am using reflection to access internal methods and properties, so this might break at runtime when updating the nuget package for Microsoft.Toolkit.Win32 even on minor or patch revisions so I cannot give any guarantees that this will not stop working.

@Andreas-Hjortland
Copy link
Author

Another small trick I used to ensure that the app runs in IE11 mode when it falls back to Web Browser controls is that I run this code in the entry point of the application (App.xaml.cs constructor)

var appPath = System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName;
Registry.SetValue(
    keyName: @"HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION",
    valueName: Path.GetFileName(appPath),
    value: 11001,
    valueKind: RegistryValueKind.DWord
);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment