Search code examples
vb.netwinformswebbrowser-control

How to set the Style of a HtmlElement at the Mouse position when a Key is pressed


I have a TABLE Element in the current Document of a WebBrowser control.
I want to be able to color a Cell when my cursor is on it, when I press the A Key

Private Sub WebBrowser1_DocumentCompleted(sender As Object, e As WebBrowserDocumentCompletedEventArgs) Handles WebBrowser1.DocumentCompleted
        htmlDocument1 = WebBrowser1.Document
        AddHandler htmlDocument1.MouseOver, AddressOf Me.gettd
End Sub

Public Sub gettd(ByVal sender As Object, ByVal e As System.Windows.Forms.HtmlElementEventArgs)
    Dim theElementCollection As HtmlElementCollection
    theElementCollection = WebBrowser1.Document.GetElementsByTagName("td")
    For Each curElement As HtmlElement In theElementCollection
        e.ToElement.Style = "background-color: orange;"
    Next
End Sub

Setting KeyPreview = True:

Public Sub Form1_KeyDown(ByVal sender As System.Object, ByVal e As KeyEventArgs) Handles Me.KeyDown
   If e.KeyCode = Keys.A Then
   ...
   end if
End Sub

How else can I achieve this?


Solution

  • A few changes are required:

    • The DocumentCompleted event can be used to add handlers to other events, but you have to remember that this event is raised multiple times per URI navigated: you're adding a multitude of handlers to the MouseOver event.
      It's simpler, in this case, to add a handler when the URI has been Navigated and remove it when we're Navigating to another.
      Also, read the notes here: How to get an HtmlElement value inside Frames/IFrames?

    • Instead of parsing the whole collection of Elements in the Document, we can simply use the Document.GetElementFromPoint() method, passing the e.ClientMousePosition value provided by the MouseOver or MouseMove events as the current Point position.

    • The Form's KeyDown event cannot be used to trap keys pressed - even with KeyPreview set to True - when other controls capture the input. You can get that key press overriding the Form's ProcessCmdKey, since it's called when messages are pre-processed.

    • Always check for null before accessing a HtmlElement or its properties. E.g., string properties never initialized are null (Nothing), not String.Empty.

    Add this code to a Form, then navigate to a Html Page:

    webBrowser1.Navigate("[Some URI]")
    

    I've added a toggle function, to set the current Cell's Style to a background-color value when Keys.A is pressed and set it back when it's pressed again. Here, hard-coded to white, but you can add some logic to save the previous style, if required.

    Public Class FormBrowser
        Private trackedElement As HtmlElement = Nothing
        Private elementColorOrange As String = "background-color: orange;"
        Private elementColorWhite As String = "background-color: white;"
    
        Private Sub webBrowser1_Navigating(sender As Object, e As WebBrowserNavigatingEventArgs) Handles webBrowser1.Navigating
            If webBrowser1.Document Is Nothing Then Return
            RemoveHandler webBrowser1.Document.MouseOver, AddressOf OnBrowserMouseOver
        End Sub
    
        Private Sub webBrowser1_Navigated(sender As Object, e As WebBrowserNavigatedEventArgs) Handles webBrowser1.Navigated
            AddHandler webBrowser1.Document.MouseOver, AddressOf OnBrowserMouseOver
        End Sub
    
        Protected Overrides Function ProcessCmdKey(ByRef msg As Message, keyData As Keys) As Boolean
            If keyData = Keys.A Then
                If trackedElement IsNot Nothing AndAlso trackedElement.TagName = "TD" Then
                    Dim currentStyle As String = trackedElement.Style & ""
                    trackedElement.Style = If(currentStyle.Contains(elementColorOrange), elementColorWhite, elementColorOrange)
                    Return True
                End If
            End If
            Return MyBase.ProcessCmdKey(msg, keyData)
        End Function
    End Class
    
    Private Sub OnBrowserMouseOver(sender As Object, e As HtmlElementEventArgs)
        Dim doc = DirectCast(sender, WebBrowser).Document
        If doc Is Nothing Then Return
        trackedElement = doc.GetElementFromPoint(e.ClientMousePosition)
        txtElmName.Text = trackedElement?.OuterHtml
        txtElmValue.Text = trackedElement?.InnerHtml
    End Sub
    

    This is how it works:

    WebBrowser Track Mouse Position