Search code examples
c#winformsuser-controlslambda

Attaching delegates to webBrowser html elements


I am working on a windows form UserControl to render and interact with some HTML using the webBrowser control. Part of the content are some radio buttons that I need to capture click events from within my control. So in the DocumentCompleted event of the webBrowser I am attaching an onClick event to the radio buttons. All of this seems to work until I click on one of the radio buttons, it seems that only the final radio button is actually having the event attached, even though I have stepped through the code and the delegate is being attached for each radio button.

This is the lambda I am using to attach the delegate to the HtmlElement.

void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
     foreach (HtmlElement el in webBrowser1.Document.GetElementsByTagName("input"))
     {
         el.AttachEventHandler("onclick", (sender1, e1) => clickEventHandler(el, EventArgs.Empty));
     }
}

public void clickEventHandler(object sender, EventArgs e)
{
    Guid answerId;
    var he = (HtmlElement)sender;
    if (Guid.TryParse(he.Id, out answerId))
        if (AnswerSelected != null)
            AnswerSelected(answerId);
 }

Delegate and event for AnswerSelected

public delegate void HtmlControlAnswerEventHandler(Guid answerId);
public event HtmlControlAnswerEventHandler AnswerSelected;

Input button string.

<input type=radio id="{0}" name="answer" value="{1}" />

Solution

  • It seems that when using lambdas like this the variable used to reference the object is lost. If I attach the events this way

    void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
    {
         foreach (HtmlElement el in webBrowser1.Document.GetElementsByTagName("input"))
         {
             el.AttachEventHandler("onclick", (sender1, e1) => clickEventHandler(el, EventArgs.Empty));
         }
    }
    

    then only the last attached event is valid.

    When I attach the events this way

    private void AttachClickEventToInputs()
    {
          var htmlElements = webBrowser1.Document.GetElementsByTagName("input");
          for (int i = 0; i < htmlElements.Count; i++)
          {
              htmlElements[i].AttachEventHandler("onclick", (sender1, e1) => clickEventHandler(htmlElements[i], EventArgs.Empty));
          }
     }
    

    There is an issue with the inder i becoming greater then the number of elements (this stumps me.)

    However if I attach the events this way

    private void AttachClickEventToInputs()
    {
         var htmlElements = webBrowser1.Document.GetElementsByTagName("input");
         for (int i = 0; i < htmlElements.Count; i++)
         {
              HtmlElement el = htmlElements[i];
              el.AttachEventHandler("onclick", (sender1, e1) => clickEventHandler(el, EventArgs.Empty));
         }
    }
    

    then everything works as intended. I am not completly sure why this works, other then maybe we need a hard reference to the object when attaching delegates?