|
|||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
IntroductionI've recently published a couple of articles on CodeProject covering customisation of the MSHTML editor. Those articles are VC7 and MFC7 specific. Naturally there have been some posts asking how one can do the same things using VC6. This article will show you how you can use the MSHTML editor in a VC6 MFC document/view application. BackgroundThe version of MFC that ships with VC6 includes support for displaying HTML documents in a view. You derive your view class from the MFC7, as shipped with VC7, extends this to include a Scope of this articleI didn't set out to completely rewrite the Structural differences between this implemenation and the MFC oneMFC adds HTML editing capability toCHtmlView by bolting on a class called CHtmlEditCtrlBase which actually implements the low
level details. They did it that way so they could reuse the code in the CHtmlEditCtrl class (also new to MFC7). Being a lazy sod I chose to
add the edit support into a direct derivative of CHtmlView called (so as not to conflict with MFC's naming) CHtmlEditorView.
Putting MSHTML into edit modeis trivial. All you do is get anIHTMLDocument2 interface for the document being displayed and call the put_designMode(() function
passing a case sensitive BSTR specifying edit mode as either On or Off. You can only do this once a document has been
loaded into MSHTML and each time you navigate to a new document you need to repeat the process. I do it in the OnNavigateComplete2() event
handler. In code it looks like this.
void CHTMLEditorView::OnNavigateComplete2(LPCTSTR strURL)
{
BSTR bs = SysAllocString(L"On");
CHtmlView::OnNavigateComplete2(strURL);
// Get a pointer to our document
m_pHTMLDoc = (IHTMLDocument2 *) GetHtmlDocument();
if (m_pHTMLDoc != (IHTMLDocument2 *) NULL)
m_pHTMLDoc->put_designMode(bs);
SysFreeString(bs);
}
which will always force the document into edit mode. Once that's done you can click around on the document to your hearts content changing things, moving them
around and generally messing up the page.
What about formatting the content?That's the next step. TheIHTMLDocument2 interface has an execCommand() method that lets you execute commands on the current
selection in the MSHTML editor. For the purpose of this article I've only implemented the Bold command, which looks like this.
void CHTMLEditorView::Bold(BOOL bState)
{
BSTR bCmd = SysAllocString(L"Bold");
VARIANT_BOOL vb;
VARIANT vValue;
vValue.vt = VT_BOOL;
vValue.boolVal = bState;
if (m_pHTMLDoc != (IHTMLDocument2 *) NULL)
m_pHTMLDoc->execCommand(bCmd, VARIANT_FALSE, vValue, &vb);
SysFreeString(bCmd);
}
we set up a BSTR containing our command verb and a VARIANT containing the desired state. Then we call
IHTMLDocument2::execCommand to actually execute the command. The second parameter in the call indicates that we don't want any user interface
shown. In the case of the Bold command there isn't a user interface required but some other commands may support one. That second parameter gives
us the chance to suppress the user interface if we wish. The fourth parameter is a pointer to a VARIANT_BOOL which recieves a value indicating
the success of the operation.
All of the MSHTML commands follow this pattern however it should be clear that the variant type you assign to the variant depends on the datatype which in turn depends on the command. Setting the font face requires a string, setting the text colour requires a specially formatted string and so forth. Querying the current state of a selectionis done using theIHTMLDocument2::QueryCommandState() or IHTMLDocument2::QueryCommandValue() methods. In each case you pass the
name of the command you're querying (for example, is the selection bolded?) and a location to return the value. The demo project has a
button on the toolbar to let you toggle Bold on selected text. Naturally it's expected that if the selection is already bold the button will
be depressed indicating that fact. So whenever we change the selection we need to query the Bold state. The code to do this looks like
BOOL CHTMLEditorView::Bold()
{
BSTR bCmd = SysAllocString(L"Bold");
VARIANT_BOOL vb = FALSE;
if (m_pHTMLDoc != (IHTMLDocument2 *) NULL)
m_pHTMLDoc->queryCommandState(bCmd, &vb);
SysFreeString(bCmd);
return vb;
}
which queries the document for the Bold state. This is returned in the VARIANT_BOOL which we simply return to the caller. The
IHTMLDocument2::queryCommandState() method works only for BOOL properties. If we wanted to query the font name we'd do it like
this
CString CHTMLEditorView::FontName()
{
BSTR bCmd = SysAllocString(L"FontName");
VARIANT vb;
CString csValue;
if (m_pHTMLDoc != (IHTMLDocument2 *) NULL)
m_pHTMLDoc->queryCommandValue(bCmd, &vb);
SysFreeString(bCmd);
if (vb.vt == VT_BSTR)
csValue = CW2CT(vb.bstrVal);
return csValue;
}
which receives the return value in a VARANT. Then it checks that the return value is indeed a string; if so it converts it from the
BSTR returned in the VARIANT to a CString and returns the CString. If the return value isn't a string
the conversion attempt doesn't take place and the empty CString is returned instead. Naturally the caller needs to allow for the case where an
empty CString is returned, which might happen if one were to execute a font name query on an image, which leads to the next topic.
Determing if a command can be executedThis is done by calling theIHTMLDocument2::queryCommandEnabled() method. This takes the command name and a pointer to a VARIANT_BOOL
which receives the return value. If the command is valid for the current selection you get back TRUE, otherwise you get back FALSE.
The code that determines if the Bold command is valid for the current selection looks like this.
BOOL CHTMLEditorView::CanBold()
{
BSTR bCmd = SysAllocString(L"Bold");
VARIANT_BOOL vb = FALSE;
if (m_pHTMLDoc != (IHTMLDocument2 *) NULL)
m_pHTMLDoc->queryCommandEnabled(bCmd, &vb);
SysFreeString(bCmd);
return vb;
}
which is almost identical to querying the current value.
Putting it all togetherSo now we have the code in place to setBold on the current selection, determine if the current selection is Bold or not and
determine if the current selection can be Bolded. We'd use that code in our toolbar Bold button command handler and UI state
update handler like this.
void CHTMLEditorView::OnBold()
{
Bold(Bold() ? 1 : 0);
}
void CHTMLEditorView::OnUpdateBold(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(Bold() ? 1 : 0);
pCmdUI->Enable(CanBold());
}
which is trivial in the extreme.
What commands are available?I refer you to theIHTMLDocument2::execCommand() documentation available online at MSDN for the exhaustive list. I'm not providing a URL because
MSDN sometimes change things around on their site and invalidate links.
Wich versions of Microsoft Internet Explorer will this work with?According to the MSDN documentation theIHTMLDocument2 interface was added in version 4.0 of Microsoft Internet Explorer. It's probably safe to
assume that almost every Windows machine you install your code on will have at least that version. You may run across the occasional command that requires a
later version of Internet Explorer - if so the MSDN documentation will list version requirements.
Using the codeThe source file download contains a copy of theCHtmlEditorView header and implementation files. As aforesaid I've only implemented the
Bold command - it's up to you to add handlers for any other commands you're interested in. The class does include an override of
OnNavigateComplete2() which puts the document into edit mode.
You derive your own class from this, or use the class directly if you wish. History26 April 2004 - Initial version.
|
||||||||||||||||||||||||||||||