WPF Web Browser Control and DPI Scaling

后端 未结 4 1806
星月不相逢
星月不相逢 2021-02-07 06:50

I\'m working with a WPF application that uses the Web Browser control and I\'m having issues with High DPI scaling.

It looks like the Web Browser control is not properl

相关标签:
4条回答
  • 2021-02-07 07:05

    I have found what I think is the best way to achieve the required functionality (provided, that you anyway need to specify FEATURE_BROWSER_EMULATION in registry to use most recent IE version).

    All you need to do, is to create a new key in HKCU\Software\Microsoft\Internet Explorer\Main\FeatureControl named FEATURE_96DPI_PIXEL and add your executable entry there of type DWORD (32-bit), with application exe as a key name and value of 1.

    Check the setting on startup of the application, before actually instantiating the WebBrowser component and you should be fine.

    Original post (with other possible features): https://www.reddit.com/r/dotnet/comments/2j5m6m/wpf_webbrowser_control_alternatives/

    0 讨论(0)
  • 2021-02-07 07:18

    The solution for me eventually was to use a later version of .NET - 4.6.2 has improved DPI support and so this issue in the application I mentinoed resolved itself when High DPI settings are applied in the application manifest.

    If you're targeting .NET 4.6.2 or later DPI Scaling is implicitly enabled. You shouldn't need anything else.

    If you target earlier versions either add to the manifest:

    <application xmlns="urn:schemas-microsoft-com:asm.v3">
        <windowsSettings>
          <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
        </windowsSettings>
    </application>
    

    and enable DPI Awareness in your AssemblyInfo.cs:

    [assembly: DisableDpiAwareness]
    

    or (as I was doing for various reasons) using code that has to be call from app.xaml.cs:

        public static bool SetPerMonitorDpiAwareness(ProcessDpiAwareness type = ProcessDpiAwareness.Process_Per_Monitor_DPI_Aware)
        {
            try
            {
                // for this to work make sure [assembly: DisableDpiAwareness]
                ProcessDpiAwareness awarenessType;
                GetProcessDpiAwareness(Process.GetCurrentProcess().Handle, out awarenessType);
                var result = SetProcessDpiAwareness(type);
                GetProcessDpiAwareness(Process.GetCurrentProcess().Handle, out awarenessType);
    
                return awarenessType == type;
            }
            catch
            {
                return false;
            }            
        }
    

    To call somewhere in App.xaml.cs startup code:

            try
            {   // Multi-Monitor DPI awareness for screen captures
                // requires [assembly: DisableDpiAwareness] set in assemblyinfo
                bool res = WindowUtilities.SetPerMonitorDpiAwareness(ProcessDpiAwareness.Process_Per_Monitor_DPI_Aware);
            }
            catch {  /* fails not supported on Windows 7 and older */ }
    

    Again all of this was no longer necessary after targeting .NET 4.6.2 or later and things just work. The explicit code allowed more control over exactly what profile to use.

    .NET 4.6.2 introduces a host of improvements with DPI scaling including multi-monitor scaling support (previously only the main monitor was supported) and is automatically doing the right thing with most hosted controls including the Web browser control. Given that most machines these days are either on .NET 4.7.x or 4.6.2 based on Windows update targeting 4.6.2 should be considered a baseline for WPF IMHO.

    Note: If you switch DPI settings in Windows while your app is running, there are also events you can trap that will tell you of the DPI change. Not much that you can do with this other than restart as the app doesn't actually pick up the changes but at least you can let the user know that the DPI has changed and they have to restart to adjust to the new DPI settings.

    0 讨论(0)
  • 2021-02-07 07:21

    You need to determine the browser zoom percentage from the OS dpi, then set the browser zoom via its ActiveX wrapper. Unfortunately, the wrapper is not exposed in WPF so you'll need to add a reference to "Microsoft Internet Controls" and use reflection to get it.

    Private Sub Browser_LoadCompleted(sender As Object, e As NavigationEventArgs)
        Dim source = PresentationSource.FromDependencyObject(sender)
        Dim matrix As Matrix = source.CompositionTarget.TransformToDevice
        If matrix.Determinant > 1 Then
            Dim zoomLevel As Integer = matrix.Determinant * 100
            Dim ie As SHDocVw.InternetExplorer = GetType(WebBrowser).GetField("_axIWebBrowser2", BindingFlags.Instance Or BindingFlags.NonPublic).GetValue(sender)
            ie.ExecWB(SHDocVw.OLECMDID.OLECMDID_OPTICAL_ZOOM, SHDocVw.OLECMDEXECOPT.OLECMDEXECOPT_DONTPROMPTUSER, zoomLevel, IntPtr.Zero)
        End If
    End Sub
    

    Additional reading:

    https://dzimchuk.net/best-way-to-get-dpi-value-in-wpf/

    https://weblog.west-wind.com/posts/2016/Aug/22/Detecting-and-Setting-Zoom-Level-in-the-WPF-WebBrowser-Control

    0 讨论(0)
  • 2021-02-07 07:22

    Here is the code of a utility class that allows you to deactivate WPF's WebBrowser context menu. It also allows you to suppress script errors (WPF WebBrowser control - how to supress script errors?) and change IE's DOCHOSTUIFLAG.

    Usage sample:

    public partial class Player : Window
    {
        private WebBrowserHostUIHandler _wbHandler;
    
        public Player()
        {
            InitializeComponent();
            ...
            _wbHandler = new WebBrowserHostUIHandler(MyWebBrower);
            _wbHandler.IsWebBrowserContextMenuEnabled = true;
        }
    }
    

    Utility code:

    public class WebBrowserHostUIHandler : Native.IDocHostUIHandler
    {
        private const uint E_NOTIMPL = 0x80004001;
        private const uint S_OK = 0;
        private const uint S_FALSE = 1;
    
        public WebBrowserHostUIHandler(WebBrowser browser)
        {
            if (browser == null)
                throw new ArgumentNullException("browser");
    
            Browser = browser;
            browser.LoadCompleted += OnLoadCompleted;
            browser.Navigated += OnNavigated;
            IsWebBrowserContextMenuEnabled = true;
            Flags |= HostUIFlags.ENABLE_REDIRECT_NOTIFICATION;
        }
    
        public WebBrowser Browser { get; private set; }
        public HostUIFlags Flags { get; set; }
        public bool IsWebBrowserContextMenuEnabled { get; set; }
        public bool ScriptErrorsSuppressed { get; set; }
    
        private void OnNavigated(object sender, NavigationEventArgs e)
        {
            SetSilent(Browser, ScriptErrorsSuppressed);
        }
    
        private void OnLoadCompleted(object sender, NavigationEventArgs e)
        {
            Native.ICustomDoc doc = Browser.Document as Native.ICustomDoc;
            if (doc != null)
            {
                doc.SetUIHandler(this);
            }
        }
    
        uint Native.IDocHostUIHandler.ShowContextMenu(int dwID, Native.POINT pt, object pcmdtReserved, object pdispReserved)
        {
            return IsWebBrowserContextMenuEnabled ? S_FALSE : S_OK;
        }
    
        uint Native.IDocHostUIHandler.GetHostInfo(ref Native.DOCHOSTUIINFO info)
        {
            info.dwFlags = (int)Flags;
            info.dwDoubleClick = 0;
            return S_OK;
        }
    
        uint Native.IDocHostUIHandler.ShowUI(int dwID, object activeObject, object commandTarget, object frame, object doc)
        {
            return E_NOTIMPL;
        }
    
        uint Native.IDocHostUIHandler.HideUI()
        {
            return E_NOTIMPL;
        }
    
        uint Native.IDocHostUIHandler.UpdateUI()
        {
            return E_NOTIMPL;
        }
    
        uint Native.IDocHostUIHandler.EnableModeless(bool fEnable)
        {
            return E_NOTIMPL;
        }
    
        uint Native.IDocHostUIHandler.OnDocWindowActivate(bool fActivate)
        {
            return E_NOTIMPL;
        }
    
        uint Native.IDocHostUIHandler.OnFrameWindowActivate(bool fActivate)
        {
            return E_NOTIMPL;
        }
    
        uint Native.IDocHostUIHandler.ResizeBorder(Native.COMRECT rect, object doc, bool fFrameWindow)
        {
            return E_NOTIMPL;
        }
    
        uint Native.IDocHostUIHandler.TranslateAccelerator(ref System.Windows.Forms.Message msg, ref Guid group, int nCmdID)
        {
            return S_FALSE;
        }
    
        uint Native.IDocHostUIHandler.GetOptionKeyPath(string[] pbstrKey, int dw)
        {
            return E_NOTIMPL;
        }
    
        uint Native.IDocHostUIHandler.GetDropTarget(object pDropTarget, out object ppDropTarget)
        {
            ppDropTarget = null;
            return E_NOTIMPL;
        }
    
        uint Native.IDocHostUIHandler.GetExternal(out object ppDispatch)
        {
            ppDispatch = Browser.ObjectForScripting;
            return S_OK;
        }
    
        uint Native.IDocHostUIHandler.TranslateUrl(int dwTranslate, string strURLIn, out string pstrURLOut)
        {
            pstrURLOut = null;
            return E_NOTIMPL;
        }
    
        uint Native.IDocHostUIHandler.FilterDataObject(IDataObject pDO, out IDataObject ppDORet)
        {
            ppDORet = null;
            return E_NOTIMPL;
        }
    
        public static void SetSilent(WebBrowser browser, bool silent)
        {
            Native.IOleServiceProvider sp = browser.Document as Native.IOleServiceProvider;
            if (sp != null)
            {
                Guid IID_IWebBrowserApp = new Guid("0002DF05-0000-0000-C000-000000000046");
                Guid IID_IWebBrowser2 = new Guid("D30C1661-CDAF-11d0-8A3E-00C04FC9E26E");
    
                object webBrowser;
                sp.QueryService(ref IID_IWebBrowserApp, ref IID_IWebBrowser2, out webBrowser);
                if (webBrowser != null)
                {
                    webBrowser.GetType().InvokeMember("Silent", BindingFlags.Instance | BindingFlags.Public | BindingFlags.PutDispProperty, null, webBrowser, new object[] { silent });
                }
            }
        }
    }
    
    internal static class Native
    {
        [ComImport, Guid("BD3F23C0-D43E-11CF-893B-00AA00BDCE1A"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        internal interface IDocHostUIHandler
        {
            [PreserveSig]
            uint ShowContextMenu(int dwID, POINT pt, [MarshalAs(UnmanagedType.Interface)] object pcmdtReserved, [MarshalAs(UnmanagedType.Interface)] object pdispReserved);
    
            [PreserveSig]
            uint GetHostInfo(ref DOCHOSTUIINFO info);
    
            [PreserveSig]
            uint ShowUI(int dwID, [MarshalAs(UnmanagedType.Interface)] object activeObject, [MarshalAs(UnmanagedType.Interface)] object commandTarget, [MarshalAs(UnmanagedType.Interface)] object frame, [MarshalAs(UnmanagedType.Interface)] object doc);
    
            [PreserveSig]
            uint HideUI();
    
            [PreserveSig]
            uint UpdateUI();
    
            [PreserveSig]
            uint EnableModeless(bool fEnable);
    
            [PreserveSig]
            uint OnDocWindowActivate(bool fActivate);
    
            [PreserveSig]
            uint OnFrameWindowActivate(bool fActivate);
    
            [PreserveSig]
            uint ResizeBorder(COMRECT rect, [MarshalAs(UnmanagedType.Interface)] object doc, bool fFrameWindow);
    
            [PreserveSig]
            uint TranslateAccelerator(ref System.Windows.Forms.Message msg, ref Guid group, int nCmdID);
    
            [PreserveSig]
            uint GetOptionKeyPath([Out, MarshalAs(UnmanagedType.LPArray)] string[] pbstrKey, int dw);
    
            [PreserveSig]
            uint GetDropTarget([In, MarshalAs(UnmanagedType.Interface)] object pDropTarget, [MarshalAs(UnmanagedType.Interface)] out object ppDropTarget);
    
            [PreserveSig]
            uint GetExternal([MarshalAs(UnmanagedType.IDispatch)] out object ppDispatch);
    
            [PreserveSig]
            uint TranslateUrl(int dwTranslate, [MarshalAs(UnmanagedType.LPWStr)] string strURLIn, [MarshalAs(UnmanagedType.LPWStr)] out string pstrURLOut);
    
            [PreserveSig]
            uint FilterDataObject(IDataObject pDO, out IDataObject ppDORet);
        }
    
        [StructLayout(LayoutKind.Sequential)]
        internal struct DOCHOSTUIINFO
        {
            public int cbSize;
            public int dwFlags;
            public int dwDoubleClick;
            public IntPtr dwReserved1;
            public IntPtr dwReserved2;
        }
    
        [StructLayout(LayoutKind.Sequential)]
        internal struct COMRECT
        {
            public int left;
            public int top;
            public int right;
            public int bottom;
        }
    
        [StructLayout(LayoutKind.Sequential)]
        internal class POINT
        {
            public int x;
            public int y;
        }
    
        [ComImport, Guid("3050F3F0-98B5-11CF-BB82-00AA00BDCE0B"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        internal interface ICustomDoc
        {
            [PreserveSig]
            int SetUIHandler(IDocHostUIHandler pUIHandler);
        }
    
        [ComImport, Guid("6D5140C1-7436-11CE-8034-00AA006009FA"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        internal interface IOleServiceProvider
        {
            [PreserveSig]
            uint QueryService([In] ref Guid guidService, [In] ref Guid riid, [MarshalAs(UnmanagedType.IDispatch)] out object ppvObject);
        }
    }
    
    [Flags]
    public enum HostUIFlags
    {
        DIALOG = 0x00000001,
        DISABLE_HELP_MENU = 0x00000002,
        NO3DBORDER = 0x00000004,
        SCROLL_NO = 0x00000008,
        DISABLE_SCRIPT_INACTIVE = 0x00000010,
        OPENNEWWIN = 0x00000020,
        DISABLE_OFFSCREEN = 0x00000040,
        FLAT_SCROLLBAR = 0x00000080,
        DIV_BLOCKDEFAULT = 0x00000100,
        ACTIVATE_CLIENTHIT_ONLY = 0x00000200,
        OVERRIDEBEHAVIORFACTORY = 0x00000400,
        CODEPAGELINKEDFONTS = 0x00000800,
        URL_ENCODING_DISABLE_UTF8 = 0x00001000,
        URL_ENCODING_ENABLE_UTF8 = 0x00002000,
        ENABLE_FORMS_AUTOCOMPLETE = 0x00004000,
        ENABLE_INPLACE_NAVIGATION = 0x00010000,
        IME_ENABLE_RECONVERSION = 0x00020000,
        THEME = 0x00040000,
        NOTHEME = 0x00080000,
        NOPICS = 0x00100000,
        NO3DOUTERBORDER = 0x00200000,
        DISABLE_EDIT_NS_FIXUP = 0x00400000,
        LOCAL_MACHINE_ACCESS_CHECK = 0x00800000,
        DISABLE_UNTRUSTEDPROTOCOL = 0x01000000,
        HOST_NAVIGATES = 0x02000000,
        ENABLE_REDIRECT_NOTIFICATION = 0x04000000,
        USE_WINDOWLESS_SELECTCONTROL = 0x08000000,
        USE_WINDOWED_SELECTCONTROL = 0x10000000,
        ENABLE_ACTIVEX_INACTIVATE_MODE = 0x20000000,
        DPI_AWARE = 0x40000000
    }
    
    0 讨论(0)
提交回复
热议问题