8

I have a project that I am working on in VS2005. I have added a WebBrowser control. I add a basic empty page to the control

private const string _basicHtmlForm = "<html> "
                                      + "<head> "
                                      + "<meta http-equiv='Content-Type' content='text/html; charset=utf-8'/> "
                                      + "<title>Test document</title> "
                                      + "<script type='text/javascript'> "
                                      + "function ShowAlert(message) { "
                                      + "   alert(message); "
                                      + "} "
                                      + "</script> "
                                      + "</head> "
                                      + "<body><div id='mainDiv'> "
                                      + "</div></body> "
                                      + "</html> ";

private string _defaultFont = "font-family: Arial; font-size:10pt;";

private void LoadWebForm()
{
    try 
    {
        _webBrowser.DocumentText = _basicHtmlForm;
    }
    catch(Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}  

and then add various elements via the dom (using _webBrowser.Document.CreateElement). I am also loading a css file:

private void AddStyles()
{
    try
    {
        mshtml.HTMLDocument currentDocument = (mshtml.HTMLDocument) _webBrowser.Document.DomDocument;
        mshtml.IHTMLStyleSheet styleSheet = currentDocument.createStyleSheet("", 0);

        TextReader reader = new StreamReader(Path.Combine(Path.GetDirectoryName(Application.ExecutablePath),"basic.css"));
        string style = reader.ReadToEnd();
        styleSheet.cssText = style;
    }
    catch(Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}

Here is the css page contents:

body {
    background-color: #DDDDDD;
}

.categoryDiv {
    background-color: #999999;
}

.categoryTable {
    width:599px; background-color:#BBBBBB;
}

#mainDiv {
    overflow:auto; width:600px;
}

The style page is loading successfully, but the only elements on the page that are being affected are the ones that are initially in the page (body and mainDiv). I have also tried including the css in a element in the header section, but it still only affects the elements that are there when the page is created.

So my question is, does anyone have any idea on why the css is not being applied to elements that are created after the page is loaded? I have also tried no applying the css until after all of my elements are added, but the results don't change.

abatishchev
  • 98,240
  • 88
  • 296
  • 433
JamesL
  • 319
  • 2
  • 4
  • 11
  • Ive wondered the same. I always assumed that creating a webpage in that manner just created a stale/dead/static page, and that the rendering engine stopped checking styles unless an element within the page causes the renderer to "wake up". But im pulling that out of thin air. I just learned to add the CSS as the first thing to the head when initially creating a doc. – Mike_G Apr 01 '10 at 18:16
  • 2
    I know that this probably doesn't help at all (which is why it's a comment), but I wanted to tell you from experience that C#'s WebBrowser component is junk. Seemingly trivial things tend to end up as massive headaches and when it doesn't do what you want, you often end up having to go to the underlying ActiveX component... which is a mess to say the least (~5 different interfaces completely unorganized, having arbitrary sets of functionality). I highly recommend a different browser component whenever possible, for example GeckoFX, based on the Firefox engine. – Teekin Jun 17 '10 at 15:54
  • 2
    There's a key piece of information missing here: the elements you're creating, the ones that aren't getting styled. Presumably, you're setting the class attribute to `"categoryDiv"`, `"categoryTable"`, etc... But, that isn't explicitly stated in your question. Given the results you're describing, I'm far more inclined to believe there's a bug in your element creation than anything directly connected to your stylesheet creation. That being said, you do realize you're forcing the new stylesheet to be the first in the document (and therefore lowest-priority), right? – Shog9 Jun 19 '10 at 04:30
  • 1
    @Shog9: I suspect you are right. I created a test version of James' project and I am getting the styles applied to new elements just fine (given a few small mods). – Andre Artus Jun 22 '10 at 00:15
  • @Andre: heh, I did the same, with the same results. Good work on the answer! – Shog9 Jun 22 '10 at 18:07

7 Answers7

7

I made a slight modification to your AddStyles() method and it works for me. Where are you calling it from? I called it from "_webBrowser_DocumentCompleted".

I have to point out that I am calling AddStyles after I modify the DOM.

private void AddStyles()
{
    try
    {
        if (_webBrowser.Document != null)
        {
            IHTMLDocument2 currentDocument = (IHTMLDocument2)_webBrowser.Document.DomDocument;

            int length = currentDocument.styleSheets.length;
            IHTMLStyleSheet styleSheet = currentDocument.createStyleSheet(@"", length + 1);
            //length = currentDocument.styleSheets.length;
            //styleSheet.addRule("body", "background-color:blue");
            TextReader reader = new StreamReader(Path.Combine(Path.GetDirectoryName(Application.ExecutablePath), "basic.css"));
            string style = reader.ReadToEnd();
            styleSheet.cssText = style;

        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}

Here is my DocumentCompleted handler (I added some styles to basic.css for testing):

private void _webBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
  HtmlElement element = _webBrowser.Document.CreateElement("p");
  element.InnerText = "Hello World1";
  _webBrowser.Document.Body.AppendChild(element);

  HtmlElement divTag = _webBrowser.Document.CreateElement("div");
  divTag.SetAttribute("class", "categoryDiv");
  divTag.InnerHtml = "<p>Hello World2</p>";
  _webBrowser.Document.Body.AppendChild(divTag);


  HtmlElement divTag2 = _webBrowser.Document.CreateElement("div");
  divTag2.SetAttribute("id", "mainDiv2");
  divTag2.InnerHtml = "<p>Hello World3</p>";
  _webBrowser.Document.Body.AppendChild(divTag2);

  AddStyles();
}

This is what I get (modified the style to make it as ugly as a single human being can hope to make it :D ):

alt text

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
Andre Artus
  • 1,850
  • 15
  • 21
  • just a couple of comments, for me using this code with a wpf webbrowser control I had to use WebControl.Document as mshtml.HTMLDocument; to get the document and I applied the style on the Navigated event. thanks – Kezza Feb 12 '13 at 16:38
  • 1
    @LifeH2O: Mission Accomplished. :) – Andre Artus Dec 12 '13 at 02:01
1

one solution is to inspect the html prior to setting the DocumentText and inject CSS on the client side. I don't set the control url property but rather get the HTML via WebCLient and then set the DocumentText. maybe setting DocumentText (or in your case Document) after you manipulate the DOM could get it to re-render properly

private const string CSS_960 = @"960.css";
private const string SCRIPT_FMT = @"<style TYPE=""text/css"">{0}</style>";
private const string HEADER_END = @"</head>";


 public void SetDocumentText(string value)
{
    this.Url = null;  // can't have both URL and DocText
    this.Navigate("About:blank");
    string css = null;
    string html = value;

    // check for known CSS file links and inject the resourced versions
    if(html.Contains(CSS_960))
    {
      css = GetEmbeddedResourceString(CSS_960);
      html = html.Insert(html.IndexOf(HEADER_END), string.Format(SCRIPT_FMT,css));
    }

    if (Document != null) { 
      Document.Write(string.Empty);
    }
    DocumentText = html;
}
MarkDav.is
  • 640
  • 1
  • 8
  • 16
0

It would be quite hard to say unless you send a link of this.

but usually the best method for doing style related stuff is that you have the css already in the page and in your c# code you only add ids or classes to elements to see the styles effects.

makeitmorehuman
  • 11,287
  • 3
  • 52
  • 76
  • I have tried having the css in the page when I create it in a style element in the header, and it still only affects the body elements that are there when the page is created, not any that I add via code. – JamesL Apr 01 '10 at 18:12
  • do you have inline styles maybe so your added css loses to their inheritance hierarchy? – makeitmorehuman Apr 01 '10 at 18:17
  • its probably best if you send a link so I can firebug it. its hard to just ask questions to get to the bottom of this. – makeitmorehuman Apr 01 '10 at 19:23
  • There is no link. It is a WebBrowser control inside of a windows form. – JamesL Apr 01 '10 at 19:46
0

I have found that generated tags with class attribute does not get their styles applied.

This is my workaround that is done after the document is generated:

public static class WebBrowserExtensions
{
    public static void Redraw(this WebBrowser browser)
    {
        string temp = Path.GetTempFileName();
        File.WriteAllText(temp, browser.Document.Body.Parent.OuterHtml,
            Encoding.GetEncoding(browser.Document.Encoding));
        browser.Url = new Uri(temp);
    }
}
hultqvist
  • 17,451
  • 15
  • 64
  • 101
  • Can you please explain this a little more ? I have some div generated dynamically and they have class attribute - now I want to set display:none to this class- any idea how I can do so ? – marifrahman Jan 08 '15 at 02:50
  • @marifrahman I can't help you with that, that sounds like something worth a [question of its own](http://stackoverflow.com/questions/ask) – hultqvist Jan 08 '15 at 22:02
0

I use similiar control instead of WebBrowser, I load HTML page with "default" style rules and I change the rules within the program.

(DrawBack - maintainance, when I need to add a rule, I also need to change it in code)

' ----------------------------------------------------------------------
Public Sub mcFontOrColorsChanged(ByVal isRefresh As Boolean)  
  ' ----------------------------------------------------------------------
  ' Notify whichever is concerned:
  Dim doc As mshtml.HTMLDocument = Me.Document
  If (doc.styleSheets Is Nothing) Then Return
  If (doc.styleSheets.length = 0) Then Return
  Dim docStyleSheet As mshtml.IHTMLStyleSheet = CType(doc.styleSheets.item(0), mshtml.IHTMLStyleSheet)
  Dim docStyleRules As mshtml.HTMLStyleSheetRulesCollection = CType(docStyleSheet.rules, mshtml.HTMLStyleSheetRulesCollection)

  ' Note: the following is needed seperately from 'Case "BODY"
  Dim docBody As mshtml.HTMLBodyClass = CType(doc.body, mshtml.HTMLBodyClass)
  If Not (docBody Is Nothing) Then
    docBody.style.backgroundColor = colStrTextBg 
  End If

  Dim i As Integer
  Dim maxI As Integer = docStyleRules.length - 1
  For i = 0 To maxI
    Select Case (docStyleRules.item(i).selectorText)
      Case "BODY"
        docStyleRules.item(i).style.fontFamily = fName ' "Times New Roman" | "Verdana" | "courier new" | "comic sans ms" | "Arial"
      Case "P.myStyle1"
        docStyleRules.item(i).style.fontSize = fontSize.ToString & "pt"
      Case "TD.myStyle2" ' do nothing  
      Case ".myStyle3"
        docStyleRules.item(i).style.fontSize = fontSizePath.ToString & "pt"
        docStyleRules.item(i).style.color = colStrTextFg
        docStyleRules.item(i).style.backgroundColor = colStrTextBg
      Case Else
        Debug.WriteLine("Rule " & i.ToString & " " & docStyleRules.item(i).selectorText)
    End Select

  Next i

  If (isRefresh) Then  
    Me.myRefresh(curNode)
  End If

End Sub
Atara
  • 3,523
  • 6
  • 37
  • 56
0

It could be that the objects on the page EXIST at the time the page is being loaded, so each style can be applied. just because you add a node to the DOM tree, doesnt mean that it can have all of its attributes manipulated and rendered inside of the browser. the methods above seem to use an approach the reloads the page (DOM), which suggests that this may be the case. In short, refresh the page after you've added an element

rigamonk
  • 1,179
  • 2
  • 17
  • 45
0

It sounds as though phq has experienced this. I think the way I would approach is add a reference to jquery to your html document (from the start).

Then inside of the page, create a javascript function that accepts the element id and the name of the class to apply. Inside of the function, use jquery to dynamtically apply the class in question or to modify the css directly. For example, use .addClass or .css functions of jquery to modify the element.

From there, in your C# code, after you add the element dynamically invoke this javascript as described by Rick Strahl here: http://www.west-wind.com/Weblog/posts/493536.aspx

j03m
  • 5,195
  • 4
  • 46
  • 50