Chapter 13

The Rich Edit Control


If you took all of the energy that's been expended on writing text-editing software and concentrated that energy on other, less mundane programming problems, computer science would probably be a decade ahead of where it is now. Okay, that might be an exaggeration, but it is true that, when it comes to text editors, a huge amount of effort has been dedicated to reinventing the wheel. Wouldn't it be great to have one piece of text-editing code that all programmers could use as the starting point for their own custom text editors?

With Visual C++'s CRichEditCtrl control, which gives programmers access to Windows 95's rich edit control, you can get a huge jump on any text-editing functionality that you need to install in your applications. The rich edit control is capable of handling fonts, paragraph styles, and text color, as well as handling other types of tasks that are traditionally found in text editors. In fact, a rich edit control provides a solid starting point for any text-editing tasks that your application must handle.

Introducing the Rich Edit Control

For all intents and purposes, you can consider the rich edit control to be a sort of "black box" word processor. That is, to add text-editing capabilities to your MFC application, you need to do very little programming. Nor do you need to know much about the inner workings of a rich edit control. Just place the control in your application's main window and call the control object's member functions to control it. A rich edit control enables the user to perform the following text-editing tasks:

As the preceding list proves, a rich edit control is powerful. It is, in fact, almost a complete word-processor-in-a-box that you can plug into your program and use immediately. Of course, because a rich edit control offers so many features, there's a lot to learn. In this chapter, you'll get an introduction to creating and manipulating the rich edit control.

Creating the RichEdit Application

There are a couple of ways you can add rich edit text-editing capabilities to your application using Visual C++. The first way is to add a rich edit control to your application's frame or view window. When you use this method, you are completely responsible for dealing with the control. Another way to add a rich edit control to your application is to make your view window an object of MFC's CRichEditView class. When you use this method, MFC handles much of the work for you.

In this section, you'll see how to directly create and manipulate a rich edit control. Using this method, you get an introduction to the control itself and how your MFC program manipulates it directly. Just follow these steps to create the RichEdit application:

ON THE WEB

http://www.quecorp.com/semfc The complete source code and executable file for the RichEdit application can be found in the CHAP13\RichEdit directory at this book's Web site.

1. Start a new AppWizard project workspace called RichEdit, as shown in Figure 13.1.


FIG. 13.1

Here's how you should start the RichEdit project workspace.

2. Give the new project the following settings in the AppWizard dialog boxes. The New Project Information dialog box should then look like Figure 13.2.

Step 1: Single document

Step 2: Default settings

Step 3: Default settings

Step 4: Turn off Printing and Print Preview

Step 5: Default settings

Step 6: Default settings


FIG. 13.2

These are the AppWizard settings for the RichEdit project.

3. Using the resource editor, remove the application's Edit menu. Also, remove all items from the File menu except for Exit.

4. Using the resource editor, remove all of the buttons from the application's toolbar.

5. Add four buttons to the application's toolbar, as shown in Figure 13.3. Give the buttons the IDs ID_UNDERLINED, ID_LEFT, ID_CENTERED, and ID_RIGHT.


FIG. 13.3

Use the resource editor's painting tools to add four buttons to the application's toolbar.

6. Use ClassWizard to associate the ID_UNDERLINED command with the OnUnderlined() message response function, as shown in Figure 13.4. Make sure that you have CMyRichEditView selected in the Class Name box before you add the function.


FIG. 13.4

Add the OnUnderlined() message response function to the view class.

7. Click the Edit Code button, and then add the lines shown in Listing 13.1 to the new OnUnderlined() function, right after the TODO: Add your command handler code here comment:

Listing 13.1 LST13_01.CPP Code for the OnUnderlined() Function

    CHARFORMAT charFormat;
    charFormat.cbSize = sizeof(CHARFORMAT);
    charFormat.dwMask = CFM_UNDERLINE;
    m_richEdit.GetSelectionCharFormat(charFormat);
    if (charFormat.dwEffects & CFM_UNDERLINE)
        charFormat.dwEffects = 0;
    else
        charFormat.dwEffects = CFE_UNDERLINE;
    m_richEdit.SetSelectionCharFormat(charFormat);

8. Use ClassWizard to associate the ID_LEFT command with the OnLeft() message response function, as shown in Figure 13.5. Again, make sure that you have CMyRichEditView selected in the Class Name box before you add the function.


FIG. 13.5

Add the OnLeft() message response function to the view class.

9. Click the Edit Code button, and then add the lines shown in Listing 13.2 to the new OnLeft() function, right after the TODO: Add your command handler code here comment:

Listing 13.2 LST13_02.CPP Code for the OnLeft() Function

    PARAFORMAT paraFormat;
    paraFormat.cbSize = sizeof(PARAFORMAT);
    paraFormat.dwMask = PFM_ALIGNMENT;
    paraFormat.wAlignment = PFA_LEFT;
    m_richEdit.SetParaFormat(paraFormat);

10. Use ClassWizard to associate the ID_CENTERED command with the OnCentered() message response function in the view class, as shown in Figure 13.6.


FIG. 13.6

Add the OnCentered() message response function to the view class.

11. Click the Edit Code button, and then add the lines shown in Listing 13.3 to the new OnCentered() function, right after the TODO: Add your command handler code here comment:

Listing 13.3 LST13_03.CPP Code for the OnCentered() Function

    PARAFORMAT paraFormat;
    paraFormat.cbSize = sizeof(PARAFORMAT);
    paraFormat.dwMask = PFM_ALIGNMENT;
    paraFormat.wAlignment = PFA_CENTER;
    m_richEdit.SetParaFormat(paraFormat);

12. Use ClassWizard to associate the ID_RIGHT command with the OnRight() message response function in the view class, as shown in Figure 13.7.


FIG. 13.7

Add the OnRight() message response function to the view class.

13. Click the Edit Code button, and then add the lines shown in Listing 13.4 to the new OnRight() function, right after the TODO: Add your command handler code here comment:

Listing 13.4 LST13_04.CPP Code for the OnRight() Function

    PARAFORMAT paraFormat;
    paraFormat.cbSize = sizeof(PARAFORMAT);
    paraFormat.dwMask = PFM_ALIGNMENT;
    paraFormat.wAlignment = PFA_RIGHT;
    m_richEdit.SetParaFormat(paraFormat);

14. Use ClassWizard to override the OnCreate() message response function in the view class, as shown in Figure 13.8.


FIG. 13.8

Add the OnCreate() message response function to the view class.

15. Click the Edit Code button, and then add the following line to the new OnRight() function, right after the TODO: Add your command handler code here comment:

    m_richEdit.Create(WS_CHILD | WS_VISIBLE | WS_BORDER |
        ES_AUTOVSCROLL | ES_MULTILINE,
        CRect(0,0,388,302), this, 111);

16. Load the RichEditView.h header file, and add the following lines to the class's Attributes section, right after the line CMyRichEditDoc* GetDocument() that's already there:

protected:
    CRichEditCtrl m_richEdit;

17. Load the MainFrm.cpp file, and add the following lines to the beginning of the PreCreateWindow() function:

    cs.cx = 400;
    cs.cy = 400;

You've now completed the RichEdit application. To compile the application, choose Developer Studio's Build, Build command. You can then run the program by choosing the Build, Execute command. When you do, the application's main window appears. First, click in the window to give the rich edit control the focus. Then just start typing. Want to try out character attributes? Click the Underline button to add underlining to either selected text or the next text that you type. To try out paragraph formatting, click either the Left, Center, or Right buttons to specify paragraph alignment. Figure 13.9 shows the rich edit control with the different character and paragraph styles used.


FIG. 13.9

A RichEdit control is a complete word processor.

If you carefully examine the RichEdit application's main window, you'll notice that you can see both the window's border and the rich edit control's border. The application shows the control's border so that you can see the control in the window. However, you can get rid of the edit control's border by removing the WS_BORDER flag from the edit control's window styles. When you do this, the main window will show no evidence of the control, except when you type text into it.

Examining the RichEdit Application

Now that you've had a chance to try out a rich edit control, you'll want to examine the source code that accomplishes all those text-editing tricks. In the sections that follow, you'll see how to create a rich edit control and place it in a window. You'll also learn how to change default text settings such as character and paragraph formatting.

Creating the Rich Edit Control

In the RichEdit application, the rich edit control is created in the OnCreate() member function, which MFC calls when the Windows sends the WM_CREATE message. In OnCreate(), the program creates the control by calling its Create() member function:

m_richEdit.Create(WS_CHILD | WS_VISIBLE | WS_BORDER |
    ES_AUTOVSCROLL | ES_MULTILINE,
    CRect(0,0,388,302), this, 111);

This function's four arguments are the control's style flags, the control's size, a pointer to the control's parent window, and the control's ID. The style constants include the same constants that you would use for creating any type of window, with the addition of special styles used with rich edit controls. Table 13.1 lists these special styles.

Table 13.1 Rich Edit Styles

Style Description
ES_AUTOHSCROLL Automatically scrolls horizontally
ES_AUTOVSCROLL Automatically scrolls vertically
ES_CENTER Centers text
ES_LEFT Left aligns text
ES_LOWERCASE Lowercases all text
ES_MULTILINE Enables multiple lines
ES_NOHIDESEL Disallows hiding selected text when losing the focus
ES_OEMCONVERT Converts from ANSI characters to OEM characters and back to ANSI
ES_PASSWORD Displays characters as asterisks
ES_READONLY Disables editing in the control
ES_RIGHT Right aligns text
ES_UPPERCASE Uppercases all text
ES_WANTRETURN Inserts return characters into text when Enter is pressed

Initializing and Manipulating the Rich Edit Control

As soon as the rich edit control is created, you might want to initialize it in some way. The CRichEditCtrl class features a number of member functions that enable you to initialize and manipulate the control. Those member functions and their descriptions are listed in Table 13.2.

Table 13.2 Member Functions of the CRichEditCtrl Class

Function Description
CanPaste() Determines whether the Clipboard's contents can be pasted into the control
CanUndo() Determines whether the last edit can be undone
Clear() Clears selected text
Copy() Copies selected text to the Clipboard
Create() Creates the control
Cut() Cuts selected text to the Clipboard
DisplayBand() Displays a portion of the control's text
EmptyUndoBuffer() Resets the control's undo flag
FindText() Finds the given text
FormatRange() Formats text for an output target device
GetCharPos() Gets the position of a given character
GetDefaultCharFormat() Gets the default character format
GetEventMask() Gets the control's event mask
GetFirstVisibleLine() Gets the index of the first visible line
GetIRichEditOle() Gets the IRichEditOle interface pointer for the control
GetLimitText() Gets the maximum number of characters that can be entered
GetLine() Gets the specified text line
GetLineCount() Gets the number of lines in the control
GetModify() Determines whether the control's contents have changed since the last save
GetParaFormat() Gets the paragraph format of selected text
GetRect() Gets the control's formatting rectangle
GetSel() Gets the position of the currently selected text
GetSelectionCharFormat() Gets the character format of selected text
GetSelectionType() Gets the selected text's contents type
GetSelText() Gets the currently selected text
GetTextLength() Gets the length of the control's text
HideSelection() Hides or shows selected text
LimitText() Sets the maximum number of characters that can be entered
LineFromChar() Gets the number of the line containing the given character
LineIndex() Gets the character index of a given line
LineLength() Gets the length of the given line
LineScroll() Scrolls the text the given number of lines and characters
Paste() Pastes the Clipboard's contents into the control
PasteSpecial() Pastes the Clipboard's contents using the given format
ReplaceSel() Replaces selected text with the given text
RequestResize() Forces the control to send EN_REQUESTRESIZE notification messages
SetBackgroundColor() Sets the control's background color
SetDefaultCharFormat() Sets the default character format
SetEventMask() Sets the control's event mask
SetModify() Toggles the control's modification flag
SetOLECallback() Sets the control's IRichEditOleCallback COM object
SetOptions() Sets the control's options
SetParaFormat() Sets the selection's paragraph format
SetReadOnly() Disables editing in the control
SetRect() Sets the control's formatting rectangle
SetSel() Sets the selected text
SetSelectionCharFormat() Sets the selected text's character format
SetTargetDevice() Sets the control's target output device
SetWordCharFormat() Sets the current word's character format
StreamIn() Brings text in from an input stream
StreamOut() Stores text in an output stream
Undo() Undoes the last edit

As you can tell from Table 13.2, you can do a lot more with a rich edit control than can possibly be described within the scope of this book. However, the RichEdit program shows you the basics of using the rich edit control in an application, by setting character attributes and paragraph formats. When you include a rich edit control in an application, you'll probably want to give the user some control over its contents. For this reason, you'll usually create menu and toolbar commands for selecting the various options that you want to support in the application. RichEdit doesn't have an Options menu, but it does have a toolbar, whose buttons enable the user to manipulate the rich edit control.

Thanks to MFC's message mapping, these toolbar buttons are associated with the functions that respond to them. For example, when the user clicks the Underline button, MFC calls the OnUnderlined() method, where the program toggles the underline text attribute. In OnUnderline(), the program first initializes a CHARFORMAT structure, like this:

CHARFORMAT charFormat;
charFormat.cbSize = sizeof(CHARFORMAT);
charFormat.dwMask = CFM_UNDERLINE;

A CHARFORMAT structure holds information about character formatting and is declared by Windows as shown in Listing 13.5.

Listing 13.5 LST13_05.CP The CHARFORMAT Structure

typedef struct _charformat
{ 
    UINT     cbSize; 
    _WPAD    _wPad1; 
    DWORD    dwMask; 
    DWORD    dwEffects; 
    LONG     yHeight; 
    LONG     yOffset; 
    COLORREF crTextColor; 
    BYTE     bCharSet; 
    BYTE     bPitchAndFamily; 
    TCHAR    szFaceName[LF_FACESIZE]; 
    _WPAD    _wPad2; 
} CHARFORMAT;

In Listing 13.5, cbSize is the size of the structure; dwMask indicates which members of the structure are valid (this mask can be a combination of

dwEffects is the character effects (this value can be a combination of

yHeight is the character height; yOffset is the character baseline offset (for superscript and subscript characters); crTextColor is the text color; bCharSet is the character set value (see the ifCharSet member of the LOGFONT structure); bPitchAndFamily is the font pitch and family, and szFaceName is the font name.

After initializing the CHARFORMAT structure as needed to toggle underlining, the program calls the control's GetSelectionCharFormat() member function, like this:

m_richEdit.GetSelectionCharFormat(charFormat);

This function call, whose single argument is a reference to the CHARFORMAT structure, will return the current character format in the structure's dwEffects member. The program checks the result of this function to determine whether to turn underlining on or off, as shown in Listing 13.6.

Listing 13.6 LST13_06.CPP Determining Whether to Turn Underlining On or Off

if (charFormat.dwEffects & CFM_UNDERLINE)
    charFormat.dwEffects = 0;
else
    charFormat.dwEffects = CFE_UNDERLINE;
m_richEdit.SetSelectionCharFormat(charFormat);

The call to the control's SetSelectionCharFormat() member function sets the character format. Its single argument is a reference to the CHARFORMAT structure containing information about the requested character format.

The RichEdit application also enables the user to switch between the three types of paragraph alignment. This is accomplished similarly to toggling character formats. Listing 13.7 shows the three functions OnLeft(), OnRight(), and OnCenter() that handle the alignment commands. As you can see, the main difference is the use of the PARAFORMAT structure instead of CHARFORMAT and the call to SetParaFormat() instead of SetSelectionCharFormat().

Listing 13.7 LST13_07.TXT Changing Paragraph Formats

void CRicheditView::OnLeft() 
{
    PARAFORMAT paraFormat;
    paraFormat.cbSize = sizeof(PARAFORMAT);
    paraFormat.dwMask = PFM_ALIGNMENT;
    paraFormat.wAlignment = PFA_LEFT;
    m_richEdit.SetParaFormat(paraFormat);
}
void CRicheditView::OnCenter() 
{
    PARAFORMAT paraFormat;
    paraFormat.cbSize = sizeof(PARAFORMAT);
    paraFormat.dwMask = PFM_ALIGNMENT;
    paraFormat.wAlignment = PFA_CENTER;
    m_richEdit.SetParaFormat(paraFormat);
}
void CRicheditView::OnRight() 
{
    PARAFORMAT paraFormat;
    paraFormat.cbSize = sizeof(PARAFORMAT);
    paraFormat.dwMask = PFM_ALIGNMENT;
    paraFormat.wAlignment = PFA_RIGHT;
    m_richEdit.SetParaFormat(paraFormat);
}

Using the MFC CRichEditView Class

As I mentioned previously, there's an easier way to add a rich edit control to your application: Derive your view class from MFC's CRichEditView class. When you do this with AppWizard, you get an almost complete application, one that not only enables the user to type and edit text but that also incorporates sophisticated cut-and-paste features, as well as file saving and loading and even printing and print preview. To see all of this work, first create the MFCRichEdit application by performing the following steps:

ON THE WEB

http://www.quecorp.com/semfc The complete source code and executable file for the MFC RichEdit application can be found in the CHAP13\MFCRichEdit directory at this book'sWeb site.

1. Start a new AppWizard project workspace called MFCRichEdit, as shown in Figure 13.10.


FIG. 13.10

Here's how to use AppWizard to start a project workspace called MFCRichEdit.

2. Give the new project the following settings in the AppWizard dialog boxes:

Step 1: Single document

Step 2: Default settings

Step 3: Default settings

Step 4: Default settings

Step 5: Default settings

Step 6: Change the base class for CMFCRichEditView to CRichEditView (see Figure 13.11).


FIG. 13.11

You want to derive your view class from MFC's CRichEditView.

3. Add a button to the application's toolbar, as shown in Figure 13.12. Give the button the ID ID_UNDERLINED.


FIG. 13.12

Use the drawing tools to add a button to the application's toolbar.

4. Use ClassWizard to associate the ID_UNDERLINED command with the OnUnderlined() message response function, as shown in Figure 13.13. Make sure that you have CMFCRichEditView selected in the Class Name box before you add the function.


FIG. 13.13

Add the OnUnderlined() message response function to the view class.

5. Click the Edit Code button, and then add the lines shown in Listing 13.8 to the new OnUnderlined() function, right after the TODO: Add your command handler code here comment:

Listing 13.8 LST13_08.CPP Code for the OnUnderlined() Function

    CHARFORMAT charFormat = GetCharFormatSelection();
    if (charFormat.dwEffects & CFM_UNDERLINE)
        charFormat.dwEffects = 0;
    else
        charFormat.dwEffects = CFE_UNDERLINE;
    SetCharFormat(charFormat);

You've now completed the MFCRichEdit application. To compile the application, choose Developer Studio's Build, Build command. You can then run the program by choosing the Build, Execute command. When you do, the application's main window appears, as shown in Figure 13.14. Take some time to experiment with the application. As you'll discover, the application automatically takes care of editing, file-saving, file-loading, print, and print-preview features.


FIG. 13.14

The MFCRichEdit application provides a host of features with little effort from the programmer.

Although MFC's CRichEditView class automatically takes care of most text-handling features, you still have to provide your own code for doing things like setting character and paragraph styles. These tasks, however, are accomplished similarly to the way in which they were handled in the original program, RichEdit. The difference is that, rather than calling a rich edit control's member functions directly, you call the CRichEditView object's member functions. The sample program demonstrates only text underlining, but that should be enough to show how you can easily add other features.

An important advantage of using the CRichEditDoc and CRichEditView classes is that CRichEditDoc and CRichEditView manage the document and view using MFC's document/view architecture, enabling you to save your documents in RTF format and to reload them with the formatting intact.