Hiding the Progress Bar of a .NET 2.0 CF WebBrowser

One of the nice additions to the .NET 2.0 Compact Framework is the WebBrowser control. This control has always been present in the full framework, but to implement a web browser on the PocketPC you would have had to either write your own managed wrapper or use an existing one such as OpenNETCF’s HTMLViewer. But now that it’s included in the CF, we should give it a try. Many developers, myself included, use an embedded browser control to display rich, custom formatted content in their .NET apps. You can do this with the WebBrowser class by generating the HTML and setting the DocumentText property on the control. However, the new managed WebBrowser has a major drawback: Everytime you set the DocumentText property it shows a progress bar while loading the content into the browser. As far as I can tell, this behavior is only on Windows Mobile 5 devices, not on PocketPC 2003. Read on to figure out how to disable this behavior.

So, here’s what it looks like:

progressbar.png

At first glance, it doesn’t look intrusive. But when you start updating the HTML regularly and the progress bar pops up every time, it starts to look out of place. It would be nice if MS just exposed an option to disable it, but they don’t. We’re going to have to do some work ourselves to get rid of it.

First, some background. In my application, I created a UserControl named WebBrowserPanel. WebBrowserPanel has a single WebBrowser child control which is anchored Top, Right, Bottom, and Left. It has the following constuctor to make it use all the available space:

public WebBrowserPanel()
{
    InitializeComponent();       

    WebBrowser.Width = this.Width - 2;
    WebBrowser.Height = this.Height;
}

Putting the WebBrowser inside a user control lets us give it a border and, as you’ll see later, will be crucial for this fix to work.

Using the Remote Spy tool that ships with VS 2005, we can examine the window structure of the application on the Windows Mobile 5 emulator:

handles.png

The “MSPIE Status” window, a child of the “IExplore” window, looks interesting. Inspecting its properties shows us that it’s 23 pixels tall, and likely the progress bar we’re looking to hide. If we can get our hands on its window handle we can try to move, resize, or manipulate it in some other way to hide it. To get its window handle, I wrote the following function:

public static IntPtr FindHwndByClass(string strClass, IntPtr parentHwnd)
{
    StringBuilder sbClass = new StringBuilder(256);
    if (0 != GetClassName(parentHwnd, sbClass, sbClass.Capacity) && sbClass.ToString() == strClass)
        return parentHwnd;       

    IntPtr hwndChild = GetWindow(parentHwnd, (int)GetWindowFlags.GW_CHILD);
    while (hwndChild != IntPtr.Zero)
    {
        IntPtr result = FindHwndByClass(strClass, hwndChild);
        if (result != IntPtr.Zero)
            return result;       

        hwndChild = GetWindow(hwndChild, (int)GetWindowFlags.GW_HWNDNEXT);
    }       

    return IntPtr.Zero;
}

We can pass in “MSPIE Status” as the class name and look for it as a child of the WebBrowser.

Now that we have the handle, we can try to hide it. My initial attempts to hide the progress bar included using SetWindowPos to move the bar off the screen, resize it to one pixel high, and a number of other hacks that didn’t work. As a last resort, I tried the ultimate: DestroyWindow. After initializing the WebBrowser in WebBrowserPanel’s constructor, I would find the status bar window and destroy it. This seemed to work, kind of. As you can see below, there are about 20 pixels of whitespace at the bottom of the browser, noticeable when you have enough content to need scrollbars:

whitespace.png

Not good, but easy enough to work around. After we get the progress bar handle we can get its window height. After we destroy the window, we can resize the WebBrowser to be this much taller than its parent panel, thus clipping off this whitespace.  WebBrowserPanel’s constructor now looks like:

public WebBrowserPanel()
{
    InitializeComponent();

    WebBrowser.Width = this.Width - 2;
    WebBrowser.Height = this.Height;

    IntPtr hwndStatus = FindHwndByClass("MSPIE Status", WebBrowser.Handle);
    if (hwndStatus != IntPtr.Zero)
    {
        RECT rectStatus = new RECT();
        GetClientRect(hwndStatus, out rectStatus);

        DestroyWindow(hwndStatus);

        WebBrowser.Height += rectStatus.Height;
    }
}

And we end up with:

final.png

Success! You can now set the DocumentText any number of times without the progress bar showing up.

The code presented above makes use of the following PInvokes and enums:

[DllImport("coredll.dll")]
public static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);

[DllImport("coredll.dll")]
public static extern IntPtr GetWindow(IntPtr hwnd, int cmd);

public enum GetWindowFlags : int
{
    GW_HWNDFIRST = 0,
    GW_HWNDLAST = 1,
    GW_HWNDNEXT = 2,
    GW_HWNDPREV = 3,
    GW_OWNER = 4,
    GW_CHILD = 5,
    GW_MAX = 5
}

[DllImport("coredll.dll")]
public static extern bool DestroyWindow(IntPtr hwnd);

[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
    public int X;
    public int Y;
    public int Width;
    public int Height;
}

[DllImport("coredll.dll")]
static extern bool GetClientRect(IntPtr hWnd, out RECT lpRect);

Download Web Browser Demo

Downloaded a total of 292 times

12 comments so far

  1. NetRipper February 28, 2008 4:48 pm

    Sweet! I was just looking for this. :) I was initially hoping CF 3.5 would bring us a RichTextBox, but we’re not that lucky I guess. As I just needed it for read-only purposes anyway, this is a real good alternative.

    Thanks and regards.

  2. David June 5, 2008 4:04 pm

    Very nice workaround, I was just looking for something like this.

    Thank you and regards.

  3. Scott Gifford July 8, 2008 12:58 pm

    I’m using this code with great success on a SmartPhone (Windows Mobile Standard) device. Thanks!

    On a PocketPC (Windows Mobile Professional), though, I end up with 6 pixels of whitespace at the bottom where the progress bar used to be. Any ideas?

    Thanks!

    —-Scott.

  4. Scott Gifford July 8, 2008 8:43 pm

    Turned out to be a scaling thing. The status window was measured before the control was scaled then added to the WebBrowser’s height. When the WebBrowser was scaled down, the additional height was scaled down too, and so left some white space.

    My solution was to save the height in statusBarHeight, then compensate for the scaling when the control is scaled, like this:

    protected override void ScaleControl(SizeF factor, BoundsSpecified specified)
    {
    // Adjust this here, before this is scaled.
    // After base.ScaleControl, this will be scaled, but WebBrowser will not.
    WebBrowser.Height = this.Height + (int)(statusBarHeight / factor.Height);
    base.ScaleControl(factor, specified);
    }

  5. chris July 9, 2008 7:59 am

    Until you mentioned it, I had never heard of Windows Mobile Professional vs. Windows Mobile Standard. I googled around and found that MS just re-branded the regular and smartphone editions of WM6.

    Anyways, I’ve never tested this code on WM6 and I imagine that MS put out a new version of Pocket IE that changed things slightly. Until I get a chance to dig into it some, I would try modifying the WebBrowser height resizing to:

    WebBrowser.Height += rectStatus.Height + 6;

  6. Scott Gifford July 9, 2008 10:58 am

    Thanks Chris,

    Yeah, the whole product renaming thing is a bit annoying; it just means you have to say both names whenever you’re talking about it, because different people will know different names.

    The solution I posted above with scaling seemed to work. It might have worked just as well to put your code verbatim into a Load event; I think all scaling is done by then.

    Thanks again for this very useful tip!

    —-Scott.

  7. Kaustubh Deshpande July 23, 2008 11:37 pm

    Hi,
    It worked yaaaaaaar…
    Thanx.

  8. Nikita November 5, 2008 10:38 pm

    cool you saytik! Write more!

  9. Ajinkya Kulkarni March 23, 2009 9:32 pm

    This is one of the great article I have ever found on .Net Compact Framework programming

    Thank you very much!

  10. Bas Jaburg May 5, 2009 1:17 pm

    Excellent work!

    i have a question though: would it be possible to hide the context (popup) menu as well?

    - Bas

  11. chris May 6, 2009 9:09 am

    Bas,
    Back when I wrote the article, I spent some time looking at hiding the context menu but was unsuccessful. Googling around, it looked like other people had also tried without any luck. Please let me know if you find a way…

  12. Adam Dudzik January 1, 2010 10:21 am

    This is the link where you will find an article about hide contextmenu on webbrowser compact framework component
    http://www.blog.dudzik.org.pl/blokada-contextmenu-na-komponencie-webbrowser-2.html

    and this is the link with different implementation of above article.
    http://www.blog.dudzik.org.pl/ukrycie-paska-postepu-webbrowser-cf20.html

Leave a comment

Please be polite and on topic. Your e-mail will never be published.