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."
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.
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.
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.
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.
Function | Description |
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.
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. |
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.
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.
Style | Description |
TBS_AUTOTICKS | Enables the trackbar to automatically draw its tick marks |
TBS_BOTH | Draws tick marks on both slides of the slider |
TBS_BOTTOM | Draws tick marks on the bottom of a horizontal trackbar |
TBS_ENABLESELRANGE | Enables a trackbar to display a subrange of values |
TBS_HORZ | Draws the trackbar horizontally |
TBS_LEFT | Draws tick marks on the left side of a vertical trackbar |
TBS_NOTICKS | Draws a trackbar with no tick marks |
TBS_RIGHT | Draws tick marks on the right side of a vertical trackbar |
TBS_TOP | Draws tick marks on the top of a horizontal trackbar |
TBS_VERT | Draws a vertical 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.
Function | Description |
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.
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 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.
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.
Styles | Description |
UDS_ALIGNLEFT | Places the up-down control on the left edge of the buddy control |
UDS_ALIGNRIGHT | Places the up-down control on the right edge of the buddy control |
UDS_ARROWKEYS | Enables the user to change the control's values using the keyboard's Up and Down arrow keys |
UDS_AUTOBUDDY | Makes the previous window the buddy control |
UDS_HORZ | Creates a horizontal up-down control |
UDS_NOTHOUSANDS | Eliminates separators between each set of three digits |
UDS_SETBUDDYINT | Displays the control's value in the buddy control |
UDS_WRAP | Causes 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.
Function | Description |
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.
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.
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.
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.
Function | Description |
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 |
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.
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.
Style | Description |
LVS_ALIGNLEFT | Left aligns items in the large-icon and small-icon views |
LVS_ALIGNTOP | Top aligns items in the large-icon and small-icon views |
LVS_AUTOARRANGE | Automatically arranges items in the large-icon and small-icon views |
LVS_EDITLABELS | Enables the user to edit item labels |
LVS_ICON | Sets the control to the large-icon view |
LVS_LIST | Sets the control to the list view |
LVS_NOCOLUMNHEADER | Shows no column headers in report view |
LVS_NOITEMDATA | Stores only the state of each item |
LVS_NOLABELWRAP | Disallows multiple-line item labels |
LVS_NOSCROLL | Turns off scrolling |
LVS_NOSORTHEADER | Turns off the button appearance of column headers |
LVS_OWNERDRAWFIXED | Enables owner-drawn items in report view |
LVS_REPORT | Sets the control to the report view |
LVS_SHAREIMAGELISTS | Prevents the control from destroying its image lists when the control no longer needs them |
LVS_SINGLESEL | Disallows multiple selection of items |
LVS_SMALLICON | Sets the control to the small-icon view |
LVS_SORTASCENDING | Sorts items in ascending order |
LVS_SORTDESCENDING | Sorts items in descending order |
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:
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.
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);
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");
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.
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.
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.
Style | Description |
TVS_DISABLEDRAGDROP | Disables drag-and-drop operations |
TVS_EDITLABELS | Enables user to edit labels |
TVS_HASBUTTONS | Gives each parent item a button. |
TVS_HASLINES | Adds lines between items in the tree |
TVS_LINESATROOT | Adds a line between the root and child items |
TVS_SHOWSELALWAYS | Forces a selected item to stay selected when losing focus |
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:
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 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);
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.
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.
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.
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 | Doesn't hide 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 |
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.
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 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(); }
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: