NOTICE: As of 11/11/2012, a fully functioning commercial version of this component, with support, is available at http://www.liveswitch.com
Includes the following features:
- Insert inline images either by URL or through the file system. File system images are embedded in the resulting HTML.
- Full programmatic access to HTML, Body HTML, Body Text
- Set the background color of the body.
- Insert images.
- Resize images.
- Add and edit hyperlinks from text.
- Add and edit hyperlinks from images.
- Support for Windows XP SP2 to Windows 8.
- Support for all versions of IE.
Introduction
A while ago, I was working on a chat application where one of the chat clients was web based and written with ASP.NET 2.0. The other chat client was a Windows Forms based .NET application written in C#. I needed a rich text editor for my WinForms client that could output HTML, so that the ASP.NET client could display the formatting from the WinForms client. This was ruled out while using the RTF text box.
The solution I worked out was to use a WebBrowser
control in edit mode within my WinForms client. Formatting buttons on a toolbar above the web browser are synchronized to the current selection in theWebBrowser
control.
This article explains solutions to most of the issues involved in building an editor control from aWebBrowser
control. I won't go into everything, as the source code isn't that difficult to browse. But, I do cover some of the tricks necessary to get this to work.
Setting Design Mode
Applying design mode and establishing an editing template for the document occurs automatically when using the component. But, for reference, here is a brief explanation of how it works.
Applying design mode requires using a COM interface: adding a reference to "Microsoft HTML Object Library", which resolves to MSHTML, and adding a 'using' of 'MSHTML'.
It is necessary to add a body to the control before you can apply changes to the DOM document. To do this, you can simply apply some text to the DocumentText
property of the WebBrowser
control.
Collapse | Copy Code webBrowser1.DocumentText = "<html><body></body></html>"
Next, get a reference to the new DomDocument
COM interface, and set the design mode to "On
".
Collapse | Copy Code IHTMLDocument2 doc =
webBrowser1.Document.DomDocument as IHTMLDocument2;
doc.designMode = "On";
Finally, I replace the context menu for the web browser control so the default IE browser context menu doesn't show up.
Collapse | Copy Code webBrowser1.Document.ContextMenuShowing +=
new HtmlElementEventHandler(Document_ContextMenuShowing);
The browser is now in design mode, with a custom method to display the context menu.
Applying Formatting
You can apply formatting and editor functions to a browser control in design mode with the ExecCommand
method on browser.Document
.
Here are several examples:
Collapse | Copy Code public void Cut()
{
webBrowser1.Document.ExecCommand("Cut", false, null);
}
public void Paste()
{
webBrowser1.Document.ExecCommand("Paste", false, null);
}
public void Copy()
{
webBrowser1.Document.ExecCommand("Copy", false, null);
}
Some commands will toggle the formatting state of the current selection.
Collapse | Copy Code public void Bold()
{
webBrowser1.Document.ExecCommand("Bold", false, null);
}
public void Italic()
{
webBrowser1.Document.ExecCommand("Italic", false, null);
}
Synchronizing Formatting Buttons with Selected Text
This is a bit more tricky than applying formatting commands to the browser control. I literally query the state of the browser editor selection every 200 ms, and set the toolbar formatting buttons based on this.
Collapse | Copy Code private void timer_Tick(object sender, EventArgs e)
{
SetupKeyListener();
boldButton.Checked = IsBold();
italicButton.Checked = IsItalic();
underlineButton.Checked = IsUnderline();
orderedListButton.Checked = IsOrderedList();
unorderedListButton.Checked = IsUnorderedList();
linkButton.Enabled = SelectionType == SelectionType.Text;
UpdateFontComboBox();
UpdateFontSizeComboBox();
if (Tick != null) Tick();
}
You'll notice that there is a Tick
event that is fired at the end of the timer's tick event. External components can subscribe to this event to update the state of their GUI. For example, they may update theEnabled
state of cut/copy/paste/undo/redo buttons based on the state of the Editor control.
I do this by using the COM document interface retrieved from the WebBrowser
control with:
Collapse | Copy Code IHTMLDocument2 doc = webBrowser1.Document.DomDocument as IHTMLDocument2;
Then I use queryCommandState
to determine the state of the current selection:
Collapse | Copy Code public bool IsBold()
{
return doc.queryCommandState("Bold");
}
public bool IsItalic()
{
return doc.queryCommandState("Italic");
}
public bool IsUnderline()
{
return doc.queryCommandState("Underline");
}
The link button and font controls are managed in a similar way, but I'll leave that for you to check out in the code.
Focusing
Focusing the control isn't necessarily simple either. The WebBrowser
control itself will not accept focus. Neither will the WebBrowser
control's document. But, the body
will focus, assuming there is a body
element on the control.
Collapse | Copy Code private void SuperFocus()
{
if (webBrowser1.Document != null &&
webBrowser1.Document.Body != null)
webBrowser1.Document.Body.Focus();
}
Of course, you never need to call this method directly. Calling the Focus
method on the control will place the focus on the editor control containing the WebBrowser
control. The editor control will transfer focus to the WebBrowser
's document body automatically, when it receives the GotFocus
event.
Retrieving Text or HTML
The BodyHtml
and BodyText
methods retrieve the body HTML and text, respectively.
Linking to the Component Assembly
In Visual Studio 2005, you can link to an assembly (add a reference), even if the assembly is an executable. The editor is written as a component embedded in a form, so you can add this component to the control palette and drag and drop into your application. The control's name is Editor
, under namespace Design
.
Conclusion
The .NET 2.0 WebBrowser
control can effectively be used as a text editor. This is useful when you need an editor control that can be used as an HTML editor. The implementation is not completely straightforward in some areas. This article attempts to show some of the tricks necessary to get it to work.