簡體   English   中英

WPF Web 瀏覽器控制和 DPI 縮放

[英]WPF Web Browser Control and DPI Scaling

我正在使用一個使用 Web 瀏覽器控件的 WPF 應用程序,但我遇到了高 DPI 縮放問題。

看起來 Web 瀏覽器控件沒有正確遵守系統的 DPI 設置,而 WPF 應用程序的其余部分正在正確縮放 UI。 這意味着在更高的比例級別上,WPF 界面會變得更大,而 Web 瀏覽器內容保持原來的外觀,現在看起來更小。

下面是使用兩個 Web 瀏覽器控件的 WPF 應用程序的屏幕截圖示例。

100% 縮放:

在此處輸入圖片說明

150% 縮放:

在此處輸入圖片說明

請注意,相對於主表單內容(工具欄/菜單/狀態欄),第二張圖片中的 Web 瀏覽器縮放比例比第一張圖片小得多。

是否有某種方法可以強制 Web 瀏覽器控件正確使用從應用程序繼承的高 DPI 設置?

此 MSDN 鏈接:解決 DPI 問題

顯示了一個非常低級的方法(在文檔底部)實現自定義 Web 瀏覽器 COM 接口,但我想知道是否有更簡潔的方法來解決這個問題。

我發現了我認為實現所需功能的最佳方法(前提是您無論如何都需要在注冊表中指定FEATURE_BROWSER_EMULATION以使用最新的 IE 版本)

您需要做的就是在HKCU\\Software\\Microsoft\\Internet Explorer\\Main\\FeatureControl創建一個名為FEATURE_96DPI_PIXEL的新鍵,並在其中添加類型為DWORD (32-bit)可執行條目,以應用程序 exe 作為鍵名和1值。

在實際實例化 WebBrowser 組件之前檢查應用程序啟動時的設置,你應該沒問題。

原始帖子(具有其他可能的功能): https : //www.reddit.com/r/dotnet/comments/2j5m6m/wpf_webbrowser_control_alternatives/

下面是一個實用程序類的代碼,它允許您停用 WPF 的 WebBrowser 上下文菜單。 它還允許您抑制腳本錯誤( WPF WebBrowser 控件 - 如何抑制腳本錯誤?)並更改 IE 的DOCHOSTUIFLAG

使用示例:

public partial class Player : Window
{
    private WebBrowserHostUIHandler _wbHandler;

    public Player()
    {
        InitializeComponent();
        ...
        _wbHandler = new WebBrowserHostUIHandler(MyWebBrower);
        _wbHandler.IsWebBrowserContextMenuEnabled = true;
    }
}

實用程序代碼:

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
}

我的解決方案最終是使用更高版本的 .NET - 4.6.2 改進了 DPI 支持,因此當在應用程序清單中應用高 DPI 設置時,我提到的應用程序中的這個問題會自行解決。

如果您的目標是 .NET 4.6.2 或更高版本,則隱式啟用 DPI 縮放。 你不應該需要其他任何東西。

如果您的目標是早期版本,請添加到清單中:

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

並在您的AssemblyInfo.cs啟用 DPI Awareness:

[assembly: DisableDpiAwareness]

或者(正如我出於各種原因所做的那樣)使用必須從app.xaml.cs調用的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;
        }            
    }

要在 App.xaml.cs 啟動代碼中的某處調用:

        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 */ }

在面向 .NET 4.6.2 或更高版本之后,所有這些都不再需要,並且一切正常。 顯式代碼允許對使用的配置文件進行更多控制。

.NET 4.6.2 引入了大量 DPI 縮放改進,包括多顯示器縮放支持(以前只支持主顯示器),並且自動對大多數托管控件(包括 Web 瀏覽器控件)執行正確的操作。 鑒於現在大多數機器都基於 .NET 4.7.x 或 4.6.2,基於面向 4.6.2 的 Windows 更新應該被視為 WPF 恕我直言的基准。

注意:如果您在應用程序運行時在 Windows 中切換 DPI 設置,您還可以捕獲一些事件,這些事件會告訴您 DPI 更改。 除了重新啟動之外,您可以做的不多,因為應用程序實際上並沒有接受更改,但至少您可以讓用戶知道 DPI 已更改,他們必須重新啟動以適應新的 DPI 設置。

您需要根據操作系統 dpi 確定瀏覽器縮放百分比,然后通過其 ActiveX 包裝器設置瀏覽器縮放比例。 不幸的是,WPF 中沒有公開包裝器,因此您需要添加對“Microsoft Internet Controls”的引用並使用反射來獲取它。

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

補充閱讀:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM