Activating the MSHTML Editor

This topic describes how to activate the MSHTML Editor from Microsoft Visual C++, Microsoft Visual Basic, Microsoft JScript, and Microsoft Visual Basic Scripting Edition (VBScript). The Editor provides a rich set of capabilities and serves as a fine "what you see is what you get" (WYSIWYG) HTML editing environment. In addition, the Editor includes the ability to customize its behavior. These customization features are only available in C++. For more information, see Activating the MSHTML Editor at the end of this topic.

The following topics are discussed in this topic.

  • Activating the MSHTML Editor from C++
    • Using IHTMLDocument2::put_designMode
    • Using IHTMLElement3::put_contentEditable
    • Using DISPID_AMBIENT_USERMODE
  • Activating the MSHTML Editor from Visual Basic
  • Activating the MSHTML Editor from Script and HTML
    • Handling Events in a Content-Editable Region
  • Related topics

Activating the MSHTML Editor from C++

There are three ways to switch MSHTML into editing mode in C++.

Using IHTMLDocument2::put_designMode

IHTMLDocument2 has an IHTMLDocument2::designMode property that takes a BSTR argument. The IHTMLDocument2::designMode property can be switched on and off like this:

// Assume pDoc is a valid IHTMLDocument2 interface pointer
pDoc->put_designMode(L"On"); // switches MSHTML Editor on
pDoc->put_designMode(L"Off"); // switches MSHTML Editor off

The IHTMLDocument2::designMode property is stored with an initial capital letter. It also has an initial value of "Inherit" when the WebBrowser is first activated. Remember this if you test the IHTMLDocument2::designMode property's value. For instance, the following code switches the MSHTML Editor on and off based on the current IHTMLDocument2::designMode value. Note: even if you don't use an initial capital letter in the argument for the put_designMode calls, the method still works correctly.

USES_CONVERSION; // Needed for the OLE2A conversion macro
BSTR bstrMode;

pDoc->get_designMode(&bstrMode);
char* cMode = OLE2A(bstrMode);

// The "O" in "On" must be capitalized
if (strcmp(cMode, "On")) // strcmp returns 0 when
                         // the strings are the same
    pDoc->put_designMode(L"On");
else
    pDoc->put_designMode(L"Off");

Security Warning: Using the strcmp API incorrectly can compromise the security of your application. The strcmp function assumes both of its string parameters to be null-terminated. Passing improperly terminated strings to this function may cause a buffer overrun.

Using IHTMLElement3::put_contentEditable

The MSHTML Editor can also be activated on a per-element basis using the IHTMLElement3::contentEditable property.

// Assume pElement is a valid pointer to an IHTMLElement3 interface

pElement->put_contentEditable(L"true");

Notice that the IHTMLElement3::contentEditable property is set with a BSTR rather than a Boolean value. This is because it has a possible value of "inherit," besides the usual "true" and "false" values.

Before you can make an element editable, you must locate it in the document. Here is how you might locate an element whose IHTMLElement::id is "editableblock."

// Assume m_spDoc is a valid pointer to the IHTMLDocument2 interface
IHTMLElementCollection* pEColl;
IDispatch* pDisp;
IHTMLElement3* pElem;
VARIANT id, index;

V_VT(&id) = VT_BSTR;
V_BSTR(&id) = L"editableblock";
V_VT(&index) = VT_I4;
V_I4(&index) = 0;

m_spDoc->get_all(&pEColl);
pEColl->item(id, index, &pDisp);
pDisp->QueryInterface(IID_IHTMLElement3, (void**)&pElem);
pElem->put_contentEditable(L"true");

// Release interfaces when done
pEColl->Release();
pDisp->Release();
pElem->Release();

When IHTMLElement3::contentEditable is set to "true" on an element, the IHTMLDocument2::designMode property of the document will not affect it. That is, the element content will remain editable even when the instruction pDoc->put_designMode(L"Off") is executed. Any of the editing commands issued on the document as a whole through IOleCommandTarget::Exec will apply to the editable elements in a document. Implementations of IHTMLEditHost and IHTMLEditDesigner will also apply.

Calling IHTMLElement3::contentEditable on the body element is the same thing as calling IHTMLDocument2::designMode on the document.

The IHTMLElementDefaults interface also has a IHTMLElementDefaults::contentEditable property.

Using DISPID_AMBIENT_USERMODE

The final way to activate the MSHTML Editor uses an ambient property through an IDispatch interface implemented by an application hosting the WebBrowser. To switch between browsing and editing this way, you need to implement IDispatch so that it handles the DISPID_AMBIENT_USERMODE message passed into IDispatch::Invoke:

HRESULT
CAtlBrCon::Invoke(DISPID dispidMember,
                  REFIID riid,
                  LCID lcid, WORD wFlags,
                  DISPPARAMS* pDispParams,
                  VARIANT* pvarResult,
                  EXCEPINFO*  pExcepInfo,
                  UINT* puArgErr)
{
    switch (dispidMember)
    {
        case DISPID_AMBIENT_USERMODE:

            V_VT(pvarResult) = VT_BOOL;
            V_BOOL(pvarResult) = m_bBrowseMode ?
                            VARIANT_TRUE : VARIANT_FALSE;

            break;
            .
            .
            .

Here, IDispatch::Invoke sets the out-parameter pvarResult that is returned to MSHTML based on the value of the variable m_bBrowseMode. Passing VARIANT_TRUE back to MSHTML in the pvarResult parameter switches the browser into browse mode. Passing VARIANT_FALSE switches it into editing mode.

Once your IDispatch interface is implemented, you must trigger MSHTML to call your IDispatch::Invoke implementation. To do this, use the IOleControl::OnAmbientPropertyChange method of an IOleControl interface obtained from the IWebBrowser2 control.

IOleControl* pControl;

hr = m_spWebBrowser->QueryInterface(IID_IOleControl,
                                    (void**)&pControl);

pControl->OnAmbientPropertyChange(DISPID_AMBIENT_USERMODE);

pControl->Release();

In this example, the call to IOleControl::OnAmbientPropertyChange causes MSHTML to call the IDispatch::Invoke method passing DISPID_AMBIENT_USERMODE for the DISPID.

Activating the MSHTML Editor from Visual Basic

In Visual Basic, you can use the MSHTML Editor on the document as a whole through the IHTMLDocument2::designMode property. You can make an individual element editable by using the element's IHTMLElement3::contentEditable property. To use the IHTMLDocument2::designMode property, obtain the document object from either the WebBrowser object or the InternetExplorer object and change its IHTMLDocument2::designMode property.

Set doc = WebBrowser1.Document

If doc.DesignMode = "On" Then
    doc.DesignMode = "Off"
Else
    doc.DesignMode = "On"
End If

As with C++, the IHTMLDocument2::designMode property is stored with an initial capital letter. Keep this in mind if you test its value.

For editing on a per element basis, identify the element you want to make editable and set its IHTMLElement3::contentEditable property to True:

Set doc = WebBrowser1.Document
Set allColl = doc.All
Set elem = allColl.Item("editableblock")
elem.contentEditable = True

The IHTMLElement3::contentEditable property can be set with either a Boolean value or a string.

Setting the IHTMLElement3::contentEditable property to True on the body element is the same thing as setting the IHTMLDocument2::designMode property to "On."

Activating the MSHTML Editor from Script and HTML

Activating the Editor from script is very similar to activating it from Visual Basic. You can use either the document's IHTMLDocument2::designMode property or an element's IHTMLElement3::contentEditable property.

document.designMode = "On"

document.all("editableblock").contentEditable = true;

The syntax in JScript is case-sensitive; in VBScript, it is not. However, the IHTMLDocument2::designMode property itself is case sensitive, as always, if you test it.

You can also make an element editable in HTML by setting the CONTENTEDITABLE attribute:

<div CONTENTEDITABLE="true">The text in this div is editable.</div>

If you set the CONTENTEDITABLE attribute or IHTMLElement3::contentEditable property on the body element, it's the same thing as setting the document's IHTMLDocument2::designMode property to "On." The IHTMLDocument2::designMode property cannot be used as an HTML attribute.

Handling Events in a Content-Editable Region

Handling selection events in a IHTMLElement3::contentEditable region has some special considerations in script. These considerations are illustrated in the scripting sample for the article Modifying Documents in Edit Mode.

Code example: http://samples.msdn.microsoft.com/workshop/samples/browser/webediting/onepageeditor.htm

When building an HTML application using content-editable regions, a developer might include an editable area and a toolbar whose buttons allow actions on the selection in the editable area. For instance, clicking one button might make the selection bold, clicking another might italicize the selection, and so on. However, a problem arises when the user clicks a button. Because the focus moves from the editable region to the button before the user clicks, the selection object also changes and will no longer contain a selection made in the editable region.

To resolve this difficulty, use the UNSELECTABLE attribute on the toolbar buttons. The UNSELECTABLE attribute does two things. First, it makes an element unselectable. This attribute is not inherited; it must be set on every element that you want unselectable. Second, clicking in an UNSELECTABLE element does not change the previous selection. Anything that was selected before the UNSELECTABLE element was clicked will remain selected after the click. This enables you to perform actions on the editor selection in the event handlers for the buttons.

Conceptual

Introduction to MSHTML Editing

Modifying Documents in Edit Mode

Using the MSHTML Editor's Extra Features

Using Editing Glyphs

Implementing IHTMLEditHost

About Edit Designers