Adam Kane
Adam Kane

Reputation: 4036

WPF WebBrowser control zoom in/out support?

For a WPF WebBrowser control, is there a way to duplicate Internet Explorer's zoom functionality?

In other words, Internet Explorer has the menu View > Zoom > 75%, which renders the web page at 75% scale. Is there a way to make a web browser control, which is embedded in a WPF app, do the same thing?

I've seen this post: WPF WebBrowser - How to Zoom Content?

But it only seems to scale the page and not the page content.

Upvotes: 8

Views: 12457

Answers (4)

phoff
phoff

Reputation: 183

Here's how I did it:

// Needed to expose the WebBrowser's underlying ActiveX control for zoom functionality
[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("6d5140c1-7436-11ce-8034-00aa006009fa")]
internal interface IServiceProvider
{
    [return: MarshalAs(UnmanagedType.IUnknown)]
    object QueryService(ref Guid guidService, ref Guid riid);
}

static readonly Guid SID_SWebBrowserApp = new Guid("0002DF05-0000-0000-C000-000000000046");

private void ZoomListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    object zoomPercent; // A VT_I4 percentage ranging from 10% to 1000%
    switch(ZoomListBox.SelectedItem.ToString())
    {
        case "System.Windows.Controls.ListBoxItem: 200%":
            zoomPercent = 200;
            break;
        case "System.Windows.Controls.ListBoxItem: 100%":
            zoomPercent = 100;
            break;
        case "System.Windows.Controls.ListBoxItem: 50%":
            zoomPercent = 50;
            break;
        default:
            zoomPercent = 100;
            break;
    }

    // grab a handle to the underlying ActiveX object
    IServiceProvider serviceProvider = null;
    if (m_webView.Document != null)
    {
        serviceProvider = (IServiceProvider)m_webView.Document;
    }

    Guid serviceGuid = SID_SWebBrowserApp;
    Guid iid = typeof(SHDocVw.IWebBrowser2).GUID;
    SHDocVw.IWebBrowser2 browserInst = (SHDocVw.IWebBrowser2)serviceProvider.QueryService(ref serviceGuid, ref iid);

    // send the zoom command to the ActiveX object
    browserInst.ExecWB(SHDocVw.OLECMDID.OLECMDID_OPTICAL_ZOOM, SHDocVw.OLECMDEXECOPT.OLECMDEXECOPT_DONTPROMPTUSER, ref zoomPercent, IntPtr.Zero);
}

All the service provider stuff exposes the ActiveX since the WPF WebBrowser control doesn't expose it directly. Aside from that, it's pretty much the same as alexei's solution.

Upvotes: 4

sikemullivan
sikemullivan

Reputation: 71

public partial class TestWindow: UserControl
{
    public TestWindow()
    {
        InitializeComponent();

        browser.LoadCompleted += new LoadCompletedEventHandler(browser_LoadCompleted);
    }

    private void browser_LoadCompleted(object sender, NavigationEventArgs e)
    {
        try
        {

            FieldInfo webBrowserInfo = browser.GetType().GetField("_axIWebBrowser2", BindingFlags.Instance | BindingFlags.NonPublic);

            object comWebBrowser = null;
            object zoomPercent = 120;
            if (webBrowserInfo != null)
                comWebBrowser = webBrowserInfo.GetValue(browser);
            if (comWebBrowser != null)
            {
                InternetExplorer ie = (InternetExplorer)comWebBrowser;
                ie.ExecWB(SHDocVw.OLECMDID.OLECMDID_OPTICAL_ZOOM, SHDocVw.OLECMDEXECOPT.OLECMDEXECOPT_DONTPROMPTUSER, ref zoomPercent, IntPtr.Zero);
            }
        }
        catch (Exception ex)
        {
        }
    }


    public void SetBrowser(string url)
    {
        browser.Navigate(url,null,null,null);

    }

    internal void Destroy()
    {
        try
        {
            if (browser.Parent != null)
            {
                ((Grid)browser.Parent).Children.Remove(browser);
                browser.Navigate("about:blank");
                browser.Dispose();
                browser = null;
            }
        }
        catch { }

    }

}

Upvotes: 7

Uwe Keim
Uwe Keim

Reputation: 40736

When using the other solutions, I always get errors of kind

HRESULT: 0x80040100
DRAGDROP_E_NOTREGISTERED

I found a solution on this page that worked for me:

var wb = webBrowser.ActiveXInstance.GetType();

object o = zoomPercentage; // Between 10 and 1000.

wb.InvokeMember(
    @"ExecWB", 
    BindingFlags.InvokeMethod, 
    null, 
    webBrowser.ActiveXInstance, 
    new[]
        {
            OLECMDID.OLECMDID_OPTICAL_ZOOM, 
            OLECMDEXECOPT.OLECMDEXECOPT_DONTPROMPTUSER, 
            o, 
            o
        }); 

With OLECMDID_OPTICAL_ZOOM being 63 and OLECMDEXECOPT_DONTPROMPTUSER being 2.

Upvotes: 0

alexei
alexei

Reputation: 2291

This is not an exact answer since it is for the WinForms control, but perhaps will be useful in case you decide to use it in a WindowsFormsHost instead of the WPF control, which exposes way too little to be useful.

You could use an OLE commands through ExecWB on the ActiveX instance: OLECMDID_ZOOM for text size and OLECMDID_OPTICAL_ZOOM for optical zoom. For example,

object pvaIn = 200; // A VT_I4 percentage ranging from 10% to 1000%
var browserInst = ((SHDocVw.IWebBrowser2)(browserContol.ActiveXInstance));
browserInst.ExecWB(SHDocVw.OLECMDID.OLECMDID_OPTICAL_ZOOM,
                   SHDocVw.OLECMDEXECOPT.OLECMDEXECOPT_DONTPROMPTUSER,
                   ref pvaIn, IntPtr.Zero);

Some notes:

  • a reference to Interop.SHDocVw assembly is needed
  • the command succeeds only after a document has loaded
  • the range of pvaIn could be retrieved via OLECMDID_GETZOOMRANGE
  • for reference list of commands is on MSDN
  • I experienced this strange behavior that seemed to happen only on non-96 dpi. Upon startup, the rendered text size did not correspond to that stored in OLECMDID_ZOOM state. Setting the value (to any value) did not fix the discrepancy: the rendered size is still what looked like [stored size + 2]. When optical zoom was set to 100%, the discrepancy in text-size went away (text size visibly shrank after zooming to 100%). This did't happen in IE, and perhaps that was just a weird artifact in my environment -- but just fyi.

Upvotes: 2

Related Questions