views:

941

answers:

7

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.

A: 

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.

XGreen
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
do you have inline styles maybe so your added css loses to their inheritance hierarchy?
XGreen
no, I don't have any inline styles.
JamesL
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.
XGreen
There is no link. It is a WebBrowser control inside of a windows form.
JamesL
A: 

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);
    }
}
phq
A: 

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
A: 

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

A: 

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

Dr.HappyPants
A: 

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;
}
Mark Davis
+1  A: 

Hi James, 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

Andre Artus