[CListCtrl]欄位顯示多行文字

由於MFC提供的CListCtrl並無輸入多行文字功能,因此也需要從繪圖的部分下手。
由於ClistCtrl一次也只能顯示一行文字,因此在進行顯示多行文字之前,需先讓CListCtrl的欄位可以隨著文字段數變動。這邊可以參考[CListCtrl]改變行高 。(參考文獻在上述連結內都有)

在顯示多行文字部分,則是透過改寫DrawItem()來讓文字可以斷行。

使用前,需先將CListCtrl的Owner Draw Fixed屬性打開(坊間流傳的SetExtendedStyle(LVS_OWNERDRAWFIXED)並無效果)。(PS.在這邊浪費了好幾個小時@@,果然跑不起來還是要先查設定@@)

在.h部分
 afx_msg void OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct);  

在.cpp部分
 void CXListCtrl::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)   
 {  
     TCHAR lpBuffer[256];  
     LV_ITEM lvi;  
     lvi.mask = LVIF_TEXT | LVIF_PARAM ;  
     lvi.iItem = lpDrawItemStruct->itemID ;       
     lvi.iSubItem = 0;  
     lvi.pszText = lpBuffer ;  
     lvi.cchTextMax = sizeof(lpBuffer);  
     VERIFY(GetItem(&lvi));  
     LV_COLUMN lvc, lvcprev ;  
     ::ZeroMemory(&lvc, sizeof(lvc));  
     ::ZeroMemory(&lvcprev, sizeof(lvcprev));  
     lvc.mask = LVCF_WIDTH | LVCF_FMT;  
     lvcprev.mask = LVCF_WIDTH | LVCF_FMT;  
     for ( int nCol=0; GetColumn(nCol, &lvc); nCol++)  
     {  
         if ( nCol > 0 )   
         {  
             // Get Previous Column Width in order to move the next display item  
             GetColumn(nCol-1, &lvcprev) ;  
             lpDrawItemStruct->rcItem.left += lvcprev.cx ;  
             lpDrawItemStruct->rcItem.right += lpDrawItemStruct->rcItem.left ;      
         }

         // Get the text   
         ::ZeroMemory(&lvi, sizeof(lvi));  
         lvi.iItem = lpDrawItemStruct->itemID;  
         lvi.mask = LVIF_TEXT | LVIF_PARAM;  
         lvi.iSubItem = nCol;  
         lvi.pszText = lpBuffer;  
         lvi.cchTextMax = sizeof(lpBuffer);  
         VERIFY(GetItem(&lvi));  
         CDC* pDC;  
         pDC = CDC::FromHandle(lpDrawItemStruct->hDC);  
 //--------------- Full selection - highlight -----------------------  
         if ( lpDrawItemStruct->itemState & ODS_SELECTED )  
         {  
             pDC->FillSolidRect(&lpDrawItemStruct->rcItem, GetSysColor(COLOR_HIGHLIGHT)) ;   
             pDC->SetTextColor(GetSysColor(COLOR_HIGHLIGHTTEXT)) ;  
         }  
         else  
         {  
             pDC->FillSolidRect(&lpDrawItemStruct->rcItem, GetSysColor(COLOR_WINDOW)) ;  
             pDC->SetTextColor(GetSysColor(COLOR_WINDOWTEXT)) ;   
         }  
 //--------------- Full selection - highlight -----------------------  
         pDC->SelectObject(GetStockObject(DEFAULT_GUI_FONT));  
         UINT        uFormat  = DT_LEFT ;  
         ::DrawText(lpDrawItemStruct->hDC, lpBuffer, strlen(lpBuffer),   
              &lpDrawItemStruct->rcItem, uFormat) ;  
         pDC->SelectStockObject(SYSTEM_FONT) ;  
 //Enable measureitem().  
         this->OnSetFont(NULL, NULL);  //配合MeasureItem()用的        
     }  
 }  

目前這個範例裡面還看不出是怎麼讓文字斷行的,但可確定的是,由於這個function會重新變更欄位的字體與背景顏色。因此當同時還有使用到OnNMCustomdrawLst()的話,OnNMCustomdrawLst()所繪製的顏色會被DrawItem()所覆蓋掉(因OnNMCustomdrawLst()比DrawItem()早執行),所以這邊要注意。
如有找到換行的方法,之後再補上。
PS.測試結果,DrawText似乎會在遇到newline時,就會把後面的文字自動輸出到下一行。

Problem:
1.在移動欄位title時,欄位內顯示之文字並不會像CListCtrl一樣,會加上"..."來代表有文字被遮蓋。
看來這邊是需要自行處理。(變更欄位大小時,畫面不會更新到其他地方)
2.上一次顯示文字之尾端總是會殘留在下一個欄位的開頭,這邊也需要另外再處理。
3.此範例所繪製的文字會貼齊於Cell的最左側邊界,會與CListCtrl顯示結果不同。

新找到的:Multi-line List Control
這篇提供的方法可以解決P.1、P.2、P3的問題。
差別在於,這篇提供的畫文字方法是CDC::DrawTextEx。這樣就可以讓CListCtrl自行去處理上述之問題,不然要手動處理的話,倒是挺麻煩的。如此一來,函式也變得比較乾淨了。

修改過後的函式如下:
 void CXListCtrl::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)   
 {  
     LPDRAWITEMSTRUCT lpDrawHighLight = new DRAWITEMSTRUCT();  
     CString cstrSubitemText = "";  
     CRect rcSubItemRect;          
     CDC* pDC =     CDC::FromHandle(lpDrawItemStruct->hDC);        //不用釋放  
     LV_ITEM lvi;  
     rcSubItemRect.SetRectEmpty();  
     LV_COLUMN lvc, lvcprev ;  
     ::ZeroMemory(&lvc, sizeof(lvc));  
     ::ZeroMemory(&lvcprev, sizeof(lvcprev));  
     lvc.mask = LVCF_WIDTH | LVCF_FMT;  
     lvcprev.mask = LVCF_WIDTH | LVCF_FMT;  
     //lpDrawItemStruct->rcItem為目前所要畫出文字的row的邊界。  
     for ( int nCol=0; GetColumn(nCol, &lvc); nCol++)  
     {  
         // Get Previous Column Width in order to move the next display item  
         if ( nCol > 0 )   
         {              
             GetColumn(nCol-1, &lvcprev) ;  
             lpDrawItemStruct->rcItem.left += lvcprev.cx;  
             //lpDrawItemStruct->rcItem.right += lpDrawItemStruct->rcItem.left;            //會影響到最後一個SubItem的顯示長度。  
         }  
 //--------------- Full selection - highlight -----------------------  
         //Let Drawed highlight like the CListCtrl do it.  
         ::memcpy(lpDrawHighLight, lpDrawItemStruct, sizeof(DRAWITEMSTRUCT));  
         lpDrawHighLight->rcItem.left += this->GetStringWidth(" ");        //保留開頭的空白。讓行為同CListCtrl。  
         if ( lpDrawItemStruct->itemState & ODS_SELECTED )  
         {  
             pDC->FillSolidRect(&lpDrawHighLight->rcItem, GetSysColor(COLOR_HIGHLIGHT)) ;   
             pDC->SetTextColor(GetSysColor(COLOR_HIGHLIGHTTEXT)) ;  
         }  
         else  
         {  
             pDC->FillSolidRect(&lpDrawHighLight->rcItem, GetSysColor(COLOR_WINDOW)) ;  
             pDC->SetTextColor(GetSysColor(COLOR_WINDOWTEXT)) ;   
         }  
 //--------------- Full selection - highlight -----------------------  
 //-------------- Draw item -------------------------  
         pDC->SelectObject(GetStockObject(DEFAULT_GUI_FONT));  
         if ( !this->GetSubItemRect(lpDrawItemStruct->itemID, nCol, LVIR_BOUNDS, rcSubItemRect) ) {  
             continue;    };  
         //::DrawText(lpDrawItemStruct->hDC, lpDisplayBuf, nShowWordsLen, &lpDrawText->rcItem, uFormat) ;  
         rcSubItemRect.left += this->GetStringWidth(" ");  
         rcSubItemRect.right -= this->GetStringWidth(" ");  
         cstrSubitemText = this->GetItemText(lpDrawItemStruct->itemID, nCol);  
         pDC->DrawTextEx(cstrSubitemText, rcSubItemRect, (DT_LEFT|DT_NOPREFIX|DT_TOP|DT_WORD_ELLIPSIS), NULL);  
         pDC->SelectStockObject(SYSTEM_FONT) ;  
 //-------------- Draw item -------------------------  
 //Enable measureitem().  
         this->OnSetFont(NULL, NULL);          
     }    //End of for ( int nCol=0; GetColumn(nCol, &lvc); nCol++)  
     if ( lpDrawHighLight != NULL )    {  
         delete lpDrawHighLight;  
         lpDrawHighLight = NULL;  
     }  
 }  

No comments:

Post a Comment

Build docker image from multiple build contexts

Build docker image from multiple build contexts ...