Chapter 8

Win95 Common Controls


CONTENTS

The toolbar and status bar controls are covered in Chapter 18, "Interface Issues," and property sheets and wizards are covered in Chapter 9 "Property Pages and Sheets and Wizards."

The Win95 Controls Application

This chapter's sample program is called Win95 Controls App. You can find the program's executable file, as well as the complete source code, in the CHAP08 folder of this book's CD-ROM. When you run the program, you see the window shown in Figure 8.1. As you can see, Win95 Controls App demonstrates six of the Windows 95 common controls: the progress bar, the trackbar, the up-down control, the list view, the tree view, and the rich edit control. In the following sections, you learn the basics of creating and using these controls in your own applications.

Figure 8.1 : The Win95 Controls App demonstrates six of Windows 95's common controls.

The Progress Bar Control

Probably the easiest of the new common controls to use is the progress bar, which is nothing more than a rectangle that fills in slowly with colored blocks. The more colored blocks filled in, the closer to complete a task is. When the progress bar is completely filled in, the task associated with the progress bar is also complete. You might use a progress bar to show the status of a sorting operation or to give the user visual feedback about a large file that's being loaded.

To see a progress bar in action, click anywhere in the background of Win95 Controls App's window. When you do, the progress bar starts filling with colored blocks. When the progress bar is completely filled, it starts over again. This continues until you click the window again or exit the program. Of course, in this program, the progress bar isn't tracking a real task in progress. It's simply responding to timer messages. However, the program still demonstrates how you might use a progress bar in your own applications.

Creating the Progress Bar

This may be an obvious statement, but before you can use a progress bar, you must create it. Often in an MFC program, the controls are created as part of a dialog box. However, Win95 Controls App displays its controls in the application's main window. It does this by creating all the controls in the view class's OnCreate() function, which responds to the WM_CREATE Windows message. The controls themselves are declared as data members of the view class, as shown in Listing 8.1. As you can see, the progress bar is an object of the CProgressCtrl class.


Listing 8.1  LST8_1.TXT-Declaring the View Class's Controls

protected:

    CProgressCtrl m_progressBar;

    CSliderCtrl m_trackbar;

    BOOL m_timer;

    CSpinButtonCtrl m_upDown;

    CEdit m_buddyEdit;

    CListCtrl m_listView;

    CImage list m_smallImage list;

    CImage list m_largeImage list;

    CButton m_smallButton;

    CButton m_largeButton;

    CButton m_listButton;

    CButton m_reportButton;

    CTreeCtrl m_treeView;

    CImage list m_treeImage list;

    CRichEditCtrl m_richEdit;

    CButton m_boldButton;

    CButton m_leftButton;

    CButton m_centerButton;

    CButton m_rightButton;


Because it takes quite a lot of code to create and initialize each of the six controls, the application relegates the details to separate functions. For example, to create the progress bar control, OnCreate() calls the CreateProgressBar() local member function, like this:


CreateProgressBar();

In CreateProgressBar() is where the fun begins. First, the function creates the progress bar control by calling the control's Create() function:


m_progressBar.Create(WS_CHILD | WS_VISIBLE | WS_BORDER,

    CRect(20, 40, 250, 80), this, 102);

This function's four arguments are the control's style flags, the control's size (as a CRect object), a pointer to the control's parent window, and the control's ID. (Usually, you declare a constant, such as IDC_PROGRESSBAR, to use as the control's ID. I used a hard-coded value to keep the code as simple as possible.) The style constants are the same constants you use for creating any type of window (a control is really nothing more than a special kind of window, after all). In this case, you need at least WS_CHILD (which indicates the control is a child window) and WS_VISIBLE (which ensures that the user can see the control). The WS_BORDER is a nice addition because it adds a dark border around the control, setting it off from the rest of the window.

Initializing the Progress Bar

After the progress bar control is created, it must be initialized. The CProgressCtrl 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 8.1. For more information, please look the functions up in your Visual C++ online documentation.

Table 8.1  Member Functions of the CProgressCtrl Class

FunctionDescription
Create()Creates the progress bar control
OffsetPos()Advances the control the given number of blocks
SetPos()Sets the control's current value
SetRange()Sets the control's minimum and maximum values
SetStep()Sets the value by which the control advances
StepIt()Advances the control by one step unit

To initialize the control, you merely call the CProgressCtrl object's appropriate member functions, which Win95 Controls App does like this:


m_progressBar.SetRange(1, 100);

m_progressBar.SetStep(10);

m_progressBar.SetPos(50);

The call to SetRange() determines the values represented by the progress bar. The two arguments are the minimum and maximum values. So, after the preceding call to SetRange(), if the progress bar is set to 1, it displays no colored blocks, whereas setting the control's position to 100 fills the control with colored blocks.

Next, the CreateProgressBar() function calls SetStep(), which determines how far the progress bar advances with each increment. The larger this value, the faster the progress bar fills with colored blocks. Because the range and the step rate are related, a control with a range of 1-10 and a step rate of 1 works almost identically to a control with a range of 1-100 and a step rate of 10.

When the Win95 Controls App starts, the progress bar is already half filled with colored blocks. (This is purely for aesthetic reasons. Usually a progress bar begins its life empty.) This is because of the call to SetPos() with the value of 50, which is the midpoint of the control's range.

Manipulating the Progress Bar

In Win95 Controls App, the progress bar starts counting forward when you click in the window's background. This is because the program responds to the mouse click by starting a timer that sends WM_TIMER messages to the program twice a second. In the view class's OnTimer() function, the program makes the following function call:


m_progressBar.StepIt();

The StepIt() function increments the progress bar control's value by the step rate, causing new blocks to be displayed in the control as the control's value setting counts upward. When the control reaches its maximum, it automatically starts over.

NOTE
Notice that there are no CprogressCtrl member functions that control the size or number of blocks that will fit into the control. This attribute is controlled indirectly by the size of the control.

The Trackbar Control

Many times in a program you may need to have the user enter a value that lies within a specific range. For this sort of task, you use MFC's CSliderCtrl class to create a trackbar control. For example, suppose you need the user to enter a percentage that your program needs to calculate another value. In that case, you want the user to enter only values in the range from 0 to 100. Other values would be invalid and could cause problems in your program if such invalid values were not carefully trapped.

Using the trackbar control, you can force the user to enter a value in the specified range. Although the user can accidentally enter a wrong value (a value that doesn't accomplish what the user wants to do), he can't enter an invalid value (one that brings your program crashing down like a stone wall in an earthquake).

In the case of a percentage, you create a trackbar control with a minimum value of 0 and a maximum value of 100. Moreover, to make the control easier to position, you want to place tick marks at each setting that's a multiple of 10, giving 11 tick marks in all (including the one at 0). Win95 Controls App creates exactly this type of trackbar.

To see the trackbar work, click the trackbar's slot. When you do, the slider moves forward or backward and the selected value appears to the right of the control's caption. Once the trackbar has the focus, you can also control it with your keyboard's Up and Down arrow keys, as well as with the Page Up and Page Down keys.

Creating the Trackbar

In the Win95 Controls App application, the trackbar is created in the CreateTrackbar() local member function, which, like CreateProgressBar(), the program calls from the view class's (CWin95View's) OnCreate() function. In CreateTrackbar(), the program first creates the trackbar control by calling its Create() member function, like this:


m_trackbar.Create(WS_CHILD | WS_VISIBLE | WS_BORDER |

    TBS_AUTOTICKS | TBS_BOTH | TBS_HORZ,

    CRect(270, 40, 450, 80), this, 101);

This function's four arguments are the control's style flags, the control's size (as a CRect object), a pointer to the control's parent window, and the control's ID. The style constants include the same constants you would use for creating any type of window, with the addition of special styles used with trackbars. Table 8.2 lists these special styles.

Table 8.2  Trackbar Styles

StyleDescription
TBS_AUTOTICKSEnables the trackbar to automatically draw its tick marks
TBS_BOTHDraws tick marks on both slides of the slider
TBS_BOTTOMDraws tick marks on the bottom of a horizontal trackbar
TBS_ENABLESELRANGEEnables a trackbar to display a subrange of values
TBS_HORZDraws the trackbar horizontally
TBS_LEFTDraws tick marks on the left side of a vertical trackbar
TBS_NOTICKSDraws a trackbar with no tick marks
TBS_RIGHTDraws tick marks on the right side of a vertical trackbar
TBS_TOPDraws tick marks on the top of a horizontal trackbar
TBS_VERTDraws a vertical trackbar

Initializing the Trackbar

After the trackbar control is created, it must be initialized. The CSliderCtrl class features many member functions that enable you to initialize and manipulate the control. Those member functions and their descriptions are listed in Table 8.3. For more information, please look up the functions in your Visual C++ online documentation.

Table 8.3  Member Functions of the CSliderCtrl Class

FunctionDescription
ClearSel()Clears a selection from the control
ClearTics()Clears tick marks from the control
Create()Creates a trackbar control
GetChannelRect()Gets the size of the control's slider
GetLineSize()Gets the control's line size
GetNumTics()Gets the number of tick marks
GetPageSize()Gets the control's page size
GetPos()Gets the control's position
GetRange()Gets the control's minimum and maximum values
GetRangeMax()Gets the control's maximum value
GetRangeMin()Gets the control's minimum value
GetSelection()Gets the current range selection
GetThumbRect()Gets the size of the control's thumb
GetTic()Gets the position of a tick mark
GetTicArray()Gets all the control's tick positions
GetTicPos()Gets the client coordinates of a tick mark
SetLineSize()Sets the control's line size
SetPageSize()Sets the control's page size
SetPos()Sets the control's position
SetRange()Sets the control's minimum and maximum values
SetRangeMax()Sets the control's maximum value
SetRangeMin()Sets the control's minimum value
SetSelection()Sets a selected subrange in the control
SetTic()Sets the position of a tick mark
SetTicFreq()Sets the control's tick frequency
VerifyPos()Determines whether the control's position is valid

Usually, when you create a trackbar control, you want to set the control's range and tick frequency. If the user is going to use the control from the keyboard, you also need to set the control's line and page size. In the Win95 Controls App application, the program initializes the trackbar as shown in Listing 8.2.


Listing 8.2  LST8_2.TXT-Initializing the Trackbar Control

m_trackbar.SetRange(0, 100, TRUE);

m_trackbar.SetTicFreq(10);

m_trackbar.SetLineSize(1);

m_trackbar.SetPageSize(10);


The call to SetRange() sets the trackbar's minimum and maximum values to 0 and 100, respectively. The arguments are the minimum value, the maximum value, and a Boolean value indicating whether the trackbar should redraw itself after setting the range. Next, the call to SetTicFreq() ensures that there is a tick mark at each interval of 10. (Without this function call, the trackbar would have a tick mark for each possible setting, 101 in all.) Finally, the calls to SetLineSize() and SetPageSize() determine how much the trackbar moves when the user presses his Up arrow, Down arrow, Page Up, or Page Down keys.

Manipulating the Trackbar

When you get down to it, a trackbar is really a special scrollbar control. As such, when the user moves the slider, the control generates WM_HSCROLL messages, which Win95 Controls App captures in its view class's OnHScroll() member function, shown in Listing 8.3.


Listing 8.3  LST8_3.TXT-Responding to a Trackbar Control

void CWin95View::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) 

{

    // TODO: Add your message handler code here and/or call default

    

    CSliderCtrl* slider = (CSliderCtrl*)pScrollBar;

    int position = slider->GetPos();

    char s[10];

    wsprintf(s, "%d   ", position);

    CClientDC clientDC(this);

    clientDC.TextOut(390, 22, s);

    CView::OnHScroll(nSBCode, nPos, pScrollBar);

}


OnHScroll()'s fourth parameter is a pointer to the scroll object that generated the WM_HSCROLL message. The preceding function first casts this pointer to CSliderCtrl pointer. It then gets the current position of the trackbar's slider by calling the CSliderCtrl member function GetPos(). After the program has the slider's position, it converts the integer to a string and displays that string in the window. (If you don't understand how to display data in a window, check out Chapter 11, "Drawing on the Screen.")

The Up-Down Control

The trackbar control isn't the only way you can get a value in a predetermined range from the user. If you don't care about using the trackbar for visual feedback, you can use an up-down control, which is little more than a couple of arrows the user clicks to raise or lower the control's setting. If the trackbar control is a scroller with only a bar and a thumb, then an up-down control is the leftover arrow buttons.

In the Win95 Controls App application, you can change the setting of the up-down control by clicking either of its arrows. When you do, the value in the attached edit box changes, indicating the up-down control's current setting. After the control has the focus, you can also change its value by pressing your keyboard's Up and Down arrow keys.

Creating the Up-Down Control

In the Win95 Controls App application, the up-down control is created in the CreateUpDownCtrl() local member function, which the program calls from the view class's OnCreate() function. In CreateUpDownCtrl(), the program creates the up-down control by first creating the associated buddy control to which the up-down control communicates its current value. In this case, as is typical, the buddy control is an edit box, which is created by calling the CEdit class's Create() member function:


m_buddyEdit.Create(WS_CHILD | WS_VISIBLE | WS_BORDER,

    CRect(50, 120, 110, 160), this, 103);

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. If you remember from the control declarations, m_buddyEdit is an object of the CEdit class.

Now that the program has created the buddy control, it can create the up-down control in much the same way, by calling the object's Create() member function, like this:


m_upDown.Create(WS_CHILD | WS_VISIBLE | WS_BORDER |

    UDS_ALIGNRIGHT | UDS_SETBUDDYINT | UDS_ARROWKEYS,

    CRect(0, 0, 0, 0), this, 104);

As you can guess by now, 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. As with most controls, the style constants include the same constants you use for creating any type of window. However, the CSpinButtonCtrl class, of which m_upDown is an object, defines special styles to be used with up-down controls. Table 8.4 lists these special styles.

Table 8.4  Up-Down Control Styles

StylesDescription
UDS_ALIGNLEFTPlaces the up-down control on the left edge of the buddy control
UDS_ALIGNRIGHTPlaces the up-down control on the right edge of the buddy control
UDS_ARROWKEYSEnables the user to change the control's values using the keyboard's Up and Down arrow keys
UDS_AUTOBUDDYMakes the previous window the buddy control
UDS_HORZCreates a horizontal up-down control
UDS_NOTHOUSANDSEliminates separators between each set of three digits
UDS_SETBUDDYINTDisplays the control's value in the buddy control
UDS_WRAPCauses the control's value to wrap around to its minimum when the maximum is reached, and vice versa

After the up-down control is created, it must be initialized. The CSpinButtonCtrl class features member functions that enable you to initialize and manipulate the control. Those member functions and their descriptions are listed in Table 8.5. For more detailed information, please look up the functions in your Visual C++ online documentation.

Table 8.5  CSpinButtonCtrl Member Functions

FunctionDescription
Create()Creates the up-down control
GetAccel()Gets the control's speed
GetBase()Gets the control's numerical base
GetBuddy()Gets a pointer to the control's buddy control
GetPos()Gets the control's position
GetRange()Gets the control's minimum and maximum values
SetAccel()Sets the control's speed
SetBase()Sets the control's numerical base (10 for decimal, 16 for hex)
SetBuddy()Sets the control's buddy control
SetPos()Sets the control's position
SetRange()Sets the control's minimum and maximum values

After creating an up-down control, you usually want to set the control's buddy, range, and position. In the Win95 Controls App application, the program initializes the control like this:


m_upDown.SetBuddy(&m_buddyEdit);

m_upDown.SetRange(1, 100);

m_upDown.SetPos(50);

Here, the up-down control's buddy is set to the edit box that was first created in CreateUpDownCtrl(). Then, the program calls SetRange() and SetPos() to give the control its starting range and position, respectively. Thanks to the UDS_SETBUDDYINT flag passed to Create() and the call to the control's SetBuddy() member function, Win95 Controls App needs to do nothing else to have the control's value appear on the screen. The control handles its buddy automatically.

The Image List Control

Often in programs you need to use a lot of images that are related in some way. For example, your application may have a toolbar with many command buttons, each of which uses a bitmap for its icon. In a case like this, it would be great to have some sort of program object that could not only hold the bitmaps, but also organize them so they can be accessed easily. That's exactly what an image list control does for you. An image list does nothing more than stores a list of related images. You can use the images any way you see fit in your program. However, several Windows 95 controls rely on image lists. These controls are:

Besides the preceding controls, you will undoubtedly come up with many other uses for image lists. You might, for example, have an animation sequence that you'd like to display in a window. An image list is the perfect storage place for the frames that make up the animation, because you can easily access any frame just by using an index.

If the word index makes you think of arrays, you're close to understanding how an image list stores images. An image list is much like an array that holds pictures rather than integers or floating-point numbers. Just as with an array, you initialize each "element" of an image list and thereafter can access any part of the "array" using an index.

You won't, however, ever see in your running application an image list control in the same way that you can see a status bar or a progress bar control. That's because (again, like an array) an image list is nothing more than a storage structure for pictures. You can display the images stored in an image list, but you can't display the image list itself. Figure 8.2 shows how an image list is organized.

Figure 8.2 : An image list is much like an array of pictures.

Creating the Image List

In the Win95 Controls App application, image lists are used with the list view and tree view controls, so the image lists for the controls are created in the CreateListView() and CreateTreeView() local member functions, which the program calls from the view class's OnCreate() function. An image view, which is an object of the CImageList class, is created by its Create() member function, like this:


m_smallImageList.Create(16, 16, FALSE, 1, 0);

The Create() function's five arguments are the width of the pictures in the control, the height of the pictures, a Boolean value indicating whether the images contain a mask, the number of images initially in the list, and the number of images by which the list can dynamically grow. This last value is 0 to indicate that the list is not allowed to grow at runtime. The Create() function is overloaded in the CImageList class so that you can create image lists in various ways. You can find the other versions of Create() in your Visual C++ online documentation.

Initializing the Image List

After you have an image list created, you'll want to add images to it. After all, an empty image list isn't a heck of a lot of use. The easiest way to add the images is to have the images as part of your application's resource file and load them from there. For example, the following code shows how Win95 Controls App loads an icon into one of its image lists:


HICON hIcon = ::LoadIcon (AfxGetResourceHandle(),

    MAKEINTRESOURCE(IDI_ICON1));

m_smallImageList.Add(hIcon);

Here, the program first gets a handle to the icon. Then, it adds the icon to the image list by calling the image list's Add() member function. Table 8.6 lists other member functions you can use to manipulate an object of the CImageList class. As you can see, you have a lot of control over an image list if you really want to dig in.

Table 8.6  Member Functions of the CImageList Class

FunctionDescription
Add()Adds an image to the image list
Attach()Attaches an existing image list to an object of the CImageList class
BeginDrag()Starts an image-dragging operation
Create()Creates an image list control
DeleteImageList()Deletes an image list
Detach()Detaches an image list from an object of the CImageList class
DragEnter()Locks a window for updates and shows the drag image
DragLeave()Unlocks a window for updates
DragMove()Moves the drag image
DragShowNolock()Handles the drag image without locking the window
Draw()Draws an image that's being dragged
EndDrag()Ends an image-dragging operation
ExtractIcon()Creates an icon from an image
GetBkColor()Gets an image list's background color
GetDragImage()Gets the image for drag operations
GetImageCount()Gets the number of images in the control
GetImageInfo()Gets image information
GetSafeHandle()Gets an image list's handle
Read()Gets an image list from the given archive
Remove()Removes an image from the image list
Replace()Replaces one image with another
SetBkColor()Sets an image list's background color
SetDragCursorImage()Creates an image for drag operations
SetOverlayImage()Sets the index of an overlay mask
Write()Writes an image list to the given archive

The List View Control

Often, computer programs need to work with lists of objects and organize those objects in such a way that the program's user can easily determine each object's attributes. An example is a group of files on a disk. Each file is a separate object that is associated with a number of attributes including the file's name, size, and the date the file was last modified. Windows shows files either as icons in a window or as a table of entries, each entry showing the attributes associated with the files. The user has full control over the way the file objects are displayed, including which attributes are shown and which are not listed. The Windows 95 common controls includes something called a list view control, which enables Windows 95 programmers to organize lists in exactly the same way Windows 95 does with files and other objects.

If you'd like to see an example of a full-fledged list view control, just open the Windows 95 Explorer (see fig. 8.3). The right side of the window shows how the list view control can organize objects in a window. (The left side of the window contains a tree view control, which you learn about later in this chapter, in the section titled "The Tree View Control.") In the figure, the list view is currently set to the report view, in which each object in the list gets its own line showing not only the object's name but also the attributes associated with that object.

Figure 8.3 : Explorer uses a list view control to organize file information.

As I mentioned previously in this chapter, the user can change the way objects are organized in a list view control. Figure 8.4, for example, shows the list view portion of the Explorer set to the large-icon setting, whereas Figure 8.5 shows the small-icon setting, which enables the user to see more objects (in this case, files) in the window. The list view control also provides the user with the ability to edit the names of objects in the list, as well as to sort objects based on data displayed in a particular column. (This latter function works only when the list view control is in report view.)

Figure 8.4 : Here's Explorer's list view control set to large icons.

Figure 8.5 : Here's Explorer's list view control set to small icons.

The Win95 Controls App application also sports a list view control, although it's not as fancy as Explorer's. To switch between the small-icon, large-icon, list, and report views, click the appropriate button to the right of the control. Figure 8.6 shows the application's list view control displaying small icons, whereas Figure 8.7 shows the large icons.

Figure 8.6 : Here's the example application's list view control set to small icons.

Figure 8.7 : Here's the sample application's list view control set to large icons.

Creating the List View

In the Win95 Controls App application, the list view control is created in the CreateListView() local member function, which the program calls from the view class's OnCreate() function. In CreateListView(), the program first creates two image lists, as shown in Listing 8.4. One image list will hold the small icon for the list view, and the other will hold the large icon. In this case, the list only includes one icon.


Listing 8.4  LST8_4.TXT-Creating the List View's Image Lists

m_smallImageList.Create(16, 16, FALSE, 1, 0);

m_largeImageList.Create(32, 32, FALSE, 1, 0);

HICON hIcon = ::LoadIcon (AfxGetResourceHandle(),

    MAKEINTRESOURCE(IDI_ICON1));

m_smallImageList.Add(hIcon);

hIcon = ::LoadIcon (AfxGetResourceHandle(),

    MAKEINTRESOURCE(IDI_ICON2));

m_largeImageList.Add(hIcon);


Now that the program has created the image lists, it can create the list view control, by calling the class's Create() member function, like this:


m_listView.Create(WS_VISIBLE | WS_CHILD | WS_BORDER |

    LVS_REPORT | LVS_NOSORTHEADER | LVS_EDITLABELS,

CRect(160, 120, 394, 220), this, 105);

Here, Create()'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 CListCtrl class, of which m_listView is an object, defines special styles to be used with list view controls. Table 8.7 lists these special styles and their descriptions.

Table 8.7  List View Styles

StyleDescription
LVS_ALIGNLEFTLeft aligns items in the large-icon and small-icon views
LVS_ALIGNTOPTop aligns items in the large-icon and small-icon views
LVS_AUTOARRANGEAutomatically arranges items in the large-icon and small-icon views
LVS_EDITLABELSEnables the user to edit item labels
LVS_ICONSets the control to the large-icon view
LVS_LISTSets the control to the list view
LVS_NOCOLUMNHEADERShows no column headers in report view
LVS_NOITEMDATAStores only the state of each item
LVS_NOLABELWRAPDisallows multiple-line item labels
LVS_NOSCROLLTurns off scrolling
LVS_NOSORTHEADERTurns off the button appearance of column headers
LVS_OWNERDRAWFIXEDEnables owner-drawn items in report view
LVS_REPORTSets the control to the report view
LVS_SHAREIMAGELISTSPrevents the control from destroying its image lists when the control no longer needs them
LVS_SINGLESELDisallows multiple selection of items
LVS_SMALLICONSets the control to the small-icon view
LVS_SORTASCENDINGSorts items in ascending order
LVS_SORTDESCENDINGSorts items in descending order

Initializing the List View

Although not especially difficult, setting up a list view control is quite a lot more work than setting up a simpler control like a progress bar. As you already know, the list view control uses two image lists, one for its small icons and one for its large icons. A list view control also uses column headers for its report view, as well as list items and subitems. In short, to initialize a list view control, you must complete the following steps:

  1. Create the list view control.
  2. Associate the control with its image lists.
  3. Create a column object for each column that will appear in the report view.
  4. Create list items and subitems for each item that will be displayed in the list view control.

Associating the List View with Its Image Lists

You've already created your list view control, so step one is out of the way. Now, you must associate the control with its image lists (which have also already been created). The Win95 Controls App application handles the task like this:


m_listView.SetImageList(&m_smallImageList, LVSIL_SMALL);

m_listView.SetImageList(&m_largeImageList, LVSIL_NORMAL);

As you can see, the SetImageList() member function takes two parameters, which are a pointer to the image list and a flag indicating how the list is to be used. There are three constants defined for this flag: LVSIL_SMALL (which indicates that the list contains small icons), LVSIL_NORMAL (large icons), and LVSIL_STATE (state images). The SetImageList() function returns a pointer to the previously set image list, if any.

Creating the List View's Columns

The next task is to create the columns for the control's report view. You need one main column for the item itself and one column for each subitem associated with an item. For example, in Explorer's list view, the main column holds file and folder names. Each additional column holds the subitems for each item, including the file's size, type, and modification date. To create a column, you must first declare an LV_COLUMN structure. You use this structure to pass information to and from the system. The LV_COLUMN structure is defined as shown in Listing 8.5.


Listing 8.5  LST8_5.TXT-The LV_COLUMN Structure

typedef struct _LV_COLUMN

{

    UINT mask;       // Flags indicating valid fields

    int fmt;         // Column alignment

    int cx;          // Column width

    LPSTR pszText;   // Address of string buffer

    int cchTextMax;  // Size of the buffer

    int iSubItem;    // Subitem index for this column

} LV_COLUMN;


The mask member of the structure must be set so that it indicates which of the other fields in the structure are valid. The flags you can use are LVCF_FMT (meaning fmt is valid), LVCF_SUBITEM (iSubItem is valid), LVCF_TEXT (pszText is valid), and LVCF_WIDTH (cx is valid). Essentially, when you give mask its value, you're telling the system which members of the structure to use and which to ignore.

The fmt member gives the column's alignment and can be LVCFMT_CENTER, LVCFMT_LEFT, or LVCFMT_RIGHT. The alignment determines how the column's label and items are positioned in the column.

NOTE
The first column, which contains the main items, is always aligned to the left. The other columns in the report view can be aligned how you like.

The cx field specifies the width of each column, whereas pszText is the address of a string buffer. When you're using the structure to create a column (you also can use this structure to obtain information about a column), this string buffer contains the column's label. The cchTextMax member gives the size of the string buffer and is valid only when retrieving information about a column.

In Win95 Controls App's CreateListView() function, the program starts initializing the LV_COLUMN structure as shown in Listing 8.6.


Listing 8.6  LST8_6.TXT-Initializing the LV_COLUMN Structure

LV_COLUMN lvColumn;

lvColumn.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;

lvColumn.fmt = LVCFMT_CENTER;

lvColumn.cx = 75;


The values being set in Listing 8.6 will be the same for every column created, so they are done first and then are not changed as the columns are created.

Next, the program creates its main column by setting the appropriate structure members and then calling the CListCtrl object's InsertColumn() member function:


lvColumn.iSubItem = 0;

lvColumn.pszText = "Column 0";

m_listView.InsertColumn(0, &lvColumn);

Setting iSubItem to 0 indicates that the program is creating the first column. (Column numbers are zero-based like an array's indexes.) Finally, the program sets pszText to the column's label and calls InsertColumn() to add the column to the list view control. InsertColumn()'s two arguments are the column's index and a pointer to the LV_COLUMN structure.

The two subitem columns are created similarly, as shown in Listing 8.7.


Listing 8.7  LST8_7.TXT-Creating the SubItem Columns

lvColumn.iSubItem = 1;

lvColumn.pszText = "Column 1";

m_listView.InsertColumn(1, &lvColumn);

lvColumn.iSubItem = 2;

lvColumn.pszText = "Column 2";

m_listView.InsertColumn(1, &lvColumn);


Creating the List View's Items

With the columns created, it's time to create the items that will be listed in the columns when the control is in its report view. Creating items is not unlike creating columns. As with columns, Visual C++ defines a structure that you must initialize and pass to the function that creates the items. This structure is called LV_ITEM and is defined as shown in Listing 8.8.


Listing 8.8  LST8_8.TXT-The LV_ITEM Structure

typedef struct _LV_ITEM

{

    UINT   mask;         // Flags indicating valid fields

    int    iItem;        // Item index

    int    iSubItem;     // Sub-item index

    UINT   state;        // Item's current state

    UINT   stateMask;    // Valid item states.

    LPSTR  pszText;      // Address of string buffer

    int    cchTextMax;   // Size of string buffer

    int    iImage;       // Image index for this item

    LPARAM lParam;       // Additional information as a 32-bit value

} LV_ITEM;


In the LV_ITEM structure, the mask member specifies which other members of the structure are valid. The flags you can use are LVIF_IMAGE (iImage is valid), LVIF_PARAM (lParam is valid), LVIF_STATE (state is valid), and LVIF_TEXT (meaning pszText is valid).

The iItem member is the index of the item, which you can kind of think of as the row number in report view (although the position of the items can change when they're sorted). Each item has a unique index. The iSubItem member is the index of the subitem if this structure is defining a subitem. You can think of this value as the number of the column in which the item will appear. If you're defining the main item (the first column), this value should be 0.

The state and stateMask members hold the item's current state and the item's valid states, which can be one or more of LVIS_CUT (the item is selected for cut and paste), LVIS_DROPHILITED (the item is a highlighted drop target), LVIS_FOCUSED (the item has the focus), and LVIS_SELECTED (the item is selected).

The pszText member is the address of a string buffer. When using the LV_ITEM structure to create an item, the string buffer contains the item's text. When obtaining information about the item, pszText is the buffer where the information will be stored, and cchTextMax is the size of the buffer. If pszText is set to LPSTR_TEXTCALLBACK, the item uses the callback mechanism. Finally, the iImage member is the index of the item's icon in the small icon and large icon image lists. If set to I_IMAGECALLBACK, the iImage member indicates that the item uses the callback mechanism.

In Win95 Controls App's CreateListView() function, the program starts initializing the LV_ITEM structure as shown in Listing 8.9.


Listing 8.9  LST8_9.TXT-Initializing the LV_ITEM Structure

LV_ITEM lvItem;

lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE;

lvItem.state = 0;      

lvItem.stateMask = 0;  

lvItem.iImage = 0;


The values being set in Listing 8.9 will be the same for every item created, so they are done first and then are not changed as the items are created.

Now, the program can start creating the items that will be displayed in the list view control. Listing 8.10 shows how the program creates the first item.


Listing 8.10  LST8_10.TXT-Creating a List View Item

lvItem.iItem = 0;

lvItem.iSubItem = 0;

lvItem.pszText = "Item 0";

m_listView.InsertItem(&lvItem);


In Listing 8.10, the program sets the item and subitem indexes to 0 and sets the item's text to "Item 0." The program then calls the CListCtrl class's InsertItem() member function to add the item to the list view control. This function's single argument is the address of the LV_ITEM structure that contains the information about the item to be created.

At this point, the list view control has three columns and one item created. However, this single item's subitems display nothing unless you initialize them. Win95 Controls App initializes the first set of subitems like this:


m_listView.SetItemText(0, 1, "Sub Item 0.1");

m_listView.SetItemText(0, 2, "Sub Item 0.2");

The SetItemText() function takes three arguments, which are the item index, the subitem index, and the text to which to set the item or subitem.

As Listing 8.11 shows, the program then creates two more items along with the items' associated subitems.


Listing 8.11  LST8_11.TXT-Creating Additional Items and SubItems

lvItem.iItem = 1;

lvItem.iSubItem = 0;

lvItem.pszText = "Item 1";

m_listView.InsertItem(&lvItem);

m_listView.SetItemText(1, 1, "Sub Item 1.1");

m_listView.SetItemText(1, 2, "Sub Item 1.2");

lvItem.iItem = 2;

lvItem.iSubItem = 0;

lvItem.pszText = "Item 2";

m_listView.InsertItem(&lvItem);

m_listView.SetItemText(2, 1, "Sub Item 2.1");

m_listView.SetItemText(2, 2, "Sub Item 2.2");


Manipulating the List View

As I said previously, you can set a list view control to four different types of views: small icon, large icon, list, and report. In Explorer, for example, the toolbar features buttons that you can click to change the view, or you can select the view from the View menu. Although Win95 Controls App doesn't have a snazzy toolbar like Explorer, it does include four buttons that you can click to change the view. Those buttons are created in the CreateListView() function as shown in Listing 8.12.


Listing 8.12  LST8_12.TXT-Creating the View Buttons

m_smallButton.Create("Small", WS_VISIBLE | WS_CHILD | WS_BORDER,

	CRect(400, 120, 450, 140), this, 106);

m_largeButton.Create("Large", WS_VISIBLE | WS_CHILD | WS_BORDER,

	CRect(400, 145, 450, 165), this, 107);

m_listButton.Create("List", WS_VISIBLE | WS_CHILD | WS_BORDER,

	CRect(400, 170, 450, 190), this, 108);

m_reportButton.Create("Report", WS_VISIBLE | WS_CHILD | WS_BORDER,

	CRect(400, 195, 450, 215), this, 109);


These buttons are associated with entries in the view window's message map with the message-response functions OnSmall(), OnLarge(), OnList(), and OnReport(). In other words, when the user clicks one of these buttons, its matching function gets called, where the program changes the list view control to the requested view type. For example, when the user clicks the Small button, the function shown in Listing 8.13 changes the view to the list view.


Listing 8.13  LST8_13.TXT-Changing to the List View

void CWin95View::OnSmall() 

{

    SetWindowLong(m_listView.m_hWnd, GWL_STYLE,

        WS_VISIBLE | WS_CHILD | WS_BORDER |

        LVS_SMALLICON | LVS_EDITLABELS);

}


The SetWindowLong() function sets a window's attribute. Its arguments are the window's handle, a flag that specifies the value to be changed, and the new value. In this case, the GWL_STYLE flag specifies that the window's style should be changed to the style given in the third argument. Changing the list view control's style (in the preceding code, the new view style is LVS_SMALLICON) changes the type of view it displays.

Besides changing the view, there are a number of other features you can program for your list view controls. When the user does something with the control, Windows sends a WM_NOTIFY message to the parent window. By responding to these notifications, you can give your list view control its various capabilities. The most common notifications sent by a list view control are LVN_COLUMNCLICK (indicates that the user clicked a column header), LVN_BEGINLABELEDIT (indicates that the user is about to edit an item's label), and LVN_ENDLABELEDIT (indicates that the user is ending the label-editing process).

If you haven't discovered it yet, you can edit the labels of the items in Win95 Controls App's list view items. This works by the program's capturing and handling the notification messages sent by the list view control. To capture the notification messages, just override the window's OnNotify() function.

The three parameters received by OnNotify() are the message's WPARAM and LPARAM values and a pointer to a result code. In the case of a WM_NOTIFY message coming from a list view control, the WPARAM is the list view control's ID. And, if the WM_NOTIFY message is the LVN_BEGINLABELEDIT or LVN_ENDLABELEDIT notifications, the LPARAM is a pointer to a LV_DISPINFO structure, which itself contains NMHDR and LV_ITEM structures. You use the information in these structures to manipulate the item the user is trying to edit.

In OnNotify(), the program first casts the lParam parameter to a LV_DISPINFO structure, like this:


LV_DISPINFO* lv_dispInfo = (LV_DISPINFO*) lParam;

Next, the program checks whether the function is receiving a LVN_BEGINLABELEDIT notification, like this:


if (lv_dispInfo->hdr.code == LVN_BEGINLABELEDIT)

If the notification is LVN_BEGINLABELEDIT, your program can do whatever pre-editing initialization it needs to do. In the Win95 Controls App application, the function shows you how to get a pointer to the edit control being used to edit the label:


CEdit* pEdit = m_listView.GetEditControl();

The program, however, doesn't actually do anything with the control.

When handling label editing, the other notification to watch out for is LVN_ENDLABELEDIT, which this particular application does like this:


else if (lv_dispInfo->hdr.code == LVN_ENDLABELEDIT)

When the program receives this notification, the user has finished editing the label, either by typing the new label or by canceling the editing process. If the user has canceled the process, the LV_DISPINFO structure's item.pszText member will be NULL or the item.iItem member will be -1. In this case, you need do nothing more than ignore the notification. If, however, the user completed the editing process, the program must copy the new label to the item's text, which OnNotify() does like this:


m_listView.SetItemText(lv_dispInfo->item.iItem,

    0, lv_dispInfo->item.pszText);

The CListCtrl object's SetItemText() member function requires three arguments: the item index, the subitem index, and the new text. As you can see, all the information you need is stored in the LV_DISPINFO structure.

There are a lot of other things you can do with a list view control. However, there's not enough space in this chapter to get into any further detail. You can find more about these powerful controls in your Visual C++ online documentation. The ROWLIST sample program in the MSDEV\SAMPLES\MFC\GENERAL directory might be a good place to start.

The Tree View Control

In the preceding section, you learned how to use the list view control to organize the display of many items in a window. The list view control enables you to display items both as objects in a window and objects in a report organized into columns. Often, however, the data you'd like to organize for your application's user is best placed into a hierarchical view, where elements of the data are shown as they relate to each other. A good example of such a hierarchical display is the directory tree used by Windows to display directories on disk and the files they contain.

As is the case with other useful controls, Windows 95 includes the tree view control as one of its common controls. MFC provides access to this control through its CTreeCtrl class. This versatile control enables you to display data in various ways, all the while retaining the hierarchical relationship between the data objects in the view.

If you'd like to see an example of a tree view control, just open the Windows 95 Explorer (see fig. 8.8). The left side of the window shows how the tree view control organizes objects in a window. (The right side of the window contains a list view control, which you learned about in the previous section). In the figure, the tree view displays not only the storage devices on the computer, but also the directories and files stored on those devices. The tree clearly shows the hierarchical relationship between the devices, directories, and files as well as enables the user to open and close branches on the tree in order to explore it at a different level.

Figure 8.8 : A tree view control displays a hierarchical relationship between items.

The Win95 Controls App application also contains a tree view control. You can click the tree's various nodes to expose new levels of the tree. You can even edit the labels of the items in the tree. To do this, select an item and then click it. An edit box appears into which you can type the new label.

Creating the Tree View

In the Win95 Controls App application, the tree view control is created in the CreateTreeView() local member function, which the program calls from the view class's OnCreate() function. In CreateTreeView(), the program first creates the image list that holds the icons used with each item in the view. Listing 8.14 shows how the program creates the image list.


Listing 8.14  LST8_14.TXT-Creating the Tree View Control's Image List

m_treeImageList.Create(13, 13, FALSE, 3, 0);

HICON hIcon = ::LoadIcon(AfxGetResourceHandle(),

	MAKEINTRESOURCE(IDI_ICON3));

m_treeImageList.Add(hIcon);

hIcon = ::LoadIcon(AfxGetResourceHandle(),

	MAKEINTRESOURCE(IDI_ICON4));

m_treeImageList.Add(hIcon);

hIcon = ::LoadIcon(AfxGetResourceHandle(),

	MAKEINTRESOURCE(IDI_ICON5));

m_treeImageList.Add(hIcon);


Now that the program has created the image list, it can create the tree view control, by calling the class's Create() member function:


m_treeView.Create(WS_VISIBLE | WS_CHILD | WS_BORDER |

    TVS_HASLINES | TVS_LINESATROOT | TVS_HASBUTTONS |

    TVS_EDITLABELS, CRect(20, 260, 160, 360), this, 110);

Here, Create()'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 CTreeCtrl class, of which m_treeView is an object, defines special styles to be used with list view controls. Table 8.8 lists these special styles.

Table 8.8  Tree View Control Styles

StyleDescription
TVS_DISABLEDRAGDROPDisables drag-and-drop operations
TVS_EDITLABELSEnables user to edit labels
TVS_HASBUTTONSGives each parent item a button.
TVS_HASLINESAdds lines between items in the tree
TVS_LINESATROOTAdds a line between the root and child items
TVS_SHOWSELALWAYSForces a selected item to stay selected when losing focus

Initializing the Tree View

Like the list view control, a tree view control requires some hefty set-up work. As you already know, the tree view control can use an image list for item icons. A tree view control also must contain item objects that your program creates and adds to the control. To initialize a tree view control, you must complete the following steps:

  1. Create the tree view control.
  2. Associate the control with its image list (optional).
  3. Create the root and child items that will be displayed in the control.

The Win95 Controls App application associates the tree view control with its image list like this:


m_treeView.SetImageList(&m_treeImageList, TVSIL_NORMAL);

The creation of the tree view controls root and child items is covered in the following section.

Creating the Tree View's Items

Creating items for a tree view control is much like doing the same thing for a list view control. As with the list view, Visual C++ defines a structure that you must initialize and pass to the function that creates the items. This structure is called TV_ITEM and is defined as shown in Listing 8.15.


Listing 8.15  LST8_15.TXT-The TV_ITEM Structure

typedef struct _TV_ITEM

{

    UINT       mask; 

    HTREEITEM  hItem; 

    UINT       state; 

    UINT       stateMask; 

    LPSTR      pszText; 

    int        cchTextMax; 

    int        iImage; 

    int        iSelectedImage; 

    int        cChildren; 

    LPARAM     lParam; 

} TV_ITEM;


In the TV_ITEM structure, the mask member specifies which other members of the structure are valid. The flags you can use are TVIF_CHILDREN (cChildren is valid), TVIF_HANDLE (hItem is valid), TVIF_IMAGE (iImage is valid), TVIF_PARAM (lParam is valid), TVIF_SELECTEDIMAGE (iSelectedImage is valid), TVIF_STATE (state and stateMask are valid), and TVIF_TEXT (pszText and cchTextMax are valid).

The hItem member is the handle of the item, whereas the state and stateMask members hold the item's current state and the item's valid states, which can be one or more of TVIS_BOLD, TVIS_CUT, TVIS_DROPHILITED, TVIS_EXPANDED, TVIS_EXPANDEDONCE, TVIS_FOCUSED, TVIS_OVERLAYMASK, TVIS_SELECTED, TVIS_STATEIMAGEMASK, and TVIS_USERMASK. Please check your Visual C++ online documentation for the meanings of these flags.

The pszText member is the address of a string buffer. When using the LV_ITEM structure to create an item, the string buffer contains the item's text. When obtaining information about the item, pszText is the buffer where the information will be stored, and cchTextMax is the size of the buffer. If pszText is set to LPSTR_TEXTCALLBACK, the item uses the callback mechanism. Finally, the iImage member is the index of the item's icon in the image list. If set to I_IMAGECALLBACK, the iImage member indicates that the item uses the callback mechanism.

The iSelectedImage member is the index of the icon in the image list that represents the item when the item is selected. As with iImage, if this member is set to I_IMAGECALLBACK, the iSelectedImage member indicates that the item uses the callback mechanism. Finally, cChildren specifies whether there are child items associated with the item.

In addition to the TV_ITEM structure, you must initialize a TV_INSERTSTRUCT structure that holds information about how to insert the new structure into the tree view control. That structure is declared as shown in Listing 8.16.


Listing 8.16  LST8_16.TXT-The TV_INSERTSTRUCT Structure

typedef struct _TV_INSERTSTRUCT

{

    HTREEITEM hParent; 

    HTREEITEM hInsertAfter; 

    TV_ITEM   item; 

} TV_INSERTSTRUCT;


In this structure, hParent is the handle to the parent tree-view item. A value of NULL or TVI_ROOT specifies that the item should be placed at the root of the tree. The hInsertAfter member specifies the handle of the item after which this new item should be inserted. It can also be one of the flags TVI_FIRST (beginning of the list), TVI_LAST (end of the list), or TVI_SORT (alphabetical order). Finally, the item member is the TV_ITEM structure containing information about the item to be inserted into the tree.

In Win95 Controls App's CreateTreeView() function, the program initializes the TV_ITEM structure for the root item (the first item in the tree) as shown in Listing 8.17.


Listing 8.17  LST8_17.TXT-Creating the Root Item

TV_ITEM tvItem;

tvItem.mask =

	TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE;

tvItem.pszText = "Root";

tvItem.cchTextMax = 4;

tvItem.iImage = 0;

tvItem.iSelectedImage = 0;

TV_INSERTSTRUCT tvInsert;

tvInsert.hParent = TVI_ROOT;

tvInsert.hInsertAfter = TVI_FIRST;

tvInsert.item = tvItem;

HTREEITEM hRoot = m_treeView.InsertItem(&tvInsert);


As you can see, the CTreeCtrl member function InsertItem() actually inserts the item into the tree view control. Its single argument is the address of the TV_INSERTSTRUCT structure.

The program inserts the remaining items into the tree view control as shown in Listing 8.18.


Listing 8.18  LST8_18.TXT-Inserting the Child Items into the Tree View Control

// Create the first child item.

tvItem.pszText = "Child Item 1";

tvItem.cchTextMax = 12;

tvItem.iImage = 1;

tvItem.iSelectedImage = 1;

tvInsert.hParent = hRoot;

tvInsert.hInsertAfter = TVI_FIRST;

tvInsert.item = tvItem;

HTREEITEM hChildItem = m_treeView.InsertItem(&tvInsert);

// Create a child of the first child item.

tvItem.pszText = "Child Item 2";

tvItem.cchTextMax = 12;

tvItem.iImage = 2;

tvItem.iSelectedImage = 2;

tvInsert.hParent = hChildItem;

tvInsert.hInsertAfter = TVI_FIRST;

tvInsert.item = tvItem;

m_treeView.InsertItem(&tvInsert);

// Create another child of the root item.

tvItem.pszText = "Child Item 3";

tvItem.cchTextMax = 12;

tvItem.iImage = 1;

tvItem.iSelectedImage = 1;

tvInsert.hParent = hRoot;

tvInsert.hInsertAfter = TVI_LAST;

tvInsert.item = tvItem;

m_treeView.InsertItem(&tvInsert);


Manipulating the Tree View

Just as with the list view control, you can edit the labels of the items in Win95 Controls App's tree view items. Also like the list view control, this process works by the program's capturing and handling the notification messages sent by the tree view control. In Win95 Controls App, the overridden WindowProc() function routes these WM_NOTIFY messages to the program's OnNotify() local function.

In the case of a WM_NOTIFY message coming from a tree view control, the WPARAM is the list view control's ID. And, if the WM_NOTIFY message is the TVN_BEGINLABELEDIT or TVN_ENDLABELEDIT notifications, the LPARAM is a pointer to a TV_DISPINFO structure, which itself contains NMHDR and TV_ITEM structures. You use the information in these structures to manipulate the item the user is trying to edit. As you can see in Listing 8.19, OnNotify() handles the tree-view notifications almost exactly the same way as the list-view notifications. The only difference is the names of the structures used.


Listing 8.19  LST8_19.TXT-Handling Tree-View Notifications

TV_DISPINFO* tv_dispInfo = (TV_DISPINFO*) lParam;

if (tv_dispInfo->hdr.code == TVN_BEGINLABELEDIT)

{

    CEdit* pEdit = m_treeView.GetEditControl();

    // Manipulate edit control here.

}

else if (tv_dispInfo->hdr.code == TVN_ENDLABELEDIT)

{

    if (tv_dispInfo->item.pszText != NULL)

    {

        m_treeView.SetItemText(tv_dispInfo->item.hItem,

        tv_dispInfo->item.pszText);

    }

}


The tree view control sends a number of different notification messages, including TVN_BEGINDRAG, TVN_BEGINLABELEDIT, TVN_BEGINRDRAG, TVN_DELETEITEM, TVN_ENDLABELEDIT, TVN_GETDISPINFO, TVN_ITEMEXPANDED, TVN_ITEMEXPANDING, TVN_KEYDOWN, TVN_SELCHANGED, TVN_SELCHANGING, and TVN_SETDISPINFO. Please check your Visual C++ online documentation for more information on handling the different notification messages.

The Rich Edit Control

If you took all 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 may 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, text color, and other types of tasks that are traditionally found in text editors. In fact, a rich edit control (named for the fact that it handles text in Rich Text Format, also called RTF) provides a solid starting point for any text-editing tasks that your application must handle. 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 section, you get a quick introduction to how to create aznd manipulate the rich edit control.

To get started, try out the rich edit control included in the Win95 Controls App. First, click in the text box to give it the focus. Then, just start typing. Want to try out character attributes? Click the ULine button to add underlining to either selected text or the next text you type. To try out paragraph formatting, click either the Left, Center, or Right buttons to specify paragraph alignment. Figure 8.9 shows the rich edit control with the different character and paragraph styles used.

Figure 8.9 : A rich edit control is almost a complete word processor.

Creating the Rich Edit Control

In the Win95 Controls App application, the rich edit control is created in the CreateRichEdit() local member function, which the program calls from the view class's OnCreate() function. In CreateRichEdit(), 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(180, 260, 393, 360), 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 you would use for creating any type of window, with the addition of special styles used with rich edit controls. Table 8.9 lists these special styles.

Table 8.9  Rich Edit Styles

StyleDescription
ES_AUTOHSCROLLAutomatically scrolls horizontally
ES_AUTOVSCROLLAutomatically scrolls vertically
ES_CENTERCenters text
ES_LEFTLeft aligns text
ES_LOWERCASELowercases all text.
ES_MULTILINEEnables multiple lines
ES_NOHIDESELDoesn't hide selected text when losing the focus
ES_OEMCONVERTConverts from ANSI characters to OEM characters and back to ANSI
ES_PASSWORDDisplays characters as asterisks
ES_READONLYDisables editing in the control
ES_RIGHTRight aligns text
ES_UPPERCASEUppercases all text
ES_WANTRETURNInserts return characters into text when enter is pressed

Initializing the Rich Edit Control

After the rich edit control is created, you may want to initialize it in some way. (The Win95 Controls App application doesn't perform additional initialization of the control.) 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 8.10. For more information, please look the functions up in your Visual C++ online documentation.

Table 8.10  Member Functions of the CRichEditCtrl Class

FunctionDescription
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

Manipulating the Rich Edit Control

As you can tell from Table 8.10, you can do a lot more with a rich edit control than can possibly be described in a chapter this size. However, Win95 Controls App 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 usually create menu and toolbar commands for selecting the various options you want to support in the application. Win95 Controls App doesn't have an Options menu or a toolbar. However, it does create four buttons that the user can click to control the rich edit control. Listing 8.20 shows how the program creates these buttons in the CreateRichEdit() function, right after it creates the rich edit control itself.


Listing 8.20  LST8_20.TXT-Creating Editing Control Buttons

m_boldButton.Create("ULine", WS_VISIBLE | WS_CHILD | WS_BORDER,

    CRect(400, 260, 450, 280), this, 112);

m_leftButton.Create("Left", WS_VISIBLE | WS_CHILD | WS_BORDER,

    CRect(400, 285, 450, 305), this, 113);

m_centerButton.Create("Center", WS_VISIBLE | WS_CHILD | WS_BORDER,

    CRect(400, 310, 450, 330), this, 114);

m_rightButton.Create("Right", WS_VISIBLE | WS_CHILD | WS_BORDER,

    CRect(400, 335, 450, 355), this, 115);


Thanks to MFC's message mapping, these buttons are associated with the functions that respond to them. For example, when the user clicks the ULine button, MFC calls the OnULine() method, where the program toggles the underline text attribute. In OnULine(), 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 as shown in Listing 8.21.


Listing 8.21  LST8_21.TXT:-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 8.21, cbSize is the size of the structure; dwMask indicates which members of the structure are valid (can be a combination of CFM_BOLD, CFM_COLOR, CFM_FACE, CFM_ITALIC, CFM_OFFSET, CFM_PROTECTED, CFM_SIZE, CFM_STRIKEOUT, and CFM_UNDERLINE); dwEffects is the character effects (can be a combination of CFE_AUTOCOLOR, CFE_BOLD, CFE_ITALIC, CFE_STRIKEOUT, CFE_UNDERLINE, and CFE_PROTECTED); yHeight is the character height; yOffset is the character baseline offset (for super- 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, returns 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 8.22.


Listing 8.22  LST8_22.TXT-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.

Finally, after setting the character format, the OnULine() function returns the focus to the rich edit control:


m_richEdit.SetFocus();

This is necessary because, by clicking a button, the user has removed the focus from the rich edit control. You don't want to force the user to keep switching manually back to the control every time he clicks a button, so you do it for him by calling the control's SetFocus() member function.

Win95 Controls App also enables the user to switch between the three types of paragraph alignment. This is accomplished similarly to toggling character formats. Listing 8.23 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 8.23  LST8_23.TXT-Changing Paragraph Formats

void CWin95View::OnLeft() 

{

    PARAFORMAT paraFormat;

    paraFormat.cbSize = sizeof(PARAFORMAT);

    paraFormat.dwMask = PFM_ALIGNMENT;

    paraFormat.wAlignment = PFA_LEFT;

    m_richEdit.SetParaFormat(paraFormat);

    m_richEdit.SetFocus();

}

void CWin95View::OnCenter() 

{

    PARAFORMAT paraFormat;

    paraFormat.cbSize = sizeof(PARAFORMAT);

    paraFormat.dwMask = PFM_ALIGNMENT;

    paraFormat.wAlignment = PFA_CENTER;

    m_richEdit.SetParaFormat(paraFormat);

    m_richEdit.SetFocus();

}

void CWin95View::OnRight() 

{

    PARAFORMAT paraFormat;

    paraFormat.cbSize = sizeof(PARAFORMAT);

    paraFormat.dwMask = PFM_ALIGNMENT;

    paraFormat.wAlignment = PFA_RIGHT;

    m_richEdit.SetParaFormat(paraFormat);

    m_richEdit.SetFocus();

}


From Here…

The Windows 95 common controls are a huge subject that could take up a book of its own. However, this chapter attempted to give you enough of an introduction that you can explore the controls further using Visual C++'s online documentation. You might also want to pick up a good book written especially for Windows 95 programming, such as Programming the Windows 95 User Interface by Nancy Winnick Cluts, published by Microsoft Press. For more information on related topics, try the following chapters in this book: