1. What is a virtual list control?
A virtual list control is a list control with the LVS_OWNERDATA style ..
Ii. Why is the virtual list control used?
We know that the list control CListCtrl is usually used. We need to call InsertItem to insert the data to be displayed into the list, so we don't have to worry about where the data is, this is because the control opens up memory space to save the data. Now suppose we want to display a database with a large amount of information and hundreds of thousands of records. There are usually two ways to solve this problem: 1 is to insert a small amount of data in ListCtrl, for example, 100, and then use the [Previous Page] [Next Page] buttons for control, at a certain time point, only records from xxx to xxx + 100 are displayed. 2. Insert all the data into ListCtrl, and then let the user view the data by scrolling. Undoubtedly, many users prefer the second method. Especially for sorted data, users can quickly locate a row by entering the first character of a row on the keyboard. However, if this is done, the InsertItem data insertion process will be very long, and the user will see that the refresh speed of ListCtrl is also very slow, and all the data is stored in the memory, which consumes a lot of memory, when there are tens of thousands of data, it is almost intolerable.
To this end, mfc provides support for virtual lists. A virtual list looks the same as a common ListCtrl, but you do not need to use InsertItem to insert data. It only knows how much data you should display. But how does it know what data to display? The secret is that when the list control needs to display a certain data, it needs to go to the parent window. Suppose the list control contains 100 elements, and 10th to 20 elements (rows) are visible. When the list control is re-painted, it first requests the parent window To Give It 10th data elements. After receiving the request, the parent window fills in the data information in a structure provided by the list, the list can be used to display. After 10th data entries are displayed, the list continues to request the next data.
In a virtual style, ListCtrl supports up to DWORD data items. (The default listctrl control supports up to int data items ). However, the biggest advantage of a virtual list is that it only needs to keep a very small amount of data in the memory, thus accelerating the display speed. Therefore, when you use the list control to display a large database, it is best to use a virtual list.
Not only does CListCtrl provide the virtual list function, but the CListView class of MFC also has the same function.
3. messages of the virtual list control
The virtual List sends a total of three messages to the parent window: when it needs data, it sends the LVN_GETDISPINFO message. This is the most important message. When a user tries to find an element, it sends the LVN_ODFINDITEM message, and the LVN_ODCACHEHINT message is also used to buffer data, which is rarely used.
The virtual list control is easy to use. It only contains three related messages. If you directly use CListCtrl, you should respond to these three messages in the dialog box. If you use the CListCtrl derived class, you can return the reflected messages of the three messages in the derived class. The three messages are:
(1) The LVN_GETDISPINFO control requests data.
(2) LVN_ODFINDITEM
(3) LVN_ODCACHEHINT buffer a part of data
The messages we must respond to are (1). In most cases, we need to respond (2). In rare cases, we need to respond (3)
4. How to Use the virtual list control
1. Create a control, create a virtual list, and create a normal CListCtrl. Add a list control resource in the resource editor. Select the "Owner data" attribute and bind it with a CListCtrl variable. Adding columns and adding imagelist are the same as using listctrl.
2. add elements to the virtual list. Assume that m_list is the control variable of this list. Generally, data is added as follows:
M_list.InsertItem (0, _ T ("Hello world "));
However, you cannot do this for a virtual list. You only need to tell the list of data records:
// Display 100 rows in total
M_list.SetItemCount (100 );
3. process the notification message.
5. How to respond to messages in the virtual list
1. Process LVN_GETDISPINFO notification messages
When the virtual list control needs data, it sends an LVN_GETDISPINFO notification message to the parent window, indicating that a data is requested. Therefore, the owner window of the List (or its own) must process the message. For example, in the case of a derived class (CMyListCtrl is a virtual list Class Object ):
// Reflection messages are processed here
BEGIN_MESSAGE_MAP (CMyListCtrl, CListCtrl)
// {AFX_MSG_MAP (CMyListCtrl)
On_policy_reflect (LVN_GETDISPINFO, OnGetdispinfo)
//} AFX_MSG_MAP
END_MESSAGE_MAP ()
In the processing function of LVN_GETDISPINFO, you must first check the data in the List request. Possible values include:
(1) The LVIF_TEXT must be filled with pszText.
(2) LVIF_IMAGE must be filled with iImage
(3) LVIF_INDENT must be filled with iIndent
(4) LVIF_PARAM must be filled with lParam
(5) The LVIF_STATE must be filled with the state.
Fill in the required data according to its request.
// ========================== Example code ============================ ============================
The following example shows how to fill in the text and image information of a data item required by the list:
LV_DISPINFO * pDispInfo = (LV_DISPINFO *) pNMHDR;
LV_ITEM * pItem = & (pDispInfo)-> item;
Int iItemIndx = pItem-> iItem;
If (pItem-> mask & LVIF_TEXT) // The string buffer is valid.
{
Switch (pItem-> iSubItem ){
Case 0: // enter the name of the data item
Lstrcpy (pItem-> pszText, m_Items [iItemIndx]. m_strItemText );
Break;
Case 1: // fill in Subitem 1
Lstrcpy (pItem-> pszText, m_Items [iItemIndx]. m_strSubItem1Text );
Break;
Case 2: // fill in subitem 2
Lstrcpy (pItem-> pszText, m_Items [iItemIndx]. m_strSubItem2Text );
Break;
}
}
If (pItem-> mask & LVIF_IMAGE) // whether to request the image
PItem-> iImage = m_Items [iItemIndx]. m_iImageIndex;
Even the information on whether or not a row of data is selected (when there is a checkbox) needs to be maintained by the user, for example:
// Do you want to display the selection information for this row?
If (IsCheckBoxesVisible () // User-Defined Function
{
PItem-> mask | = LVIF_STATE;
PItem-> stateMask = LVIS_STATEIMAGEMASK;
If (m_database [itemid]. m_checked)
{
PItem-> state = INDEXTOSTATEIMAGEMASK (2 );
}
Else
{
PItem-> state = INDEXTOSTATEIMAGEMASK (1 );
}
}
2. Process LVN_ODFINDITEM messages
In the resource manager, locate A folder and many files will be displayed. If you press 'A' on the keyboard, the resource manager will automatically find the folder or file whose name is 'A, and select the file. Continue to press A. If there are other files whose names start with 'A', the next file will be selected. If you enter "AB", the 'AB' header file is selected. This is the automatic search function of the list control.
When the virtual list receives an LVM_FINDITEM message, it also sends this message to the parent window to find the target element. The information to be searched is provided through the LVFINDINFO structure. It is a member of the NMLVFINDITEM structure. When the data to be searched is found, the index (row number) of the data should be returned. If no data is found,-1 is returned.
Taking the dialog box as an example, the response function is roughly as follows:
// ========================== Example code ============================ ============================
Void CVirtualListDlg: OnOdfinditemList (NMHDR * pNMHDR, LRESULT * pResult)
{
// Pnmhdr contains the information of the element to be searched.
// The row number of the target element to be selected must be stored in presult. This is the key!
Nmlvfinditem * pfindinfo = (nmlvfinditem *) pnmhdr;
* Presult =-1;
// Search by text?
If (pfindinfo-> lvfi. Flags & lvfi_string) = 0)
{
Return;
}
// This is the string we are looking
Cstring searchstr = pfindinfo-> lvfi. psz;
Int startpos = pfindinfo-> istart; // Save the start position
// Determine whether the last row is used
If (startpos> = m_list.getitemcount ())
Startpos = 0;
Int currentPos = startPos;
// Start searching
Do
{
If (_ tcsnicmp (m_database [currentPos]. m_name,
Searchstr, searchstr. GetLength () = 0)
{
// Select this element and stop searching
* PResult = currentPos;
Break;
}
Currentpos ++;
// Start from scratch
If (currentpos> = m_list.getitemcount ())
Currentpos = 0;
} While (currentpos! = Startpos );
}
Obviously, if there is a lot of data, you must implement a quick search method.
For more information about pfindinfo-> lvfi, see msdn.
3. Process lvn_odcachehint messages.
If we read data from a database or other places slowly, we can use this message to read some data in batches and then provide it to the virtual list one by one based on requests. Lvn_odcachehint is used to buffer data in a program. To improve program performance.
// ========================== Example code ============================ ============================
Use classwizard to reload the onchildnotify function, check whether the lvn_odcachehint message is returned, and then prepare the buffered data:
Nmlvcachehint * pcachehint = NULL;
Nmhdr * phdr = (nmhdr *) lparam;
If (phdr-> code = LVN_ODCACHEHINT)
{
Pcachehint = (NMLVCACHEHINT *) phdr;
// Customize the function to prepare data in the specified range to the buffer zone
PrepCache (pcachehint-> iFrom, pcachehint-> iTo );
}
Else...
Note: If the message is not LVN_ODCACHEHINT, it must be passed to the base class for processing.
5. Modify the data displayed by ListCtrl.
Because the Program maintains the data by itself, you only need to modify the data in the database and then call the CListCtrl: RedrawItems function for re-painting.
6. data selection status and selection box
CListCtrl can display the checkbox selection box. It is useful in some cases. For normal listctrl, you can use the mouse to modify the selection status of an element, but the virtual list will not work. You must process some messages and save the selected status of the data yourself:
Void CVirtualListDlg: ToggleCheckBox (int item)
{
M_database [item]. m_checked =! M_database [item]. m_checked;
M_list.RedrawItems (item, item );
}
Process the LVN_KEYDOWN message and add a response to the Space key to switch the selection status:
Void CVirtualListDlg: OnKeydownList (NMHDR * pNMHDR, LRESULT * pResult)
{
LV_KEYDOWN * pLVKeyDown = (LV_KEYDOWN *) pNMHDR;
If (pLVKeyDown-> wVKey = VK_SPACE)
{
Int item = m_list.GetSelectionMark ();
If (item! =-1)
ToggleCheckBox (item );
}
* PResult = 0;
}
Then process the NM_CLICK message:
Void CVirtualListDlg: OnClickList (NMHDR * pNMHDR, LRESULT * pResult)
{
NMLISTVIEW * pNMListView = (NM_LISTVIEW *) pNMHDR;
LVHITTESTINFO hitinfo;
Hitinfo.pt = pNMListView-> ptAction;
Int item = m_list.HitTest (& hitinfo );
If (item! =-1)
{
// Check whether the mouse has been clicked on the check box?
If (hitinfo. flags & LVHT_ONITEMSTATEICON )! = 0)
{
ToggleCheckBox (item );
}
}
* PResult = 0;
}
VII. Remarks:
1. Virtual lists cannot be sorted.
2. One advantage of a virtual table is that it is easy to maintain synchronization with the database. It is easy and efficient to modify the data in the database and redraw the list.
3. Another advantage of virtual tables is that data can be generated as needed. For example, add a row number to a column.
Http://blog.vckbase.com/iwaswzq/archive/2006/07/07/21113.html
Http://www.codeproject.com/KB/list/virtuallist.aspx