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
namespace WYSIWYGnet2._
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.Load += new EventHandler(Form1_Load);
}
private mshtml.IHTMLDocument2 doc;
void Form1_Load(object sender, EventArgs e)
{
doc = (mshtml.IHTMLDocument2)this.webBrowser1.Document.DomDocument;
doc.designMode = "On";
doc.write("Hello World!");
}
private void BoldBut_Click(object sender, EventArgs e)
{
doc.execCommand("Bold", false, null);
}
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##";
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";
}
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();
}
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.