This example demonstrates the virtual list and self-drawing feature of the list control, and also demonstrates the function of some system enclosures and the use of the interface.
Click here to download the code of this article. If there is a problem when compiling sample programs, you need to go http://www.microsoft.com/msdownload/platformsdk/sdkupdate/ upgrade your header files and library files
Preparatory reading
Before reading this article, it is recommended to have a basic understanding of the list view control and system shell. It is recommended to read the following SDK articles
Shell FAQ List-View Controls Overview Using List-View Controls Customizing A Control's Appearance Using Custom Draw
Create an application
Create an SDI application using the MFC Application Wizard, and select the base class of the view as ClistView in the final step. After the creation is complete, remove the menu and toolbar buttons for saving, editing, and printing in the resource (because these features are not implemented).
The creation of the virtual list
This article uses the virtual list technology so that the display information is acquired when the first display is displayed. In order to create a virtual list, you need to specify the style of the list before creating.
BOOL CPicViewView :: PreCreateWindow (CREATESTRUCT & cs) {cs.style & = ~ LVS_TYPEMASK; cs.style | = LVS_ICON | LVS_OWNERDATA; return CListView :: PreCreateWindow (cs);} Also, since Overlay icon list items are also dynamically acquired, So you need to set up a dynamic overlay icon
Void cpicviewview :: OnInitialUpdate () {clistview :: oninitialupdate (); getListCtrl (). setCallbackmask;}
Cache display information
Before the list needs to display a range of items, the list will send a LVN_ODCACHEHINT notification, and the application can capture this message to cache the display information of the partial list to improve performance.
void CPicViewView :: OnOdcachehint (NMHDR * pNMHDR, LRESULT * pResult) {NMLVCACHEHINT * pCacheHint = (NMLVCACHEHINT *) pNMHDR; PrepCache (0, min (5, m_arpFolderItems.GetSize ())); PrepCache (pCacheHint-> iFrom, pCacheHint- > ITO); PrepCache (Max (0, m_arpfolderitems.getsize () - 5), m_arpfolderitems.getsize ()); * PRESULT = 0;} The list will send LVN_GETDISPINFO notification before the list needs to display, the application can capture This message provides information about display. If the list item that needs to be displayed during the display is in the cache, display information can be obtained from the cache. Otherwise, you need to be reopened.
void CPicViewView :: OnGetdispinfo (NMHDR * pNMHDR, LRESULT * pResult) {LV_DISPINFO * pDispInfo = (LV_DISPINFO *) pNMHDR; if (pDispInfo-> item.iItem == - 1) return; HRESULT hr = S_OK; LPCITEMIDLIST pidlItem = m_arpFolderItems [ pDispInfo-> item.iItem]; CFolderItemInfo * pFolderItemInfo = FindItemInCache (pidlItem); BOOL bCached = TRUE; if (pFolderItemInfo == NULL) {bCached = FALSE; pFolderItemInfo = new CFolderItemInfo; GetItemInfo (pidlItem, pFolderItemInfo);} if (pDispInfo -> item.mask & LVIF_TEXT) {lstrcpyn (pDispInfo-> item.pszText, pFolderItemInfo-> tszDisplayName, pDispInfo-> item.cchTextMax);} if (pDispInfo-> item.mask & LVIF_IMAGE) {pDispInfo-> item.iImage = pFolderItemInfo-> IICON;} if (pdispinfo-> item.mask & lvif_state) {pdispinfo-> item.State = pfolderiteminfo-> state;}} if (! bcached) delete pFolderItemInfo; * PRESULT = 0;} file icon display
By default, the icon of the list item is its system icon. First get the list of system images
int CPicViewView :: OnCreate (LPCREATESTRUCT lpCreateStruct) {if (CListView :: OnCreate (lpCreateStruct) == -1) return -1; HRESULT hr = SHGetMalloc (& m_pMalloc); if (FAILED (hr)) return -1; hr = SHGetDesktopFolder (& m_psfDesktop); if (FAILED (hr)) return -1; SHFILEINFO shfi; ZeroMemory (& shfi, sizeof (SHFILEINFO)); HIMAGELIST hi = (HIMAGELIST) SHGetFileInfo (NULL, 0, & shfi, sizeof (SHFILEINFO), SHGFI_ICON | SHGFI_SYSICONINDEX | SHGFI_SMALLICON); GetListCtrl () SetImageList (CImageList :: FromHandle (hi.), LVSIL_SMALL); hi = (HIMAGELIST) SHGetFileInfo (NULL, 0, & shfi, sizeof (SHFILEINFO), SHGFI_ICON | SHGFI_SYSICONINDEX | SHGFI_LARGEICON); GetListCtrl (). Setimagelist (cimagelist :: fromHandle (HI), LVSIL_NORMAL); RETURN 0;} Then obtain the index of the icon in the system image list when obtaining file information.
If the list item is an image file and the image is successfully loaded from the file, the self-drawing function is used to replace the default icon.
void CPicViewView :: OnCustomDraw (NMHDR * pNMHDR, LRESULT * pResult) {LPNMLVCUSTOMDRAW lpNMCustomDraw = (LPNMLVCUSTOMDRAW) pNMHDR; switch (lpNMCustomDraw -> nmcd.dwDrawStage) {case CDDS_PREPAINT: * pResult = CDRF_NOTIFYITEMDRAW; return; case CDDS_ITEMPREPAINT: * pResult = CDRF_NOTIFYPOSTPAINT ; return; case CDDS_ITEMPOSTPAINT: {int iItem = lpNMCustomDraw -> nmcd.dwItemSpec; if (iItem == - 1) {* pResult = CDRF_DODEFAULT; return;} CFolderItemInfo * pItemInfo = FindItemInCache (m_arpFolderItems [iItem]); if (pItemInfo = = NULL || pItemInfo-> bFailLoadPic || pItemInfo-> pic.m_pPict == NULL) {* pResult = CDRF_DODEFAULT; return;} CRect rectIcon;. GetListCtrl () GetItemRect (iItem, & rectIcon, LVIR_ICON); CDC * pDC = CDC :: fromHandle (lpnmcustomDraw-> nmcd.hdc); Piteminfo-> Pic.Render (PDC, RectCon, RectCon);} * preSult = CDRF_NewFont; Return;} * PRESULT = 0;} The code is an image in the list item icon using the image of the acquired file display information.
Get display information
In order to cache the display information of the list item, or the list item is displayed, it is necessary to obtain information such as the text, icon, Overlay icon, and thumbnail of the list item. Here, ilcombine uses ilcombine to convert the relative PIDL in the cache to a complete PIDL, and then obtain the full path to the file, then call the OLELOADPICTUREPATH function to load the image.
void CPicViewView :: GetItemInfo (LPCITEMIDLIST pidl, CFolderItemInfo * pItemInfo) {HRESULT hr = theApp.SHGetDisplayNameOf (pidl, pItemInfo-> tszDisplayName); IShellIcon * pShellIcon = NULL; hr = m_psfFolder-> QueryInterface (IID_IShellIcon, (LPVOID *) & pShellIcon) ; if (SUCCEEDED (hr) && pShellIcon) {pShellIcon-> GetIconOf (pidl, 0, & pItemInfo-> iIcon); pShellIcon-> Release ();} IShellIconOverlay * pShellIconOverlay = NULL; hr = m_psfFolder-> QueryInterface (IID_IShellIconOverlay, (LPVOID *) & pShellIconOverlay); if (SUCCEEDED (hr) && pShellIconOverlay) {int nOverlay = 0; pShellIconOverlay-> GetOverlayIndex (pidl, & nOverlay); pItemInfo-> state = INDEXTOOVERLAYMASK (nOverlay); pShellIconOverlay-> Release ();} LPITEMIDLIST pidlItemFull = Ilcombine (M_PIDLFOLDER, PIDL); if (PIDLITEMFULL) {IF (SHGETPATHFROMIDLIST (PIDLITEMFULL, PITEMINFO-> Tszpath) {Uses_Conversion; HR = OLELOADPICTUREPATH (T2ole (Piteminfo-> TSZ PATH), NULL, 0, RGB (255, 255, 255), IID_IPICTURE, (LPVOID *) & Piteminfo-> Pic.m_ppict); if (Failed (HR)) {Piteminfo-> bFailloadPic = true; trace ("OLELOADPICTUREPATH FAILED% S / R / n ", piteminfo-> tszpath);}}} m_pmalloc-> free (PIDLITEMFULL);}}
When changing the directory, you need to rebuild the cache of the directory content. This includes the PIDL and ISHELLFOLDER interface pointers of the directory, the relative PIDL of the directory content, and the display information of the list item (based on performance, the display information of the list item is cached when the LVN_ODCACHEHINT is received).
LPITEMIDLIST m_pidlFolder; IShellFolder * m_psfFolder; CTypedPtrArray
Open folder
This application displays the content of the folder instead of displaying the content of the document, so I overload the processing when opening the file, and the directory selection dialog is displayed instead of the file open dialog.
void CPicViewApp :: OnFileOpen () {TCHAR tszDisplayName [_MAX_PATH]; TCHAR tszPathSelected [_MAX_PATH]; LPITEMIDLIST pidlSelected = PidlBrowse (m_pMainWnd-> GetSafeHwnd (), 0, tszDisplayName); if (pidlSelected) {if (SHGetPathFromIDList (pidlSelected, tszPathSelected) ) {Cdocument * pdocument = OpenDocumentfile (tszpathselected); pdocument-> settitle (tszdisplayName); ilfree (pidlselected);}}}
Note that the PIDL obtained from the casing call usually needs to call iLFree or Imalloc :: Free release. One exception is the relative PIDL obtained by calling a function shbindtoparent because it is part of the input parameter complete PIDL, so it doesn't have to be released.
When new or open "file", the document needs to notify the view of the current folder, which is achieved by calling cdocument :: updateAllViews and overload CView :: onupdate. The processing of this notification is to clear the cache data of the previous directory, cache the data of the new directory, and update the document title. Open file or directory
For easy use, double-click the list item, you can open the subdirectory in the same window, or call the system's default handler to open the file. If the file is a shortcut, then open the target of the shortcut.
void CPicViewView :: OnDblclk (NMHDR * pNMHDR, LRESULT * pResult) {LPNMLISTVIEW lpnm = (LPNMLISTVIEW) pNMHDR; if (lpnm-> iItem == - 1) return; * pResult = 0; HRESULT hr = S_OK; LPCITEMIDLIST pidlItem = m_arpFolderItems [lpnm-> iItem]; LPITEMIDLIST pidlItemFull = ILCombine (m_pidlFolder, pidlItem); LPITEMIDLIST pidlItemTarget = NULL; hr = theApp.SHGetTargetFolderIDList (pidlItemFull, & pidlItemTarget); if (pidlItemTarget) {if (theApp.ILIsFolder (pidlItemTarget)) {CFolderChange FolderChange ; FolderChange.m_pidlFolder = pidlItemTarget; OnFolderChange (& FolderChange);} else {SHELLEXECUTEINFO ShExecInfo; ShExecInfo.cbSize = sizeof (SHELLEXECUTEINFO); ShExecInfo.fMask = SEE_MASK_IDLIST; ShExecInfo.hwnd = NULL; ShExecInfo.lpVerb = NULL; ShExecInfo.lpFile = NULL Shexecinfo.lpidList = pidlitemtarget; shexecinfo.lpparameters = null; ShexecInfo.lpdirectory = Null; shexecinfo.nshow = sw_maximize; shexecinfo.hinstapp = null; shellexecuteEx (& shexecinfo);} m_pmalloc-> free (PIDLITEMTARGET); m_pmalloc-> free (PIDLITEMFULL);}}
Performance optimization
For a better user experience, you can use a custom icon size (this needs to be fully drawn to the icon area of the list item), load the image with a separate thread, or use a thumbnail buffer adjusted to the icon size (so each time Don't stretch the image when drawing). But this is beyond the scope of this article. Interested readers can try themselves. reference
For more information, you can refer to
Shell FAQ List-View Controls Overview Using List-View Controls Customizing A Control's Appearance Using Custom Draw