Topic : MFC - Views
Author : Unknown
Page : << Previous 6  Next >>
Go to page :

CTreeCtrl::GetChildItem(...) first then calling CTreeCtrl::GetNextSiblingItem(...) repeatedly until it returns NULL value. Also, we can call function CTreeCtrl::ItemHasChildren(...) to examine if a node already has child nodes. The following is the implementation of function CDirView::AddChildrenChildren(...):

(Code omitted)

This function is called when a node is about to expand in function CDirView::OnItemExpanding(...) as follows:

(Code omitted)

To make the application more user friendly, the rest part of this function swaps the directory node image from the image representing open directory (IDB_BITMAP_OPENFOLDER) to the one representing closed directory (IDB_BITMAP_CLOSEFOLDER) when the node is collapsing and vice versa when it is expanding.

9 Simple Explorer, Step 7: File Sort

Sample 9\Explorer is based on sample 8\Explorer, it implements file sorting.

When the list items are displayed in "Report" style, one thing we can implement is to sort all the files by different attributes. For example, if we click on "Name" column, all the files should be sorted by their names; if we click on "Size" column, all files should be sorted by their sizes; if we click on "Type" column, all the files should be sorted by their extensions; if we click on "Updated" column, all the files should be sorted by their updated dates and times.

Sort Related Functions

We can call function CListCtrl::SortItems(...) to implement item sorting. This function has two parameters:

BOOL CListCtrl::SortItems(PFNLVCOMPARE pfnCompare, DWORD dwData);

The function's first parameter is a little special, which is the pointer to a callback function provided by the programmer. The callback function will be used to perform actual comparison. This is because when comparing two items, class CListCtrl has no way of knowing which item should precede the other. In order to provide our own rules of making comparison, we need to implement the callback function.

The callback function has the following format:

int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort);

In order to compare two items, we need to provide each item with a parameter, which is an LPARAM type value. When two items are compared, their parameters will be passed to the callback function, which will return different values indicating which item should precede the other. If the first item (whose parameter is lParam1) should precede the second item (whose parameter is lparam2), the function needs to return -1; if the first item should follow the second item, the function needs to return 1; if the two items are equal, the function needs to return 0.

When calling function CListView::SortItems(...), we can pass different pre-defined values to parameter dwData, which will be further passed to parameter lParamSort of the callback function. This provides us with a way of specifying different types of sorting methods.

Adding Parameters to Items

By now, when creating an item, we did not specify any parameter for it. Actually, any item in the list control can store a 32-bit parameter which can be used to distinguish one item from another. Of course the item index can also be used for this purpose. However, since the relative positions of two items can change frequently, the index of an item is also not fixed. Since the only information passed to the callback function about an item is its parameter, we must make it unique for any item.

In the sample, function CBrowserView::ChangeDir() is customized as follows: when a new item is added to the list control, we set its parameter to its initial index and use it as the identification of this item. This parameter will not change throughout its lifetime:

(Code omitted)

Functions Implementing Comparisons

Four static member functions are implemented for doing different types of comparisons:

(Code omitted)

Actually, in the callback function, one of the above functions is called to perform the comparison according to parameter lParamSort:

(Code omitted)

Please note that the callback function must be either a global function or a static member function. So within it we cannot call CListView::GetListCtrl() directly to obtain the list control. Instead, we must first obtain the current instance of list view, then use it to call function CListView::GetListCtrl() and obtain the list control. This is why at the beginning of the callback function the current active document is first obtained, from which the current active list view (and the list control) is obtained.

Using Parameter to Find an Item

Within the function that implements comparison, the only information we know about an item is its parameter. This is not enough for making comparison. We need to obtain the item and get more information (In our sample, this includes file name, extension, type, and updated time) before proceeding to compare the two items.

An item can be obtained from its parameter by calling function CListCtrl::FindItem(...). In order to call this function, we need to stuff a LV_FINDINFO type object specifying what information is provided for item searching. To search an item by its parameter, we need to set LVFI_PARAM bit of member flags of the structure, and assign the parameter to member lParam. In the sample, a static member function CExplorerView::FindItem(...) is implemented, it can be called from any static member function to find an item using its parameter:

(Code omitted)

Here a LV_FINDINFO type object is stuffed, with LVFI_PARAM bit of member flags set to "1" and the item parameter assigned to member lParam. Then the object is passed to function CListCtrl:: FindItem(...) to search the item in the list control. Function CExplorerView::FindItem(...)'s second parameter is a CListCtrl type reference, this is because within static member function, we must use the instance of an object to call any of its non-static functions. This function returns the current index of the corresponding item.

Comparing Two Items by File Names

The procedure of comparing two items is described in the following paragraphs.

First we pass the parameters of the items to function CExplorerView::FindItem(...) to retrieve their current indices. After the indices are obtained, we stuff a LV_ITEM type object, set LVIF_IMAGE bit of member mask to "1" and call function CListCtrl::GetItem(...). Since in the sample, a directory item is always associated with the default image (In the image list, the image index is 0), we can use an item's image index to tell if it represents a directory or a file. For different situations, the comparing function will return different values (In the sample, a directory always preceeds a file item):

(Code omitted)

In case both items are directories or files, we need to further compare their names. Since file names under Windows( are case insensitive, we neglect character case when performing the comparison. The comparison is done within a for loop, which starts from the first characters and ends under one of the following situations: 1) The two compared characters are different, in which case the character that has the greater value belongs to the item that should follow the other. 2) One of the strings reaches its end. In this case the item with longer file name should follow the other. If two strings are exactly the same, the function returns 0:

(Code omitted)


When the user clicks on one of the columns, the list control sends a notification message LVN_COLUMNCLICK to its parent window. If we want to handle this message within the list view, we need to use macro ON_NOTIFY_REFLECT to map the message to one of its member functions. In the sample, this message mapping is added through using Class Wizard:

(Code omitted)

Within message handler CExplorerView::OnColumnClick(...), function CListCtrl::SortItems(...) is called to perform file sorting:

(Code omitted)

10 Using Form View

Form view is easy to use because the procedure of implementing it is similar to that of a dialog box. We can start from building a dialog template, then adding common controls to the template. Generally everything we can implement in a dialog box can also be implemented in the form view. The difference between the two is that when creating dialog template for the form view, we must set its style to "child" and "no border" (Figure 14-2).

Both tree control and list control can be implemented in a form view. Sample 10\Explorer is based on sample 9\Explorer whose left pane of the splitter window is implemented by a form view. Within the form view, a tree control is implemented for displaying directories. We will see, it is almost the same to use tree control in a form view with using a tree view directly.

New Class and Dialog Template

We must add a new view that is based on class CFormView. This can be implemented by using Class Wizard. Because a form view must be associated with dialog template, before adding the new class, we need to add a dialog template (Of course, we can first generate the class then the dialog template, and change the ID contained in the class to the ID of the dialog template later). When creating dialog template, we need to delete all the default controls, and customize its style to "Child" and "No border". Then we can add a tree control to the template, set the following styles: "Has button", "Has lines", "Line at root". This equals to calling function ::SetWindowLong(...) and setting styles TVS_HASLINES, TVS_LINESATROOT, TVS_HASBUTTONS for the tree control.

When generating the new class, we can associate the ID of the dialog template to it.

Page : << Previous 6  Next >>