Last active
February 25, 2020 08:49
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<Window | |
... | |
xmlns:local="clr-namespace:Custom" | |
... | |
> | |
... | |
<local:AdminWebViewCompatible Source="{Binding source}" /> | |
... | |
</Window> |
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
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.