Skip to content

0026: Dynamic WebBrowser control

24/12/2010

Hello again,

Recently, during develop process I came across strange “bug” in WebBrowser control. Please, take a look at the text that I post at stackoverflow.com:

=====================================================
Good day

I have question about displaying html documents in a windows forms applications. App that I’m working on should display information from the database in the html format. I will try to describe actions that I have taken (and which failed):

1) I tried to load “virtual” html page that exists only in memory and dynamically change it’s parameters (webbMain is a WebBrowser control):

public static string CreateBookHtml()
        {
            StringBuilder sb = new StringBuilder();

            //Declaration
            sb.AppendLine(@"");
            sb.AppendLine(@"");
            sb.AppendLine(@"");
            sb.AppendLine(@""); 

            //Head
            sb.AppendLine(@"");
            sb.AppendLine(@"<title>Exemplary document</title>");
            sb.AppendLine(@"");
            sb.AppendLine(@"");

            //Body
            sb.AppendLine(@"");
            sb.AppendLine(@"<p>Example.</p>");
            sb.AppendLine(@"");
            sb.AppendLine(@"");

            return sb.ToString();
        }

void LoadBrowser()
{
this.webbMain.Navigate("about:blank");
this.webbMain.DocumentText = CreateBookHtml();
HtmlDocument doc = this.webbMain.Document;
}

This failed, because doc.Body is null, and doc.getElementById(“paragraph”) returns null too. So I cannot change paragraph InnerText property. Furthermore, this.webbMain.DocumentText is “”…

2) I tried to create html file in specified folder, load it to the WebBrowser and then change its parameters. Html is the same as created by CreateBookHtml() method:

private void LoadBrowser()
        {
            this.webbMain.Navigate("HTML\\BookPage.html"));             
            HtmlDocument doc = this.webbMain.Document;
        }

This time this.webbMain.DocumentText contains Html data read from the file, but doc.Body returns null again, and I still cannot take element using getByElementId() method. Of course, when I have text, I would try regex to get specified fields, or maybe do other tricks to achieve a goal, but I wonder – is there simply way to mainipulate html? For me, ideal way would be to create HTML text in memory, load it into the WebBrowser control, and then dynamically change its parameters using IDs. Is it possible? Thanks for the answers in advance, best regards,

Paweł
=====================================================

This is the answer that I received after few hours from Andrei Pana:

=====================================================
I’ve worked some time ago with the WebControl and like you wanted to load a html from memory but have the same problem, body being null. After some investigation, I noticed that the Navigate and NavigateToString methods work asynchronously, so it needs a little time for the control to load the document, the document is not available right after the call to Navigate. So i did something like (wbChat is the WebBrowser control):

wbChat.NavigateToString("<html><body><div>first line</div></body><html>");

DoEvents();

where DoEvents() is implemeted as:

[SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
public void DoEvents()
{
    DispatcherFrame frame = new DispatcherFrame();
    Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background,
                new DispatcherOperationCallback(ExitFrame), frame);
    Dispatcher.PushFrame(frame);
}

and it worked for me, after the DoEvents call, I could obtain a non-null body:

mshtml.IHTMLDocument2 doc2 = (mshtml.IHTMLDocument2)wbChat.Document;

mshtml.HTMLDivElement div = (mshtml.HTMLDivElement)doc2.createElement("div");
div.innerHTML = "some text";
mshtml.HTMLBodyClass body = (mshtml.HTMLBodyClass)doc2.body;

if (body != null)
{
    body.appendChild((mshtml.IHTMLDOMNode)div);

    body.scrollTop = body.scrollHeight;
}
else
    Console.WriteLine("body is still null");

I don’t know if this is the right way of doing this, but it fixed the problem for me, maybe it helps you too.

Later Edit:

public object ExitFrame(object f)
{
    ((DispatcherFrame)f).Continue = false;
    return null;
}

The DoEvents method is necessary on WPF. For System.Windows.Forms one can use Application.DoEvents().
=====================================================

I tried to implement this and it works! After short investigation, I posted comment back:

=====================================================
Hello! Thank you for the answer. I’d like to add something to it. First, there should be ExitFrame method body. It looks like: ExitFrame(object f){((DispatcherFrame)f).Continue = false;return null;}. Second, we have to add two assemblies to the solution: WindowsBase.dll and Microsoft.mshtml.dll. Third: I couldn’t find NavigateToString method in WebBrowser. Most important is, that your solution works (looks awful, but works :)), and I’m writing above only for other readers. Thank you very much!
=====================================================

And again received answer from Andrei:

=====================================================
I’ve forgot to add the ExitFrame method and to mention about the assemblies, I will edit the answer so that the code will be more visible. Regarding the NavigateToString – I was actually using the WPF version of the WebBrowser control which actually has this method (msdn.microsoft.com/en-us/library/… ) though this is strange, because I suspect it is a wrapper around the native control, just like the WebBrowser from System.Windows.Forms. I’m not happy either with how this looks, but sometimes you just don’t have what to do…
=====================================================

As you can see, this is the next time that I’ve found the answer to my question on stackoverflow, so I definitly can recommend it: in case of any coding trouble you will find the answer there (with good probability). Best regards,

Paweł

PS. I noticed that Application.DoEvents() works from time to time. Custom DoEvents proposed by Andrei works every time.

Advertisements
Leave a Comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: