Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / WinForms

WYSIWYG HTML Editor in .NET 2.0 C# version

2.53/5 (13 votes)
18 Jul 20074 min read 1   1.9K  
Barebones Example on how to use .NET 2.0 Components to build a WYSIWYG HTML Editor
Screenshot - screenie.gif

Introduction

Welcome to the wonderful world of .Net 2.0

For several weeks now I have been trying to build a WYSIWYG HTML editor for my employer. Unfortunately, they have the wonderful tendency of not providing a) Any software development tools b) Any internet access and c) Want the world to be delivered on Wednesday.

First I tried to use a RichTextBox, and spent the better part of a week developing a HTML->RTF and a RTF->HTML converters. As the project rolled on it was soon apparent that there was a need for more complicated layout controls, thus the research began. (BTW all that code is sitting on a work machine, locked away, no file transfers in and no file transfers out - the code here is something I threw together with some free time and what I remembered about what I did here at home). I was so exasperated at one point I considered developing this in C or C++, but since it took the better part of a year just to get the .NET Framework and SDK installed on the system, I wouldn't try and press my luck trying to get a C complier on there.

I started out trying to figure out the AxWebBrowser control. First I had to import the SHDocVw.dll using Aximp, then I had to import the MSHTML.tbl with the TblImp command then… I was pulling my hair out. All other code I have seen so far calls upon this control.

After working for a while, I got the code settled in and added a lot of functionality. I started to test the executable in a distributed setting. Finally, after thinking I had everything fine and settled, I got these ugly COM errors on the non-Development computers:

"Unable to cast System._ComObject into type MSHTML.HTMLDocumentClass because interlop assembly is not recognized… but you can cast it to an interface if one exists" I just about blew a gasket.

So I did some more digging and discovered that Microsoft has gladly provided a nice Microsoft.mshtml.dll reference assembly in their SDK (%INSTALLLOCATION%\SDK\PRIMARY INTEROP ASSEMBLIES). That takes care of the MSHTML import deal, now if only I could get an interop for the browser.

Wait, System.Windows.Forms has a nice WebBrowser control (one I can do all sorts of nice event setting on) already. Lets see if I can use that.

Turns out you can. So I went ahead and replaced the AxWebBrowser control with the standard Windows.Forms control - by doing so I saved a lot on memory and overhead as additional DLLs weren't needed to load into memory.

1) Add a reference to the Microsoft.mshtml.dll (either browse and select the DLL manually) or perhaps the Microsoft.mshtml assembly is already loaded in your GAC.

2) Add a webbrowser object to your form, being sure to set its default url to about:blank (e.g. webBrowser1.Navigate("about:blank");

3) Get a reference to the document and create an interface reference to an unmanaged document (allowing you to do things like edit the document contents).

4) Enjoy. All of this uses standard .NET 2.0 assemblies and no need to play around with AxSHDocVw, SHDocVw, or MSHTML.tbl.

Code example

C#
namespace WYSIWYGnet2._
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            this.Load += new EventHandler(Form1_Load);
        }

        /// <summary>
        /// Private property to hold the document you will be editing
        /// </summary>
        private mshtml.IHTMLDocument2 doc;
        
        
        /// <summary>
        /// Initialization function which sets the editable document to the display in the webbrowser
        /// as well as turn the control into an editable area.
        /// 
        /// Please note, that you must have some sort of data loaded into the webbrowser before you can work
        /// with the document element - hence in the designer code, the web broser initial URL is set to about:blank
        /// </summary>
        void Form1_Load(object sender, EventArgs e)
        {
            /*If you haven't already initalized the webBrowser with a blank document
             * do so here before you try to capture a null reference
             * this.webBrowser1.Navigate("about:blank");
             */

            doc = (mshtml.IHTMLDocument2)this.webBrowser1.Document.DomDocument;
            doc.designMode = "On";
            doc.write("Hello World!");
            

        }

        /// <summary>
        /// Example function for executing commands to the document.
        /// 
        /// Other popular commands are:
        /// 
        /// Cut, Paste, Copy, Underline, Italics
        /// CreateLink, CreateOrderedList, CreateUnorderedList
        /// 
        /// lots more - see the CommandIdentifiers section at MSDN
        /// </summary>
        private void BoldBut_Click(object sender, EventArgs e)
        {
            doc.execCommand("Bold", false, null);
        }


        /// <summary>
        /// Example function working with text ranges and the selected text.
        /// </summary>
        private void SelBut_Click(object sender, EventArgs e)
        {
            mshtml.IHTMLTxtRange txt = (mshtml.IHTMLTxtRange) doc.selection.createRange();
            MessageBox.Show("Text Selected: " + txt.text);
            MessageBox.Show("HTML Selected: " + txt.htmlText);


            MessageBox.Show("Inserting Peter Pan into selection point");
            string insert_text = "##INSERTPOINT##";
            
            /* Note, if you wish to preserve the original text
             * you would need to create an additional variable to hold the temporary text
             */
            
            txt.text = insert_text;

            doc.execCommand("SelectAll", false, null);
            txt = (mshtml.IHTMLTxtRange) doc.selection.createRange();
            txt.findText(insert_text,0,0);
            txt.select();

            txt.text = "Peter Pan";
           
        }


        /// <summary>
        /// Shows how to insert HTML code directly into the document.
        /// </summary>
        private void InsHTMLBut_Click(object sender, EventArgs e)
        {
            if (this.HTMLTB.Text.Length == 0)
            {
                MessageBox.Show("Nothing to insert");
                return;
            }
            mshtml.IHTMLTxtRange txt = (mshtml.IHTMLTxtRange) doc.selection.createRange();
            txt.pasteHTML(this.HTMLTB.Text);
            this.HTMLTB.Clear();
           
        }


        /// <summary>
        /// Shows how to extract the source of the body of the document.
        /// </summary>
        private void VwSrcBut_Click(object sender, EventArgs e)
        {
            MessageBox.Show(doc.body.innerHTML);
        }
    }
}

Concluding Remarks

All of this code is simply here for you to get a basic introductory understanding of how you can use .NET components to do what you used to have to spend ages and ages trying to import and set assemblies and all that fun stuff. Additionally, this code is just a snippet - the actual components need to be created and initialized (see the included source).

A quick note on distribution. The Microsoft.mshtml.dll is not part of the standard framework package – but is part of the SDK (ever since they got out of beta 2.0) to avoid confusion, be sure to set the property (copy to output directory to true) or include a copy of the dll in your assembly distribution. If you are leery of redistribution rights, Microsoft has included this with their Primary Interop Assembly installer, and has redistributable packages you can distribute with your app.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here