• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            S.l.e!ep.¢%

            像打了激速一樣,以四倍的速度運轉,開心的工作
            簡單、開放、平等的公司文化;尊重個性、自由與個人價值;
            posts - 1098, comments - 335, trackbacks - 0, articles - 1
              C++博客 :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

            預備性閱讀
            在閱讀本文之前,建議先對列表視圖控件和系統外殼有一個基本的了解。建議閱讀以下SDK文章

            Shell FAQ
            List-View Controls Overview
            Using List-View Controls
            Customizing a Control's Appearance Using Custom Draw
            創建應用程序
            使用MFC應用程序向導創建一個SDI應用程序,在最后一步選擇視圖的基類為CListView。創建完成之后,在資源中去掉保存、編輯和打印等功能的菜單和工具欄按鈕(因為這些功能沒有實現)。

            虛列表的創建
            本文采用虛列表技術,使得顯示信息是在第一次顯示的時候才被獲取。為了創建虛列表,在創建之前需要指定列表的風格

            BOOL CPicViewView::PreCreateWindow(CREATESTRUCT& cs)
            {
            ??? cs.style&=~LVS_TYPEMASK;
            ??? cs.style|=LVS_ICON|LVS_OWNERDATA;
            ??? return CListView::PreCreateWindow(cs);
            }
            同時,因為列表項的Overlay圖標也是被動態獲取的,所以需要設置動態Overlay圖標

            void CPicViewView::OnInitialUpdate()
            {
            ??? CListView::OnInitialUpdate();
            ??? GetListCtrl().SetCallbackMask(LVIS_OVERLAYMASK);
            }

            緩存顯示信息
            在列表需要顯示一個范圍的項目之前,列表會發送LVN_ODCACHEHINT通知,應用程序可以捕獲這個消息來緩存部分列表的顯示信息,以提高性能。

            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;
            }
            在列表需要顯示一個項目之前,列表會發送LVN_GETDISPINFO通知,應用程序可以捕獲這個消息來提供項目的顯示信息。如果顯示時需要顯示的列表項在緩存中,那么可以從緩存中獲取顯示信息。否則需要重新從文件獲得。

            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;
            }


            文件圖標的顯示
            默認情況下,列表項的圖標就是其系統圖標。首先獲得系統圖像列表

            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;
            }

            然后在獲取文件信息時,從文件獲得其圖標在系統圖像列表中的索引。

            如果列表項是圖像文件,并且從文件成功載入圖像,那么使用自畫功能以替換默認的圖標。

            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,rectIcon,rectIcon);
            ??????? }
            ??????? *pResult=CDRF_NEWFONT;return;
            ??? }
            ??? * pResult=0;
            }

            上面的代碼是使用獲取的文件顯示信息中的圖像,在列表項圖標的區域畫圖。

            獲取顯示信息
            為了緩存列表項的顯示信息,或者顯示列表項,需要獲取列表項的文字、圖標、Overlay圖標和縮略圖等信息。這里使用了ILCombine來把緩存中的相對PIDL轉化為完整的Pidl,再據此獲得文件的完整路徑,然后調用OleLoadPicturePath函數載入圖像。

            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->tszPath)
            ??????????????????? ,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);
            ??? }
            }

            緩存目錄的數據
            在更改目錄時,需要重建目錄內容的緩存。這包括目錄的pidl和IShellFolder接口指針,目錄內容的相對pidl,以及列表項的顯示信息(基于性能上的考慮,列表項的顯示信息是在接收到LVN_ODCACHEHINT通知的時候緩存的)。

            LPITEMIDLIST m_pidlFolder;
            IShellFolder * m_psfFolder;
            CTypedPtrArray<CPtrArray,LPITEMIDLIST> m_arpFolderItems;
            CTypedPtrMap<CMapPtrToPtr,LPITEMIDLIST,CFolderItemInfo*> m_mapCache;
            ?

            void CPicViewView::EnterFolder(LPCITEMIDLIST pidl)
            {
            ??? USES_CONVERSION;
            ??? m_pidlFolder=ILClone(pidl);
            ??? if(m_pidlFolder){
            ??????? LPENUMIDLIST ppenum = NULL;
            ??????? LPITEMIDLIST pidlItems = NULL;
            ??????? ULONG celtFetched;
            ??????? HRESULT hr;
            ??????? hr = m_psfDesktop->BindToObject(m_pidlFolder, NULL, IID_IShellFolder, (LPVOID *) &m_psfFolder);
            ??????? if(SUCCEEDED(hr)){
            ??????????? hr = m_psfFolder->EnumObjects(NULL,SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &ppenum);
            ??????????? if(SUCCEEDED(hr)){
            ??????????????? while( hr = ppenum->Next(1,&pidlItems, &celtFetched) == S_OK && (celtFetched) == 1){
            ??????????????????? m_arpFolderItems.Add(pidlItems);
            ??????????????????? }
            ??????????? }
            ??????? }
            ??????? GetListCtrl().SetItemCount(m_arpFolderItems.GetSize());
            ??? }
            }

            ?

            打開文件夾
            本應用程序顯示文件夾的內容而不是顯示文檔的內容,所以我重載了打開文件時的處理,顯示目錄選擇對話框而不是文件打開對話框。

            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);
            ??????? }
            ??? }
            }

            注意從外殼調用獲得的PIDL一般都需要調用ILFree或者IMalloc::Free釋放。一個例外是調用函數SHBindToParent獲得的相對pidl,因為它是輸入的參數完整pidl的一部分,所以不必另外釋放。

            在新建或者打開“文件”時候,文檔需要通知視圖當前文件夾的更改,這是通過調用CDocument::UpdateAllViews和重載CView::OnUpdate實現的。視圖對這個通知的處理是清除上一個目錄的緩存數據,緩存新目錄的數據,以及更新文檔標題。
            ?

            打開文件或者目錄
            為了使用方便,雙擊列表項時可以在同一窗口打開子目錄,或者調用系統的默認處理程序打開文件。如果文件是快捷方式,那么打開快捷方式的目標。

            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);
            ??? }
            }
            ?

            性能的優化
            為了更好的用戶體驗,可以使用自定義的圖標大?。ㄟ@需要完全自行繪制列表項的圖標區域),用單獨的線程來載入圖像,或者使用調整到圖標大小的縮略圖緩沖(這樣每次繪制時不必拉伸圖像)。但是這超出了本文的范圍。有興趣的讀者可以自己試一下。

            參考
            需要更多信息的話,可以參考

            Shell FAQ
            List-View Controls Overview
            Using List-View Controls
            Customizing a Control's Appearance Using Custom Draw
            本文來自焦點核(X)軟件安全技術網,原文地址:http://www.xfocusx.com

            精品国产一区二区三区久久久狼| 无码人妻精品一区二区三区久久 | 久久99精品国产麻豆宅宅| 99久久免费国产精品热| a级毛片无码兔费真人久久| 久久国产V一级毛多内射| 亚洲综合日韩久久成人AV| 人人狠狠综合久久亚洲婷婷| 中文字幕无码久久人妻| 久久精品国产影库免费看| 成人久久免费网站| 久久99精品九九九久久婷婷| 久久久一本精品99久久精品88| 日本免费一区二区久久人人澡| 久久久无码精品亚洲日韩京东传媒 | 乱亲女H秽乱长久久久| 亚洲国产成人精品女人久久久| 久久久久国产精品| 亚洲国产精品一区二区久久hs| 久久精品二区| 欧美激情精品久久久久| 久久精品国产亚洲AV大全| 久久综合视频网| 午夜精品久久久久久影视riav| 97精品国产91久久久久久| 亚洲AV无码久久精品成人| 欧美久久久久久| 欧美一区二区久久精品| 免费精品久久久久久中文字幕| 秋霞久久国产精品电影院| 国内精品久久久久| 国内精品久久久久影院优| 久久国产欧美日韩精品| 三上悠亚久久精品| 久久久久久久久无码精品亚洲日韩 | 久久久黄色大片| 久久人妻无码中文字幕| 久久99久国产麻精品66| 亚洲精品乱码久久久久66| 国产情侣久久久久aⅴ免费| 成人妇女免费播放久久久|