在 WinForms WebBrowser 控件中设置常规文本大小选项

Posted

技术标签:

【中文标题】在 WinForms WebBrowser 控件中设置常规文本大小选项【英文标题】:Set general text size option in WinForms WebBrowser control 【发布时间】:2021-08-16 19:37:06 【问题描述】:

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

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

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

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

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

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

【问题讨论】:

应该记录在HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\Zoom。 IIRC,如果您将 ResetTextSizeOnStartupResetTextSizeOnStartup2 都设置为 1。重新启动 WebBrowser 时,缩放和文本大小都将重置为默认值。第一个设置应该与文本大小有关,另一个与缩放值有关。 @Jimi,我在玩一个记录注册表更改的工具时发现了另一件事。我们需要键 HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\International\Scripts\3 的 REG_BINARY IEFontSize 参数。谷歌搜索确认that。 是的,但是当您设置 ResetTextSizeOnStartup = 0x0001 时,该条目会被重置。然后将其重置为0x0002(正常)。 -- 所以,你可以只添加一个允许重置默认值的 MenuItem,或者只是忽略默认设置并重置为普通视图。 @Jimi,但实际上只有IEFontSize指定了有效值,所以我们只能读/写它。根据我的注册表更改记录,Microsoft Help Viewer 没有触及 Zoom 配置单元,所以我认为 IEFontSize 是我们需要的唯一参数。即使有人会用ResetTextSizeOnStartupResetTextSizeOnStartup2 重置它,我们仍然可以使用默认的IEFontSize 值而不会出现任何问题。对吗? 这取决于你。 IEFontSize 存储通用比例(1 到 4),ZoomFactor 条目是缩放整个视口的值。您可以实现完整的功能(提供两种设置方式——ZoomFactor 为 100000 为 100%,125000 为 125% 等),或者在 WebBrowser 会话启动时忽略两者并重置为默认值。在任何情况下,您都将在您的应用程序中覆盖常规用户设置。 -- 看起来您只是参考帮助查看器,但用户也可以在 Internet Explorer 首选项中更改缩放级别,所以我假设您想同时处理(或重置两者)。 【参考方案1】:

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

问题:

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

预期行为:

应用程序应提供更改缩放和字体大小比例的方法,独立于其他应用程序可能已设置的内容。 应在不重新创建控件句柄的情况下应用新设置

在示例代码中,一个专门的类 WebBrowserHelper 包含一个从 Registry 读取当前 Zoom 和 FontSize 设置的方法,以将 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

WebBrowserHelperSetZoom() 方法使用WebBrowser ActiveX 实例来设置缩放级别,调用它的ExecWb method,作为OLECMDID 参数传递OLECMDID_OPTICAL_ZOOM 值,OLECMDEXECOPT 参数设置为OLECMDEXECOPT_DONTPROMPTUSERpvaIn 参数(缩放值)设置为指定缩放级别的整数值。

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

public static void SetZoom(WebBrowser browser, int zoomValue)

    dynamic activex = browser.ActiveXInstance;
    activex.ExecWB(OLECMDID_OPTICAL_ZOOM, OLECMDEXECOPT_DONTPROMPTUSER, zoomValue, 0);

IEFontSizeIEFontSizePrivate 键存储一个二进制值,在 [0-4] 中很受欢迎。这些值大致对应于x-smallsmallmediumlargex-large CSS 设置。FontSize 比例适用于文档正文,使用乘数:[Value + 1] * 4

SetTextScale() 方法使用 WebBrowser ActiveX 实例设置 FonSize 比例,调用其ExecWb() 方法,将OLECMDID_ZOOM 值作为OLECMDID 参数传递,OLECMDEXECOPT 参数设置为OLECMDEXECOPT_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%,并将字体大小重置为Medium

WebBrowserHelper.InternetResetZoomAndFont(true, true);

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

所有设置缩放的 ToolStripMenuItem 都分组在一个名为 zoomMenuItems 的集合中。都使用相同的Click 事件处理程序,zoomToolStripMenuItems_Click 所有设置 FontSize 比例的 ToolStripMenuItem 都分组在一个名为 textMenuItems 的集合中。都使用相同的Click 事件处理程序,textToolStripMenuItems_Click Zoom 和 Fontscale 值设置为每个 ToolStripMenuItem 的 Tag 属性(您当然可以使用任何其他方式将值与特定 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);
    


这就是它的工作原理:

【讨论】:

感谢您的详细解答!!但是,我们需要的是OLECMDID_ZOOM 参数。看我的回答。 我只是不明白:“当当前应用程序或第三方应用程序应用新设置更改缩放和字体大小比例时,WebBrowser 控件应该能够自行刷新” .看起来您想在 WebBrowserHelper 类中提供一个事件或回调方法,以提供有关相关注册表设置外部更改的通知,对吧? 是的,这里有很多事情,实际上是必要的:我不确定您实际上想要做什么,所以我发布了很多不同的方法来做不同的事情。 -- 顺便说一句,如果你想申请OLECMDID_ZOOM,你可以像这里所示的那样,与OLECMDID_OPTICAL_ZOOM相关:dynamic activex = browser.ActiveXInstance; activex.ExecWB(OLECMDID_ZOOM, OLECMDEXECOPT_DONTPROMPTUSER, textValue, 0);。就是这样,您不需要IOleCommandTarget、编组和其他COM 东西。 -- 注册表的东西在需要时就在那里,这些方法会自行更新注册表。 RegistryKey 更改通知不在此处处理,只是导航到另一个页面或按 F5 刷新当前页面应用新值。我可以添加一个处理RegNotifyChangeKeyValue 的方法——无论如何,我会更新代码以反映我认为您想要做的事情。如前所述,您不能只处理 FontSize 更改,还必须处理 Zoom 更改。 好吧,我已经更新了整个内容,以反映我认为您最终想要的内容。使用OLECMDID_ZOOM 应用文本比例(它和以前一样,只是使用自动化过程,就像OLECMDID_OPTICAL_ZOOM 之前所做的那样):查看修改后的SetTextScale() 方法(它替换了您发布的所有代码并且是更安全)。【参考方案2】:

这是我们增强的 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 文章中找到:

The most complete C# Webbrowser wrapper control

它包含在其他情况下可能有用的其他代码 sn-ps。

【讨论】:

以上是关于在 WinForms WebBrowser 控件中设置常规文本大小选项的主要内容,如果未能解决你的问题,请参考以下文章

如何在winforms的WebBrowser控件中调用javascript?

在 WinForms WebBrowser 控件中设置常规文本大小选项

Flash 视频在 WinForms WebBrowser 控件中显示不稳定

WPF 和 WinForms WebBrowser 控件之间存在哪些功能差异?

在 WinForms WebBrowser 中编辑 CSS

如何截获WebBrowser控件onbeforeunload事件