Introduction
I am in the process of adding a feature to
Intertexti in which I can create
HTML forms and log the user's data specific to the notecard. Furthermore,
I want to be able to do this without requiring a back-end server. The idea
is to be able to get/set the values for text input fields and to capture mouse
click events for radio buttons, checkboxes, and normal buttons. My search
for how to do this took me two a few places:
Attaching delegates to webbrowser HTML elements
Getting text in a webbrowser textbox
A list of HTML DOM
event names
The demonstration program utilizes .NET's WebBrowser
control (and if you read
the Intertexti article, yes, I'm ditching webkit.net and, even though I
subsequently got open-webkit-sharp to compile and work, it has so many problems
it wasn't worth persuing.)
The Test Form
I hard-coded the HTML in the test program to render the above web page:
browser.DocumentText="<form>\r\n" +
"First name: <input id='firstname' type='text' name='firstname'/><br/>\r\n" +
"Last name: <input id='lastname' type='text' name='lastname'/><br/>\r\n" +
"Password: <input id='password' type='password' name='pwd'/><br><br/>\r\n" +
"<input type='radio' id='male' name='sex' value='male'/>Male<br/>\r\n" +
"<input type='radio' id='female' name='sex' value='female'/>Female<br/><br/>\r\n" +
"<input type='checkbox' id='bike' name='vehicle' value='Bike'/>I have a bike<br/>\r\n" +
"<input type='checkbox' id='car' name='vehicle' value='Car'/>I have a car <br/><br/>\r\n" +
"<input type='button' id='ok' value='OK'/><br/>\r\n" +
"<input type='button' id='cancel' value='Cancel'/><br/><br/>\r\n" +
"</from>";
The DocumentCompleted Event
Event handlers cannot be wired up until the document has loaded, hence we
need to first handle the DocumentCompleted
event:
public class Form1()
{
public Form1()
{
...
browser.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(OnDocumentCompleted);
}
protected void OnDocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
WireUpButtonEvents();
}
}
Wiring Up Event Handlers
For the actual element wireups, we use the element method AttachEventHandler
.
Unfortunately, when the event is actually called, the sender parameter is null,
so we instead take advantage of an anonymous method to forward the event to our
real handler, with the element instance provided as the sender. However,
this means we need the actual instance of the element in the anonymous method,
hence the indexed loop rather than a more typical foreach
iterator.
Lastly, the elements of type radio button, checkbox, and button are passed to
the OnElementClicked
handler by wiring up the DOM "onclick
" property, whereas
text elements are passed to the OnElementLostFocus
handler by wiring up the DOM
"onblur
" property.
protected void WireUpButtonEvents()
{
HtmlElementCollection elements = browser.Document.GetElementsByTagName("input");
for (int i=0; i<elements.Count; i++)
{
HtmlElement el = elements[i];
string elType = el.GetAttribute("type");
switch (elType)
{
case "radio":
case "checkbox":
case "button":
{
el.AttachEventHandler("onclick", (sender, args) => OnElementClicked(el, EventArgs.Empty));
break;
}
case "text":
{
el.AttachEventHandler("onblur", (sender, args) => OnElementLostFocus(el, EventArgs.Empty));
break;
}
}
}
}
The Event Handlers
Lastly, the event handlers simply report the event occurrence in the textbox
below the browser control:
protected void OnElementClicked(object sender, EventArgs args)
{
HtmlElement el = sender as HtmlElement;
string elType = el.GetAttribute("type");
string elName = el.GetAttribute("name");
string elValue = el.GetAttribute("value");
tbMessages.Text += "Clicked: " + elType + " " + elName + " " + elValue + "\r\n";
}
protected void OnElementLostFocus(object sender, EventArgs args)
{
HtmlElement el = sender as HtmlElement;
string elType = el.GetAttribute("type");
string elName = el.GetAttribute("name");
string elValue = el.GetAttribute("value");
tbMessages.Text += elType + " " + elName + " " + elValue + "\r\n";
}
Restoring State
If you click on the "Set Data" button on the form, the state of the various
input elements are set:
protected void SetFormState()
{
browser.Document.GetElementById("firstname").SetAttribute("value", "Marcia");
browser.Document.GetElementById("lastname").SetAttribute("value", "JohnDoe");
browser.Document.GetElementById("female").SetAttribute("checked", "1");
browser.Document.GetElementById("bike").SetAttribute("checked", "");
browser.Document.GetElementById("car").SetAttribute("checked", "1");
}
Note that to uncheck a checkbox, an empty string is passed as the attribute
value. Also note that one does not need to "uncheck" a radio button - the
browser handles this state change for all radio buttons grouped by the name
attribute.
Conclusion
Hopefully this code has concisely illustrated how to hook into the web
browser's events to respond programmatically to button clicks and text value
changes, as well as how to restore the web browser's editable controls to a
specific state.