0

我们有一个适用于经典 .NET Framework 4.7 的 WinForms 应用程序。其中一个表单包含一个WebBrowser控件的实例,用于呈现 HTML 文档。事实证明,如果有人在 Visual Studio 的 Microsoft Help Viewer 中更改了 Text Size 选项

Microsoft 帮助查看器中的文本大小选项

,这也会影响我们基于 WebBrowser 的查看器中的文本大小。

我怀疑这是 WebBrowser 控件所基于的 MSIE 渲染引擎的全局设置。因此,用户可能会在其他应用程序中更改此设置,这将影响我们的应用程序。

有没有办法在渲染我们的 HTML 时完全忽略这个设置?我们尝试为 HTML 页面中的 HTML 标记指定明确的文本大小,但这似乎没有帮助。看起来 MSIE HTML 渲染器在使用指定的文本大小渲染页面后应用其比例因子。

如果无法忽略该全局设置,是否可以使用 API 来从我们的应用程序控制此文本大小参数?我们可以使用它来提供像 Microsoft Help Viewer 那样的设置,并且至少可以为用户提供一个类似的选择列表来调整文本大小以方便阅读。

如果可能,请在 C# 或 VB.NET 中提供解决方案。

4

2 回答 2

1

关于将 WebBrowser 控件的视口比例(缩放和文本大小)与其他应用程序(例如 Internet Explorer 或本例中的帮助查看器)应用的设置同步的示例。

问题:

  • 当使用 Internet Explorer 引擎呈现 HTML 内容的应用程序应用 Zoom a FontSize 缩放选项时,WebBrowser 控件在首次初始化时会将这些设置应用于其视图。

预期行为:

  • 应用程序应提供更改缩放和字体大小比例的方法,与其他应用程序可能设置的内容无关。
  • 应该应用新设置而不重新创建控件的句柄

在示例代码中,一个专用类WebBrowserHelper包含一个从注册表读取当前缩放和字体大小设置的方法,以将 MenuItems 更新为当前值:

HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\Zoom
=> ZoomFactor Key

HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\International\Scripts\3
=> IEFontSize Key

ZoomFactor键存储缩放级别乘以1000:例如,缩放的150%值为15000075%缩放的值为75000

' WebBrowserHelpersSetZoom()方法使用 WebBrowser ActiveX 实例设置缩放级别,调用其ExecWb 方法,将值作为OLECMDID参数传递,参数设置为,参数(缩放值)设置为指定缩放级别的整数值.OLECMDID_OPTICAL_ZOOMOLECMDEXECOPTOLECMDEXECOPT_DONTPROMPTUSERpvaIn

▶ 这也会更新ZoomFactor注册表项。

public static void SetZoom(WebBrowser browser, int zoomValue)
{
    dynamic activex = browser.ActiveXInstance;
    activex.ExecWB(OLECMDID_OPTICAL_ZOOM, OLECMDEXECOPT_DONTPROMPTUSER, zoomValue, 0);
}

和Keys 存储一个二进制值IEFontSizeIEFontSizePrivate在 rage 中[0-4]。这些值大致对应于x-small, small, medium, large, x-largeCSS 设置。
使用乘数将FontSize比例应用于文档正文:[Value + 1] * 4

SetTextScale()方法使用 WebBrowser ActiveX 实例设置 FonSize 比例,调用其ExecWb()方法,将值作为OLECMDID参数传递,参数设置为,并且参数(字体比例值)设置为指定范围内的整数值。OLECMDID_ZOOMOLECMDEXECOPTOLECMDEXECOPT_DONTPROMPTUSERpvaIn[0, 4]

▶ 这也会更新IEFontSize注册表项。

public static void SetTextScale(WebBrowser browser, int textValue)
{
    dynamic activex = browser.ActiveXInstance;
    activex.ExecWB(OLECMDID_ZOOM, OLECMDEXECOPT_DONTPROMPTUSER, textValue, 0);
}

另一种选择是在 WebBrowser会话开始之前将 Zoom 和 FontSize 重置为默认值。
您可以调用该WebBrowserHelper.InternetResetZoomAndFont()方法。将两个参数都设置为true,它将缩放级别重置为 ,100%并将 FontSize 重置为Medium

WebBrowserHelper.InternetResetZoomAndFont(true, true);

▶ 在这里,我添加了一些 ToolStripMenuItems,它们允许设置缩放值和 FontSize - 设置为预定义的比例 - 由 Internet Explorer 11 应用。

  • 所有设置缩放的 ToolStripMenuItem 都分组在一个集合中,名为zoomMenuItems. 都使用相同的Click事件处理程序,zoomToolStripMenuItems_Click
  • 所有设置 FontSize 比例的 ToolStripMenuItem 都分组在一个名为 的集合中textMenuItems。都使用相同的Click事件处理程序,textToolStripMenuItems_Click
  • Zoom 和 Fontscale 值设置为Tag每个 ToolStripMenuItem 的属性(您当然可以使用任何其他方式将值与特定 ToolStripMenuItem 关联)
  • 存储在命名值元组中的 Zoom 和 FontSize 的当前值,browserViewSettings

假设 WebBrowser 控件名为webBrowser1

public partial class SomeForm : Form
{
    private List<ToolStripMenuItem> textMenuItems = null;
    private List<ToolStripMenuItem> zoomMenuItems = null;
    private (int Zoom, int TextSize) browserViewSettings = (100, 2);

    public SomeForm()
    {
        InitializeComponent();

        textMenuItems = new List<ToolStripMenuItem> { textSmallestMenuItem, textSmallerMenuItem, textMediumMenuItem, textLargerMenuItem, textLargestMenuItem };
        zoomMenuItems = new List<ToolStripMenuItem> { zoom75MenuItem, zoom100MenuItem, zoom125MenuItem, zoom150MenuItem, zoom175MenuItem, zoom200MenuItem, zoom250MenuItem, zoom300MenuItem, zoom400MenuItem };
        // On startup, reads the current settings from the Registry
        // and updates the MenuItems, to reflect the current values
        UpdateMenus(true);
    }

    // MenuItems that sets the Font scale value
    private void textToolStripMenuItems_Click(object sender, EventArgs e)
    {
        var item = sender as ToolStripMenuItem;
        int newTextValue = Convert.ToInt32(item.Tag);
        if (newTextValue != browserViewSettings.TextSize) {
            browserViewSettings.TextSize = newTextValue;
            UpdateDocumentSettings(this.webBrowser1);
            UpdateMenus(false);
        }
    }

    // MenuItems that sets the Zoom level value
    private void zoomToolStripMenuItems_Click(object sender, EventArgs e)
    {
        var item = sender as ToolStripMenuItem;
        int newZoomValue = Convert.ToInt32(item.Tag);
        if (newZoomValue != browserViewSettings.Zoom) {
            browserViewSettings.Zoom = newZoomValue;
            UpdateDocumentSettings(this.webBrowser1);
            UpdateMenus(false);
        }
    }

    // Sets the new selected values and the related MenuItem
    private void UpdateDocumentSettings(WebBrowser browser)
    {
        if (browser == null || browser.Document == null) return;
        WebBrowserFeatures.WebBrowserHelper.SetZoom(browser, browserViewSettings.Zoom);
        WebBrowserFeatures.WebBrowserHelper.SetTextScale(browser, browserViewSettings.TextSize);
    }

    // Updates the MenuItem to the current values 
    private void UpdateMenus(bool refresh)
    {
        if (refresh) {
            browserViewSettings = WebBrowserFeatures.WebBrowserHelper.InternetGetViewScale();
        }
        
        zoomMenuItems.ForEach(itm => {
            int refValue = Convert.ToInt32(itm.Tag);
            itm.Checked = refValue == browserViewSettings.Zoom;
            if (itm.Checked) {
                zoomToolStripMenuItem.Text = $"Zoom ({refValue}%)";
            }
        });
        textMenuItems.ForEach(itm => itm.Checked = Convert.ToInt32(itm.Tag) == browserViewSettings.TextSize);
    }

WebBrowserHelper班级:

using System.Security;
using System.Security.AccessControl;
using Microsoft.Win32;

public class WebBrowserHelper
{
    private const int OLECMDID_ZOOM = 19;
    private const int OLECMDID_OPTICAL_ZOOM = 63;
    private const int OLECMDEXECOPT_DONTPROMPTUSER = 2;

    // Applies the Zoom value. It also updates the Registry
    public void SetZoom(WebBrowser browser, int zoomValue)
    {
        dynamic activex = browser.ActiveXInstance;
        activex.ExecWB(OLECMDID_OPTICAL_ZOOM, OLECMDEXECOPT_DONTPROMPTUSER, zoomValue, 0);
    }

    // Applies the FontSize scale. It also updates the Registry
    public void SetTextScale(WebBrowser browser, int textValue)
    {
        dynamic activex = browser.ActiveXInstance;
        activex.ExecWB(OLECMDID_ZOOM, OLECMDEXECOPT_DONTPROMPTUSER, textValue, 0);
    }

    private static string keyZoomName = @"Software\Microsoft\Internet Explorer\Zoom";
    private static string keyTextName = @"Software\Microsoft\Internet Explorer\International\Scripts\3";
    private static string keyValueTextReset = "ResetZoomOnStartup";
    private static string keyValueZoomReset = "ResetZoomOnStartup2";

    public static  (int Zoom, int TextSize) InternetGetViewScale()
    {
        int zoomValue, textValue;
        using (var zoomKey = Registry.CurrentUser.OpenSubKey(keyZoomName,
            RegistryKeyPermissionCheck.ReadSubTree, RegistryRights.ReadKey)) {
            zoomValue = (int)zoomKey.GetValue("ZoomFactor", 100000) / 1000;
        }
      
        using (var textKey = Registry.CurrentUser.OpenSubKey(keyTextName,
            RegistryKeyPermissionCheck.ReadSubTree, RegistryRights.ReadKey)) {
            var keyBValue = BitConverter.GetBytes(2);
            textValue = BitConverter.ToInt32((byte[])textKey.GetValue("IEFontSize", keyBValue), 0);
        }
        return (zoomValue, textValue);
    }

    public static void InternetResetZoomAndFont(bool resetZoom, bool resetFontSize)
    {
        int keyZoomValue = resetZoom ? 1 : 0;
        int keyFontValue = resetFontSize ? 1 : 0;
        using (var zoomKey = Registry.CurrentUser.OpenSubKey(keyZoomName,
            RegistryKeyPermissionCheck.ReadWriteSubTree,
            RegistryRights.WriteKey)) {
            zoomKey.SetValue(keyValueZoomReset, keyZoomValue, RegistryValueKind.DWord);
            zoomKey.SetValue(keyValueTextReset, keyFontValue, RegistryValueKind.DWord);
        }
        var current = InternetGetViewScale();
        if (resetZoom) current.Zoom = 100;
        if (resetFontSize) current.TextSize = 2;
        InternetSetViewScale(current.Zoom, current.TextSize);
    }
}

这是它的工作原理:

WebBrowser 控件缩放和字体大小

于 2021-05-29T17:00:42.710 回答
0

这是我们增强的 WebBrowser 控件的 TextSize 属性的设置器,它可以满足我们的需要:

set
{
    IOleCommandTarget m_WBOleCommandTarget = GetOleCommandTarget();
    if (m_WBOleCommandTarget != null)
    {
        if (((int)value > (int)-1) && ((int)value < (int)5))
        {
            IntPtr pRet = m_NullPointer;
            try
            {
                pRet = Marshal.AllocCoTaskMem((int)1024);
                Marshal.GetNativeVariantForObject((int)value, pRet);

                int hr = m_WBOleCommandTarget.Exec(m_NullPointer,
                    (uint)OLECMDID.OLECMDID_ZOOM,
                    (uint)OLECMDEXECOPT.OLECMDEXECOPT_DONTPROMPTUSER,
                    pRet, m_NullPointer);
                Marshal.FreeCoTaskMem(pRet);
                pRet = m_NullPointer;
                if (hr == Hresults.S_OK)
                    m_enumTextSize = (TopicTextSize)value;
            }
            catch (Exception)
            {
            }
            finally
            {
                if (pRet != m_NullPointer)
                    Marshal.FreeCoTaskMem(pRet);
            }
        }
    }
}

public enum OLECMDID
{
    ...
    OLECMDID_ZOOM = 19,
    ...
}

public IOleCommandTarget GetOleCommandTarget()
{
    dynamic ax = this.ActiveXInstance;

    // IHtmlDocument2 also implements IOleCommandTarget
    var qi = (IOleCommandTarget)ax.Document;
    return qi;
}

我们使用该OLECMDID_ZOOM参数来处理IEFontSize注册表设置。

我们的解决方案的源代码可在以下 CodeProject 文章中找到:

最完整的 C# Webbrowser 包装器控件

它包含在其他情况下可能有用的其他代码片段。

于 2021-05-31T14:52:03.823 回答