Geeks With Blogs
The Quandary Phase This code was generated by a tool.

Wow... that's a lot of capitals and acronyms to squeeze into a single title (well, ok... one acronym, and two initialisms, if you want to split hairs). Anyhoo... after roundly dismissing the WPF WebBrowser control in an earlier article, I now find it's (ahem) not quite so bad after all.

I needed to create a WYSIWYG HTML editor in WPF for a project I'm currently working on. There are quite a few commercial WYSIWYG editors based on the Windows Forms WebBrowser control, and also quite a few examples of free ones on CodeProject etc, but I didn't spot any out there using the WPF WebBrowser control at the time.

We didn't really want another interoperability layer between our WPF app, and the ActiveX control which forms the backbone of the WPF WebBrowser, so we wanted to avoid the Windows Forms solutions- although it is quite possible to embed one of these onto a WPF Window using a WindowsFormsHost. In the end, we decided to roll our own, though it should be pointed out that it owes a debt of gratitude to the existing windows forms implementations such as the one linked to above.

The main issue to overcome when developing the control is the necessity of working with the Microsoft HTML Object Library COM component (MSHTML for short). If you want to execute any functionality against the document exposed by the WebBrowser control, you will have to start getting familiar with this component (for anyone who has not used it before- in Visual Studio, click add reference, navigate to the COM tab, scroll down till you see Microsoft HTML Object Library. Once added, you need to import the mshtml namespace in your class).

The document exposed by the WebBrowser control is initially null, until you browse to a valid URI, or load some content into the control using the NaviagteToString or NavigateToStream methods. Once loaded, the document is of type HTMLDocumentClass, which you will find in the mshtml namespace- once you have casted the document to this type, you can access its properties and methods.

It turns out there is a wealth of useful functionality available here which isn't exposed via the WebBrowser control itself- functionality for formatting html in the WebBrowser, and (crucially) for accessing the designMode property of the document, which effectively changes it from a read-only view of the HTML to an editable one which accepts user input (which is 90% of the work done already, in a single line of code!). Of course, the Windows Forms WebBrowser control exposed this property itself, and I'm sure that the WPF one will also do so in its next release, but for the time being, the only way to get at it is via the native HTMLDocument.

I also made the decision during development of the control to avoid any explicit non-.NET dependencies. In particular, this meant that I couldn't add a direct reference to the Microsoft HTML Object Library. Why did I do this? Well, for one thing, we work in a continuous integration environment, but our continuous integration build box has an older version of the MSHTML library, so every time I checked in the code with an MSHTML reference, it was breaking the build.

I solved this by late binding to the MSHTML library, however not the way you might think...

As a c# developer, my first instinct was to rewrite the code without the MSHTML reference using reflection to perform the late binding. However, I quickly found it was turning out to be a pretty ugly coding experience, with quite a lot of nested GetMethod and InvokeMember calls. I then remembered that VB.NET is able to do late binding at the language level: if you switch Option Strict off you essentially turn VB.NET almost into a scripting language, which is exactly what I wanted here. So I created a VB.NET wrapper for the HTMLDocument which exposes the functionality I need, which works very well indeed, and now consists of code several orders of magnitude more readable than the C# equivalents (although it will be possible to achieve the same thing in c# 4.0 when it comes out, using the new dynamic features).

All well and good so far... I now had the HTML document doing what I wanted, and I even nicked a nifty colour picker from the good folks at Microsoft, in order to enable font colour and highlight colour selection.

I exposed a few methods for loading HTML content: either from a specific URI, or from text content, or a stream. Then added a routed event which reports when the current HTML is edited.

The next task was to replicate a few pieces of functionality people generally expect to be available in HTML editors, but which isn’t provided via the MSHTML object model. The first is the ability to create ordered and unordered lists, the second is the inserting of hyperlinks, and the third is the ability to align the selected text. Each of these tasks, I accomplished with the help of the indispensible HTML Agility Pack HTML Parser. I used this to parse the HTML and manipulate the DOM of the loaded document directly.

Then, just when I thought I had it all cracked, I integrated it into the app, and found that the pesky ActiveX control was not quite behaving as expected. Every time the user edited the HTML document, then attempted to load a new one, a dialogue box was being generated prompting the user that the current content had changed, and asking them if they wanted to save this updated HTML content to disk, or discard the changes.

Given that our end users have most likely never even heard of HTML, let alone have any wish to save some to their disks, this was not good. Try as I might, I simply couldn't find any good way to suppress these dialogue boxes. Whatever properties I set on the document, or on the control, up it popped each time, like that hugely irritating paperclip in older versions of office.

Eventually, I found an article which had gone some way towards solving this exact problem, which provides a suitably tortuous hack which works by hooking the WndProc and listening out for the message which is raised when new windows are created. As the dialogue box is always opened with the same text in the title bar, each time a new window is created, its title bar text is examined, if it matches the dialogue window's text, the message is swallowed before the window is shown. Excellent!

However, one problem remained: because the window that is created by the control is an Yes/No/Cancel dialogue- if you just kill the window, it returns a dialogue result of 'Cancel' which results in nothing happening; remember the window is asking the user to confirm whether they want to A- save the updated html content to disk, B- discard the changes and continue, or C- cancel the loading of the new document. So, the final task was to find a way to programmatically click the 'No' button, so the modifications are discarded without the user ever seeing the window. This, I accomplished by way of the EnumChildWindows windows hook to recursively search through the elements on the dialogue window, until it finds the 'No' button, then the SendMessage win32 function to simulate a button click. This, finally, cured the problem of the recurring popup.

All in all, quite a bit of work to re-implement a lot of functionality which came for free on the Windows Forms HTML editor, but a good learning exercise along the way. The source code can be downloaded from the link at the top of the article.

Update (9/4/2009): I have uploaded version 1.1 of the editor, which fixes a couple of bugs around the alignment and formatting.

Update (20/06/2009): I have uploaded version 1.2 today with a couple more stability fixes.

Posted on Friday, March 13, 2009 6:02 PM | Back to top


Comments on this post: WPF WYSIWYG HTML Editor

# re: WPF WYSIWYG HTML Editor
Requesting Gravatar...
Great article - exactly the functionailty I was just about to develop. Your research will prove invaluable I'm sure. Many thanks.
Left by Matthew Hammond on Mar 20, 2009 8:34 PM

# re: WPF WYSIWYG HTML Editor
Requesting Gravatar...
This is a really nice peice of work. I am having trouble setting the InnerHtml on load of my test app. If there is a way to do this please let me know.
Left by nate22405 on Apr 09, 2009 3:37 PM

# re: WPF WYSIWYG HTML Editor
Requesting Gravatar...
@nate22405: I just uploaded version 1.1 today with a number of significant bug fixes. Perhaps this will fix your issue?
Left by adampooler on Apr 09, 2009 5:03 PM

# re: WPF WYSIWYG HTML Editor
Requesting Gravatar...
Great work!, but how do You initialize the control with a string? (TextBox.TEXT = "My string";)
Left by Lester on May 01, 2009 4:40 AM

# re: WPF WYSIWYG HTML Editor
Requesting Gravatar...
@Lester: In order to load content into the control, you call the public Navigate method. One of the overloads of this method accepts a string parameter, to which you can pass the HTML you want to display.
Left by adampooler on May 03, 2009 10:43 AM

# re: WPF WYSIWYG HTML Editor
Requesting Gravatar...
@Mike. Thanks! This component is free- use at your own will and discretion. I don't, however, provide any support I'm afraid.

Use in an XBAP app would be tricky because of all the interop code, and the fact that the entire control is built on top of a COM component. It is possible, but it would need to execute in full trust, which implies a signed XBAP.

This might be OK for an intranet app, but trying to run with elevated security privileges across the web is defnitely to be avoided.

There are plenty of good (and free!) web-based WYSIWYG HTML editors around though, such as this one:

http://freetextbox.com/
Left by adampooler on May 14, 2009 2:39 PM

# re: WPF WYSIWYG HTML Editor
Requesting Gravatar...
What are the requirements, does it works with IE 6.0 ?
Left by Peter on May 18, 2009 2:27 AM

# re: WPF WYSIWYG HTML Editor
Requesting Gravatar...
@Peter: no this is for WPF applications, though you may be able to get it to work in a signed XBAP (see comments above).
Left by adampooler on May 18, 2009 9:29 AM

# re: WPF WYSIWYG HTML Editor
Requesting Gravatar...
ok, thanks, what are the .Net requirements, does it need .Net 3.5 ?
Left by Peter on May 18, 2009 9:44 PM

# re: WPF WYSIWYG HTML Editor
Requesting Gravatar...
ack, download link not working.
Left by Ken on Jun 09, 2009 10:59 AM

# re: WPF WYSIWYG HTML Editor
Requesting Gravatar...
@Ken: none of the downloads from my articles are currently working: I'm currently waiting on GeeksWithBlogs for support...
Left by adampooler on Jun 09, 2009 3:56 PM

# re: WPF WYSIWYG HTML Editor
Requesting Gravatar...
@Ken: I have fixed the link...I have posted the zip file to Windows Live Skydrive and pointed the link at it. Will think twice before entrusting uploaded files to GWB in future...
Left by adampooler on Jun 10, 2009 9:26 AM

# re: WPF WYSIWYG HTML Editor
Requesting Gravatar...
Looks like the download link has gone south again. Any chance it can be restored?
Left by Gerry on Jun 20, 2009 7:31 AM

# re: WPF WYSIWYG HTML Editor
Requesting Gravatar...
@Gerry: I was in the process of deploying a new version when you tried the link. You should find it working again now.
Left by adampooler on Jun 20, 2009 9:20 AM

# re: WPF WYSIWYG HTML Editor
Requesting Gravatar...
hello,
great job, I'm modifying it to fit my needs (a simple html editor).

I'm trying to understand when the webBrowser has Focus but the GotFocus() event never fires up.. is it possible to extend the HtmlDocumentAdapter with such an event?
I need to create a Context menu too, is it possible?
thank you
Left by Cristian on Jun 25, 2009 9:15 AM

# re: WPF WYSIWYG HTML Editor
Requesting Gravatar...
Silverlight is hosted in web browser and within that another Web Browser control?
We had enough problem in the MsHtml rendering library for our PDF print component,
Left by VB Reader on Sep 25, 2009 3:21 AM

# re: WPF WYSIWYG HTML Editor
Requesting Gravatar...
@VB Reader: I'm not really sure what you're asking. However, the control above is not intended for use in Silverlight- and definitely wouldn't work in a Silverlight app because it requires unmanaged code calls.
Left by adampooler on Sep 25, 2009 3:56 AM

# re: WPF WYSIWYG HTML Editor
Requesting Gravatar...
Great tool Adam !
Is there a chance that you convert the project to a C# solution ?
C# would be much more usable for guys like me that stuck with boo ...
thanks.
Left by Doc.NET.Noodles on Oct 18, 2009 1:41 AM

# re: WPF WYSIWYG HTML Editor
Requesting Gravatar...
@Doc.NET.Noodles: it is in c#! There is one VB.NET project in there which does the HtmlDocument COM interop as mentioned in the article, but other than that it's c# all the way.
Left by adampooler on Oct 18, 2009 3:41 AM

# re: WPF WYSIWYG HTML Editor
Requesting Gravatar...
Hi, how do i get the html without leading doctype and <html> tag? in other words just the html i have created in the editor. Great work!
Left by Andreas on Nov 06, 2009 2:10 AM

# re: WPF WYSIWYG HTML Editor
Requesting Gravatar...
@Andreas: there isn't anything built into the control for this, but you can use the HTML parser in the HTML Agility Pack for this:

http://www.codeplex.com/htmlagilitypack

This is the same parser I used in the project for a couple of operations- it's an extremely useful utility. And also free!
Left by adampooler on Nov 06, 2009 2:27 AM

# re: WPF WYSIWYG HTML Editor
Requesting Gravatar...
thanx, htmlagilitypack is pure magic!

But now i have a new problem. The Editor works great on my xp pro dev machine but when I'm running the app on a 2003 server std sp2 (32bit) i get a system.nullreferenceexception in this method:

Public Sub ExecuteCommand(ByVal commandName As String, ByVal value As Object) in Boo.HtmlEditor.Interop. There seams to be some trouble with the reflection.

Any suggestions?
Left by Andreas on Nov 09, 2009 4:27 AM

# re: WPF WYSIWYG HTML Editor
Requesting Gravatar...
Hello guys, This article is what i wanted. Thanks!!!!. I have a question related to saving of the html file.
My html file is on the server (windows 2003), when I click Yes to save the file, it saves locally on your system. I want the changes to be saved on the server from it originated with no browse dialog showing. Please any suggestion on this would be great help for me.

Thanks in advance
Mithun
Left by Mithun on Feb 19, 2010 9:41 AM

# re: WPF WYSIWYG HTML Editor
Requesting Gravatar...
Its great article that helped me lot.
Also How and when the SourceUpdated event called.. I want to get the source of the editor...
Please help me

Thanks
Prabakar
Left by Prabakar on Aug 25, 2010 7:38 PM

# re: WPF WYSIWYG HTML Editor
Requesting Gravatar...
This control does not seem to work correctly on a form with other text boxes. Here is some code for reference (the cursor keeps giving the txtMessageSubject control the focus for some reason):

<TabItem Name="tabMessage" Header="Message">
<DockPanel LastChildFill="True">
<DockPanel DockPanel.Dock="Top" LastChildFill="True" Margin="10">
<Label DockPanel.Dock="Left" Content="Email Subject" />
<TextBox Name="txtMessageSubject" />
</DockPanel>
<StackPanel DockPanel.Dock="Bottom" Orientation="Vertical" Margin="10">
<StackPanel Orientation="Horizontal" Margin="0,0,0,5">
<Label Content="Plain Text Message for clients that do not support HTML mail" />
<Button Name="btnCopyMessageFromHtml" Content="Copy from HTML" Click="btnCopyMessageFromHtml_Click" />
</StackPanel>
<TextBox Name="txtPlainTextMessage" Height="100" AcceptsReturn="True" />
</StackPanel>
<DockPanel LastChildFill="True" Margin="10">
<Label DockPanel.Dock="Top" Content="HTML Message" />
<editor:HtmlEditor Name="txtHtmlMessage" BorderBrush="Gray" BorderThickness="1" />
</DockPanel>
</DockPanel>
</TabItem>
Left by Bob M on Sep 14, 2010 9:09 AM

# re: WPF WYSIWYG HTML Editor
Requesting Gravatar...
GREAT TOOL !!!
Left by marco on Nov 30, 2010 2:33 AM

# re: WPF WYSIWYG HTML Editor
Requesting Gravatar...
I found this is an informative and interesting post so i think so it is very useful and knowledgeable. I would like to thank you for the efforts you have made in writing this article.My html file is on the server (windows 2003), when I click Yes to save the file, it saves locally on your system. I want the changes to be saved on the server from it originated with no browse dialog showing. Please any suggestion on this would be great help for me.
Left by Indonesian Teak Furniture on Dec 07, 2010 5:11 AM

# re: WPF WYSIWYG HTML Editor
Requesting Gravatar...
Excellent tool thanks, I am using this with some fudged binding done manually on HtmlChanged event and works great, however this event is not fired on inserting an image, have to do something else (eg put a space after the image) before the event fires, any idea on how to capture an image insert or make it fire the HtmlChanged event?

Many Thanks
Left by mchood on Jan 19, 2011 9:51 PM

# re: WPF WYSIWYG HTML Editor
Requesting Gravatar...
It reminds me another phrase WYSIMOLWYG, What You See Is More Or Less What You Get. Thank for sharing it.
Left by cheap custom written paper on Jan 29, 2011 3:50 AM

# re: WPF WYSIWYG HTML Editor
Requesting Gravatar...
I have downloaded the example posted on top. Great work. However, most of the buttons (bold, italic etc) dont work AFTER pushing the "Open New Document". Could you shed a light on this one?
Left by Lasse Johansen on Feb 11, 2011 10:42 AM

# re: WPF WYSIWYG HTML Editor
Requesting Gravatar...
Thanks for the Good Editor.

But I had the same Problem with the Buttons that dont work after pushing another GuiElement.

I resolved the problem for me by integrating the menu in the editor and use the Click Events direkt instead of the CommandBinding.
Left by Dirk Stüben on Mar 08, 2011 11:23 PM

# re: WPF WYSIWYG HTML Editor
Requesting Gravatar...
Post liked, informative, method of conducting very interesting, confident that practical training is suitable for use in the available information. We hope for further cooperation
Left by essay service writing on Mar 10, 2011 8:11 AM

# 先生
Requesting Gravatar...
I love the way the people here interact and shared their opinions too. I would love to track your future posts pertaining to the said topic we are able to read.
Left by Cheap Ray Ban Sunglasses on Mar 17, 2011 8:55 PM

# re: WPF WYSIWYG HTML Editor
Requesting Gravatar...
That was a terrific article. I don't agree with every single single thing that you said but still great nonetheless.That was a terrific article. I don't agree with every single single thing that you said but still great nonetheless.
Left by attitude quotes on Mar 23, 2011 8:29 PM

# re: WPF WYSIWYG HTML Editor
Requesting Gravatar...
You could use TinyMCE WYSIWYG editor, i think it' free
Left by roof racks for cars on Mar 23, 2011 9:49 PM

Comments have been closed on this topic.
Copyright © Adam Pooler | Powered by: GeeksWithBlogs.net