FAQ VC++ et MFCConsultez toutes les FAQ
Nombre d'auteurs : 20, nombre de questions : 545, dernière mise à jour : 5 avril 2013 Ajouter une question
Cette faq a été réalisée pour répondre aux questions les plus fréquement posées sur le forum Développement Visual C++
Je tiens à souligner que cette faq ne garantit en aucun cas que les informations qu'elle contient sont correctes ; Les auteurs font le maximum, mais l'erreur est humaine. Si vous trouvez une erreur, ou si vous souhaitez devenir redacteur, lisez ceci.
Sur ce, je vous souhaite une bonne lecture. Farscape
- Comment initialiser et remplir une CListCtrl ?
- Comment récupérer les informations sur une colonne d'une CListCtrl ?
- Comment détecter le changement de ligne sur une CListCtrl ?
- Comment sélectionner et faire apparaître la dernière ligne d'une CListCtrl ?
- Comment intercepter le message de sélection dans une CListCtrl ?
- Comment retrouver le nombre de colonnes d'une CListCtrl ?
- Comment mettre une case à cocher dans une CListCtrl ?
- Comment cocher/décocher une case à cocher dans une CListCtrl ?
- Comment récupérer l'état d'une case à cocher dans une CListCtrl ?
- Comment récupérer la notification de changement d'état pour une case à cocher dans une CListCtrl ?
- Comment empêcher le changement de taille d'une colonne dans une CListCtrl ?
- Comment mettre en surbrillance la ligne entière d'une CListCtrl ?
- Comment changer les couleurs dans une CListCtrl ?
- Comment personnaliser l'affichage d'une CListCtrl ?
- Comment personnaliser l'entête d'une CListView ou CListCtrl ?
- Comment ne pas afficher d'image sur la première colonne d'une ClistCtrl ?
- Comment récupérer les informations d'une ligne pour les différents clics souris dans une CListCtrl ?
- Comment afficher des icônes dans les subitems en mode "report" dans un CListCtrl
- Comment intercepter les messages du clavier dans une CListCtrl ?
- Comment désélectionner une ligne d'une CListCtrl ?
- Comment intercepter les messages du clavier dans une CListCtrl ?
- Comment déplacer une ligne dans une CListCtrl ?
- Comment récupérer l'index de la première ligne sélectionnée d'une CListCtrl ?
- Comment redimensionner une CListCtrl et ses colonnes ?
L'exemple ci-dessous montre comme créer un entête de cinq colonnes centrées à gauche, avec une taille initiale de 70 pixels.
On commence par insérer la ligne puis on met à jour les colonnes.
Pour montrer les différentes possibilités une ligne sur deux est sélectionnée, ainsi que le type LVS_EX_FULLROWSELECT permettant d'avoir une ligne de sélection non limitée à la première colonne.
Code C++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | void CSdiSplitterView::OnInitialUpdate() { CListView::OnInitialUpdate(); CListCtrl& theCtrl = GetListCtrl(); // TODO: You may populate your ListView with items by directly accessing // its list control through a call to GetListCtrl(). CString strText; for(int nc=0;nc<5;nc++) { strText.Format(TEXT("ColHeader%d"),nc); theCtrl.InsertColumn(nc,strText,LVCFMT_LEFT,70); } // force le mode report , //inutile si fixé dans les ressources dans le cas d'un controle CListCtrl. theCtrl.ModifyStyle(0,LVS_REPORT); // style etendu voir MSDN pour les autres styles possibles. theCtrl.SetExtendedStyle(theCtrl.GetExtendedStyle() | LVS_EX_FULLROWSELECT ); static_cast<CMainFrame *>(AfxGetMainWnd())->m_wndSplitter.SetColumnInfo(0,5*70,100); for (int i=0;i < 10;i++) { strText.Format(TEXT("item %d"), i); // Insert the item, select every other item. theCtrl.InsertItem(LVIF_TEXT|LVIF_STATE, i, strText,(i%2)==0?LVIS_SELECTED : 0, LVIS_SELECTED,0, 0); // Initialize the text of the subitems. for (int j=1;j<theCtrl.GetHeaderCtrl()->GetItemCount();j++) { strText.Format(TEXT("Sub-Item %d %d"), i, j); theCtrl.SetItemText(i, j, strText); } } } |
Note : l'exemple ci-dessous s'applique à une CListView ,dans le cas d'utilisation directe d'un CListCtrl la variable theCtrl sera remplacée par la variable contrôle lié au contrôle présent dans les ressources.
Exemple : SdiSplitter.zip
En mode report la CListCtrl ou la CListView possède une têtière qui dépend de la classe CHeaderCtrl.
Pour récupérer les informations d'une colonne du HeaderCtrl on procédera comme suit :
Avec une CListCtrl :
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 | TCHAR szText[255]; HDITEM hdi; hdi.mask = HDI_WIDTH|HDI_FORMAT|HDI_TEXT|HDI_IMAGE|HDI_BITMAP; hdi.pszText = szText; hdi.cchTextMax = sizeof(szText); VERIFY(MyListCtrl.GetHeaderCtrl()->GetItem(iPos, &hdi)); |
Pour l'exemple j'ai choisi de tout demander.
Dans le cas du libellé il faut fournir l'adresse d'une chaîne de caractères valide comme dans l'exemple.
Avec une CListView :
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 | TCHAR szText[255]; HDITEM hdi; hdi.mask = HDI_WIDTH|HDI_FORMAT|HDI_TEXT|HDI_IMAGE|HDI_BITMAP; hdi.pszText = szText; hdi.cchTextMax = sizeof(szText); CListCtrl& theCtrl = GetListCtrl(); VERIFY(theCtrl.GetHeaderCtrl()->GetItem(iPos, &hdi)); |
En interceptant avec ClassWizard l'événement LVN_ITEMCHANGED ,complété du test qui suit dans l'exemple.
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | BEGIN_MESSAGE_MAP(CTestMDIView, CFormView) //{{AFX_MSG_MAP(CTestMDIView) ON_NOTIFY(LVN_ITEMCHANGED, IDC_LISTCTRL, OnItemchangedListctrl) //}}AFX_MSG_MAP END_MESSAGE_MAP() void CTestMdiView::OnItemchangedList1(NMHDR* pNMHDR, LRESULT* pResult) { NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR; // TODO: Add your control notification handler code here if(pNMListView->iItem>=0 && (pNMListView->uNewState & LVIS_SELECTED)) { // affichage de l'id et du texte colonne 0 TRACE("\nItem:%d:%s",pNMListView->iItem, (const char *)m_ListCtrl.GetItemText(pNMListView->iItem,0)); } *pResult = 0; } |
Exemple de sélection et de scroll éventuel sur la dernière ligne d'une CListCtrl:
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 | int nCount = pMyListCtrl->GetItemCount(); // nombre d'elements if (nCount > 0) { //pour la sélection pMyListCtrl->SetItemState(nCount-1, LVIS_SELECTED, LVIS_SELECTED); // pour faire apparaître la ligne au besoin par un scroll pMyListCtrl->EnsureVisible(nCount-1, FALSE); } |
A partir de ClassWizard sélectionner l'identifiant de la CListCtrl ,
Générer la fonction en réponse au message NM_CLICK.
On obtiendra les informations de sélection en castant le pointeur pNMHDR en pointeur sur la structure NMITEMACTIVATE.
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | BEGIN_MESSAGE_MAP(CTestMdiView, CFormView) //{{AFX_MSG_MAP(CTestMdiView) ON_NOTIFY(NM_CLICK, IDC_LIST, OnClickList) //}}AFX_MSG_MAP END_MESSAGE_MAP() void CTestMdiView::OnClickList(NMHDR* pNMHDR, LRESULT* pResult) { // TODO: Add your control notification handler code here NMITEMACTIVATE *pItem=(NMITEMACTIVATE *)pNMHDR; TRACE("\n iItem:%d iSubItem:%d",pItem->iItem,pItem->iSubItem); *pResult = 0; } |
Une ClistCtrl utilise un CHeaderCtrl pour la gestion de l'entête.
Pour retrouver le nombre de colonnes il suffit de faire :
Code c++ : | Sélectionner tout |
1 2 | int nColHeader=m_ListCtrl.GetHeaderCtrl()->GetItemCount() ; |
Il suffit de spécifier le style LVS_EX_CHECKBOXES
Code c++ : | Sélectionner tout |
1 2 | m_ListCtrl.SetExtendedStyle(m_ListCtrl.GetExtendedStyle() | LVS_EX_FULLROWSELECT | LVS_EX_CHECKBOXES); |
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 | void SetLstCtrlCheck (CListCtrl &rListCtrl,WPARAM nItemIndex, BOOL bCheck) { ListView_SetCheckState(rListCtrl. GetSafeHwnd( ), nItemIndex, bCheck); /* ou ListView_SetItemState (rListCtrl. GetSafeHwnd( ), nItemIndex, UINT((int(bCheck) + 1) << 12), LVIS_STATEIMAGEMASK); */ } |
Code c++ : | Sélectionner tout |
1 2 3 4 5 | BOOL GetLstCtrlCheck (CListCtrl &rListCtrl,WPARAM nItemIndex) { return (ListView_GetCheckState(rListCtrl. GetSafeHwnd( ), nItemIndex)); } |
Il faut récupérer la notification de message LVN_ITEMCHANGED sur la ListCtrl :
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | BEGIN_MESSAGE_MAP(CTestMdiView, CFormView) //{{AFX_MSG_MAP(CTestMdiView) ON_NOTIFY(LVN_ITEMCHANGED, IDC_LIST1, OnItemchangedList) END_MESSAGE_MAP() void CTestMdiView::OnItemchangedList(NMHDR* pNMHDR, LRESULT* pResult) { NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR; // TODO: Add your control notification handler code here *pResult = 0; if (!pNMListView->uOldState && !pNMListView->uNewState) return; // pas de changement // ancien etat de la check BOOL bPrevState=(BOOL)(((pNMListView->uOldState & LVIS_STATEIMAGEMASK)>>12)-1); if(bPrevState<0) bPrevState = 0; // au depart pas d'ancien etat on met false (unchecked) // nouvel etat. BOOL bChecked =(BOOL)(((pNMListView->uNewState & LVIS_STATEIMAGEMASK)>>12)-1); if(bChecked<0) bChecked = 0; if (bPrevState==bChecked) return; // votre code ici !!!! } |
Il faut définir une classe dérivée de CListCtrl, intercepter le message WM_NOTIFY et procéder au test suivant :
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | CMyListCtrl::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT *pResult) { HD_NOTIFY *pHDN = reinterpret_cast<HD_NOTIFY*> (lParam); if ( ( (pHDN->hdr.code == HDN_BEGINTRACKA) || (pHDN->hdr.code == HDN_BEGINTRACKW) || (pHDN->hdr.code == HDN_DIVIDERDBLCLICKA) || (pHDN->hdr.code == HDN_DIVIDERDBLCLICKW) ) && (pHDN->iItem == 2) // Redimensionnement interdit sur la colonne 2. ) { *pResult = TRUE; return TRUE; } return CListCtrl::OnNotify(wParam, lParam, pResult); } |
Par défaut la sélection sur une CListCtrl se fait uniquement sur la première colonne.
Pour que la sélection soit active sur toute la ligne procéder comme suit :
Code c++ : | Sélectionner tout |
1 2 | m_ListCtrl.SetExtendedStyle(m_ListCtrl.GetExtendedStyle() | LVS_EX_FULLROWSELECT); |
Changement de la couleur de fond :
Code c++ : | Sélectionner tout |
1 2 | m_MyListControl.SetBkColor(RGB(202,202,255)); |
Changement de la couleur d'écriture :
Code c++ : | Sélectionner tout |
1 2 | m_MyListControl.SetTextColor(RGB(120,120,120)); |
Changement de la couleur de fond du texte :
Code c++ : | Sélectionner tout |
1 2 | m_MyListControl.SetTextBkColor(RGB(202,202,255)); |
Il faudra passer par une classe héritée du contrôle de base CListCtrl et redéfinir la fonction OnDrawItem pour le dessin des lignes.
Exemple d'implémentation:
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | #if !defined(AFX_LISTCTRLEX_H__BB73C7E9_F905_4D3A_A0B7_AB9515DA9DA1__INCLUDED_) #define AFX_LISTCTRLEX_H__BB73C7E9_F905_4D3A_A0B7_AB9515DA9DA1__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 // ListCtrlEx.h : header file // ///////////////////////////////////////////////////////////////////////////// // CListCtrlEx window class CListCtrlEx : public CListCtrl { // Construction public: CListCtrlEx(); // Attributes private: COLORREF m_cBkColor[3]; COLORREF m_cTxtColor[3]; // Operations public: // Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CListCtrlEx) //}}AFX_VIRTUAL virtual voidDrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct); // couleur ligne Pair/impair void SetLineColor(COLORREF Bkclr,COLORREF TxtColor,bool bLinePair=true) { m_cBkColor[!bLinePair]=Bkclr; m_cTxtColor[!bLinePair]=TxtColor; } // couleur bandeau de selection. void SetSelColor(COLORREF Bkclr,COLORREF TxtColor) { m_cBkColor[2]=Bkclr; m_cTxtColor[2]=TxtColor; } // Implementation public: virtual ~CListCtrlEx(); // Generated message map functions protected: //{{AFX_MSG(CListCtrlEx) //}}AFX_MSG DECLARE_MESSAGE_MAP() }; ///////////////////////////////////////////////////////////////////////////// //{{AFX_INSERT_LOCATION}} // Microsoft Visual C++ will insert additional declarations immediately before the previous line. #endif // !defined(AFX_LISTCTRLEX_H__BB73C7E9_F905_4D3A_A0B7_AB9515DA9DA1__INCLUDED_) |
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif ///////////////////////////////////////////////////////////////////////////// // CListCtrlEx CListCtrlEx::CListCtrlEx() { m_cBkColor[0]=m_cBkColor[1]=RGB(255, 255, 255); m_cTxtColor[0]=m_cTxtColor[1]=RGB(0, 0, 0); m_cBkColor[2]=RGB(0 ,0 ,255); m_cTxtColor[2]=RGB(255,255,0); } CListCtrlEx::~CListCtrlEx() { } BEGIN_MESSAGE_MAP(CListCtrlEx, CListCtrl) //{{AFX_MSG_MAP(CListCtrlEx) //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CListCtrlEx message handlers void CListCtrlEx::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) { // TODO: Add your message handler code here and/or call default if(lpDrawItemStruct == NULL) return; CDC *pDC = CDC::FromHandle(lpDrawItemStruct->hDC); int nSavedDC = pDC->SaveDC(); RECT r; CRect rItem( lpDrawItemStruct->rcItem ); CBrush* pBrush; r.left = lpDrawItemStruct->rcItem.left; r.right = lpDrawItemStruct->rcItem.right; r.top = lpDrawItemStruct->rcItem.top; r.bottom = lpDrawItemStruct->rcItem.bottom; CRect rect(r); int item = lpDrawItemStruct->itemID; BOOL sel = ( (/*(lpDrawItemStruct->itemAction & ODA_FOCUS) &&*/ (lpDrawItemStruct->itemState & ODS_FOCUS)) || ( (lpDrawItemStruct->itemState & ODS_SELECTED)) ); if( sel ) { CBrush brush( m_cBkColor[2]); // bleue pBrush = pDC->SelectObject( &brush ); pDC->FillRect(&rect, &brush); pDC->SetTextColor(m_cTxtColor[2]); // jaune pDC->SetBkColor(m_cBkColor[2] ); // bleue pDC->SelectObject( pBrush ); } else { //?RGB(0 ,255,0):RGB(128,128,0) CBrush brush(m_cBkColor[(item %2)!=0]); pBrush = pDC->SelectObject( &brush ); pDC->FillRect(&rect, &brush); // RGB(0 ,0 ,0) pDC->SetTextColor(m_cTxtColor[(item %2)!=0]); pDC->SetBkColor( m_cBkColor[(item %2)!=0]); pDC->SelectObject( pBrush ); } CString s; int nNoColumn, WidthColumn; char *p=0; int nNbColumns = GetHeaderCtrl()->GetItemCount(); int ncx=0; LV_COLUMN lvc; char szItem[255]; LVITEM LvItem; CImageList *pImgList=GetImageList(LVSIL_NORMAL); if(!pImgList)pImgList=GetImageList(LVSIL_SMALL); bool bImage=(pImgList && pImgList->m_hImageList!=NULL); IMAGEINFO info; CRect SizeImg(0,0,0,0); if(bImage) { pImgList->GetImageInfo(0,&info); SizeImg=CRect(info.rcImage); } for( nNoColumn = 0; nNoColumn < nNbColumns ; nNoColumn++ ) { WidthColumn = GetColumnWidth( nNoColumn ); lvc.mask = LVCF_FMT; if( GetColumn( nNoColumn, &lvc ) ==0 ) continue; LvItem.mask=LVIF_TEXT |LVIF_IMAGE; LvItem.cchTextMax=sizeof(szItem); LvItem.pszText=szItem; LvItem.iItem=item; LvItem.iSubItem=nNoColumn; LvItem.iImage=0; GetItem( &LvItem ); s=LvItem.pszText; CRect rZone=r; if(bImage && LvItem.iImage!=-1) { rZone.left+=SizeImg.Width(); pImgList->Draw(pDC,LvItem.iImage,CPoint(r.left,r.top),ILD_NORMAL); } if( lvc.fmt & LVCFMT_RIGHT ) { pDC->SetTextAlign( TA_RIGHT ); s += ' '; pDC->ExtTextOut( r.left + WidthColumn , r.top, ETO_OPAQUE|ETO_CLIPPED, &rZone, s, NULL ); } else { pDC->SetTextAlign( TA_LEFT ); s = ' ' + s; pDC->ExtTextOut( rZone.left ,r.top, ETO_OPAQUE|ETO_CLIPPED, &rZone, s, NULL ); } r.left += WidthColumn; } pDC->RestoreDC( nSavedDC ); if( lpDrawItemStruct->itemState & ODS_SELECTED ) pDC->DrawFocusRect( &lpDrawItemStruct->rcItem ); } |
Note:Le contrôle devra avoir l'option owner draw fixed cochée dans les ressources.
En mode report une têtière apparaît en haut du contrôle avec le nom des colonnes.
Ce composant de la classe CHeaderCtrlne permet pas de personnalisation d'apparence.
Dans ce contexte comme faire pour le personnaliser ? , Hé bien il va falloir le subclasser et prendre en charge son dessin.
La première étape sera donc de fournir un composant dérivé de la classe CHeaderCtrl :
la définition:
Code C++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | ///////////////////////////////////////////////////////////////////////////// // CHeaderCtrlEx window class CHeaderCtrlEx : public CHeaderCtrl { // Construction public: CHeaderCtrlEx(); // Attributes public: int m_iIndex, m_iHotIndex ; COLORREF m_Bkclr,m_BkclrInsSel; COLORREF m_Txtclr,m_TxtclrInsSel; CFont *m_pFont; // Operations public: // Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CHeaderCtrlEx) //}}AFX_VIRTUAL // Implementation public: virtual ~CHeaderCtrlEx(); voidDrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct); void SetBackGround(COLORREF clr,COLORREF clrInSel); void SetTextColor (COLORREF clr,COLORREF clrInSel); void SetHeaderFont(CFont &rFont); // Generated message map functions protected: //{{AFX_MSG(CHeaderCtrlEx) afx_msg void OnPaint(); afx_msg void OnLButtonDown(UINT nFlags, CPoint point); afx_msg UINT OnNcHitTest(CPoint point); afx_msg void OnLButtonUp(UINT nFlags, CPoint point); //}}AFX_MSG DECLARE_MESSAGE_MAP() }; |
Le code:
Code C++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 | ///////////////////////////////////////////////////////////////////////////// // CHeaderCtrlEx CHeaderCtrlEx::CHeaderCtrlEx() { m_iIndex=-1; m_pFont=NULL; m_Bkclr=::GetSysColor(COLOR_BTNFACE); m_BkclrInsSel=RGB(0,0,0); m_Txtclr=RGB(0,0,0); m_TxtclrInsSel=RGB(255,255,255); } //------------------------------------------------------ CHeaderCtrlEx::~CHeaderCtrlEx() { } //------------------------------------------------------ void CHeaderCtrlEx::SetBackGround(COLORREF clr,COLORREF clrInSel) { // m_Bkclr=clr; m_BkclrInsSel=clrInSel; if(m_hWnd) Invalidate(); } //------------------------------------------------------ void CHeaderCtrlEx::SetTextColor(COLORREF clr,COLORREF clrInSel) { // m_Txtclr=clr; m_TxtclrInsSel=clrInSel; if(m_hWnd) Invalidate(); } //------------------------------------------------------ void CHeaderCtrlEx::SetHeaderFont(CFont &rFont) { // m_pFont=&rFont; if(m_hWnd) Invalidate(); } BEGIN_MESSAGE_MAP(CHeaderCtrlEx, CHeaderCtrl) //{{AFX_MSG_MAP(CHeaderCtrlEx) ON_WM_PAINT() ON_WM_LBUTTONDOWN() ON_WM_NCHITTEST() ON_WM_LBUTTONUP() //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CHeaderCtrlEx message handlers void CHeaderCtrlEx::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) { } //-------------------------------------------------------------- void CHeaderCtrlEx::OnPaint() { CPaintDC dc(this); // device context for painting // TODO: Add your message handler code here // Do not call CHeaderCtrl::OnPaint() for painting messages int nItems = GetItemCount(); CRect rect,rectItem; CBrush brBackGrnd(m_Bkclr); CPen PenWith(PS_SOLID ,1,RGB(255,255,255)); CBrush brBackGrndSel(m_BkclrInsSel); CPen PenBackGrnd(PS_SOLID ,1,m_Bkclr); for(int i = 0; i <nItems; i++) { TCHAR buf1[256]; HD_ITEM hditem1; hditem1.mask = HDI_TEXT | HDI_FORMAT | HDI_ORDER; hditem1.pszText = buf1; hditem1.cchTextMax = 255; GetItem( i, &hditem1 ); GetItemRect(i, &rect); CPen PenBorder(PS_SOLID ,1,RGB(172,168,153)); bool bPush=(m_iIndex>=0 && m_iIndex==i); CRect rFond=rect; rFond.top+=2; rFond.bottom-=1; rFond.left+=1; rFond.right-=1; dc.FillRect(&rFond,(bPush?&brBackGrndSel:&brBackGrnd)); CPen* pOldPen=dc.SelectObject(&PenBackGrnd); dc.MoveTo(rect.right-1,rect.top+2); dc.LineTo(rect.right-1,rect.bottom-1); dc.MoveTo(rect.left,rect.top+2); dc.LineTo(rect.left,rect.bottom-1); dc.SelectObject(pOldPen); pOldPen=dc.SelectObject(&PenBorder); dc.MoveTo(rect.left,rect.top); dc.LineTo(rect.right,rect.top); dc.MoveTo(rect.left,rect.bottom); dc.LineTo(rect.right,rect.bottom); dc.MoveTo(rect.right-1,rect.top+3); dc.LineTo(rect.right-1,rect.bottom-2); dc.SelectObject(pOldPen); pOldPen=dc.SelectObject(&PenWith); dc.MoveTo(rect.left,rect.top+1); dc.LineTo(rect.right,rect.top+1); dc.MoveTo(rect.left,rect.top+3); dc.LineTo(rect.left,rect.bottom-2); dc.SelectObject(pOldPen); /* UINT utype=DFCS_BUTTONPUSH; if(m_iIndex>=0 && m_iIndex==i)utype|=DFCS_PUSHED; dc.DrawFrameControl(rect,DFC_BUTTON,utype); */ DRAWITEMSTRUCTDrawItemStruct; GetItemRect(i, &rectItem); DrawItemStruct.CtlType= 100; DrawItemStruct.hDC= dc.GetSafeHdc(); DrawItemStruct.itemAction= ODA_DRAWENTIRE; DrawItemStruct.hwndItem = GetSafeHwnd(); DrawItemStruct.rcItem= rectItem; DrawItemStruct.itemID= i; DrawItem(&DrawItemStruct); UINT uFormat = DT_SINGLELINE | DT_NOPREFIX | DT_TOP |DT_CENTER | DT_END_ELLIPSIS ; CFont font,*pCurrentFont; pCurrentFont=&font; if(m_pFont) pCurrentFont=m_pFont; else { LOGFONT lf; memset(&lf, 0, sizeof(LOGFONT)); lf.lfHeight = 13; strcpy(lf.lfFaceName, "Sevenet 7"); font.CreateFontIndirect(&lf); } CFont* pOldFont = dc.SelectObject(pCurrentFont); dc.SetBkMode(TRANSPARENT); rectItem.DeflateRect(2,2,2,2); dc.SetTextColor(bPush?m_TxtclrInsSel:m_Txtclr); TCHAR buf[256]; HD_ITEM hditem; hditem.mask = HDI_TEXT | HDI_FORMAT | HDI_ORDER; hditem.pszText = buf; hditem.cchTextMax = 255; GetItem( DrawItemStruct.itemID, &hditem ); dc.DrawText(buf, &rectItem, uFormat); dc.SelectObject(pOldFont); } } //-------------------------------------------------------------- void CHeaderCtrlEx::OnLButtonDown(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default UINT m_nClickFlags = nFlags; CPoint m_ptClickPoint = point; if(m_iIndex==-1 && m_iHotIndex>=0) { CRect rect; m_iIndex=m_iHotIndex; GetItemRect(m_iIndex, &rect); InvalidateRect(rect); } CHeaderCtrl::OnLButtonDown(nFlags, point); } //-------------------------------------------------------------- UINT CHeaderCtrlEx::OnNcHitTest(CPoint point) { // TODO: Add your message handler code here and/or call default HDHITTESTINFO hdhtiHotItem; hdhtiHotItem.pt = point; ScreenToClient(&hdhtiHotItem.pt); m_iHotIndex = SendMessage(HDM_HITTEST, 0, (LPARAM)(&hdhtiHotItem)); if(m_iHotIndex >= 0) { HDITEM hditem; hditem.mask = HDI_ORDER; VERIFY(GetItem(m_iHotIndex, &hditem)); } return CHeaderCtrl::OnNcHitTest(point); } //-------------------------------------------------------------- void CHeaderCtrlEx::OnLButtonUp(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default if(m_iIndex >=0) { CRect rect; GetItemRect(m_iIndex, &rect); m_iIndex =-1; InvalidateRect(rect); } CHeaderCtrl::OnLButtonUp(nFlags, point); } |
Dans la fonction je procède au dessin de toutes les colonnes avec une fonte spécifique et une couleur d'écriture bleue.
La simulation du clic sur la colonne est gérée par les fonctions :
OnLButtonUp,OnNcHitTest,OnLButtonDown..
Il faut maintenant indiquer à la CListView ou au CListCtrl de travailler avec ce nouveau composant en subclassant le précédent.
Pour une CListView :
Code C++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 | void CCustomListView::OnInitialUpdate() { CListView::OnInitialUpdate(); // TODO: You may populate your ListView with items by directly accessing // its list control through a call to GetListCtrl(). CListCtrl& theCtrl = GetListCtrl(); // m_HeaderCtrl va prendre le relai sur le composant par defaut. m_HeaderCtrl.SubclassWindow(theCtrl.GetHeaderCtrl()->m_hWnd); //… |
Pour un CListCtrl :
Code C++ : | Sélectionner tout |
1 2 3 4 5 6 | void CCustomListCtrl::PreSubclassWindow() { // TODO: Add your specialized code here and/or call the base class m_HeaderCtrl.SubclassWindow(GetHeaderCtrl()->m_hWnd); CListCtrl::PreSubclassWindow(); } |
Voila le mécanisme de base est en place pour implémenter d'autres effets ou fonctions pour faciliter la personnalisation du composant.
Le projet de test : CustomListview.zip
Pour pouvoir afficher des images sur toutes les colonnes d'une ClistCtrl il faudra modifier son style .
Comment afficher des icônes dans les subitems en mode "report" dans un CListCtrl
Pour ne pas afficher d'image sur la première colonne on procédera comme suit :
On indiquera -1 pour l'indice de l'image.
Code C++ : | Sélectionner tout |
m_listctrl.InsertItem(LVIF_TEXT|LVIF_STATE|LVIF_IMAGE, i, strText,0, 0,-1, 0);
Inconvénient la ClistCtrlne tient pas compte de la non présence de l'image et réserve une place vide en lieu et place.
Pour remédier à ce problème il faudra faire sa propre classe dérivée et gérer le cas sur le dessin de la ligne dans la fonction OnDrawItem.
Ce sujet est traité dans la classe proposée dans Comment personnaliser l'affichage d'une CListCtrl ?
Pour récupérer les informations sur la ligne et la colonne sélectionnée pour les messages suivants :
NM_DBLCLK
NM_CLICK
NM_RCLICK
NM_RDBLCLK
On procédera comme suit :
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 | void CSdiSplitterView::OnDblclk(NMHDR* pNMHDR, LRESULT* pResult) { // TODO: Add your control notification handler code here NMITEMACTIVATE*pActive=(NMITEMACTIVATE *)pNMHDR; TRACE("item:%d subitem:%d",pActive->iItem,pActive->iSubItem); *pResult = 0; } |
Ca se fait en deux étapes :
1 - Il faut d'abord associer une liste d'images à votre contrôle :
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 | m_ImageListSmall = new CImageList(); if ( m_ImageListSmall->Create(16,16,ILC_COLOR32|ILC_MASK,3,3) ) { m_ImageListSmall->Add(pApp->LoadIcon(IDI_NOK)); m_ImageListSmall->Add(pApp->LoadIcon(IDI_OK)); m_ImageListSmall->Add(pApp->LoadIcon(IDI_UNDEFINED)); m_list.SetImageList(m_ImageListSmall,LVSIL_SMALL); } |
Code c++ : | Sélectionner tout |
1 2 3 | // Set the extended style m_list.SetExtendedStyle(LVS_EX_SUBITEMIMAGES); |
2 - Maintenant que le contrôle est prêt, on peut lui demander d'afficher une icône sur les subitems :
Code c++ : | Sélectionner tout |
1 2 | m_list.SetItem(IndexItem, IndexSubItem, LVIF_IMAGE, 0, IndexIcon, 0,0,0); |
Il suffit d'intercepter le message LVN_KEYDOWNsur la CListCtrl, on obtiendra la fonction suivante :
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 | void CTestSDIView::OnKeydownListCtrl(NMHDR* pNMHDR, LRESULT* pResult) { LV_KEYDOWN* pLVKeyDow = (LV_KEYDOWN*)pNMHDR; // TODO: Add your control notification handler code here TRACE ("\nTouche :%d",pLVKeyDow->wVKey); *pResult = 0; } |
La variable presult permet de notifier si la touche doit être prise en compte par la CListCtrl .
si *presult=0 la ListCtrl traitera normalement le message clavier
si *presult=1 la ListCtrl ignorera le message clavier.
En appelant la fonction SetItemState :
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 | CListCtrl::SetItemState BOOL SetItemState( int nItem, UINT nState, UINT nMask ); |
Code c++ : | Sélectionner tout |
1 2 3 4 | m_listCtrl.SetItemState(iItem, 0, // nState LVIS_SELECTED); // nMask |
Il suffit d'intercepter le message LVN_KEYDOWNsur la CListCtrl, on obtiendra la fonction suivante :
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 | void CTestSDIView::OnKeydownListCtrl(NMHDR* pNMHDR, LRESULT* pResult) { LV_KEYDOWN* pLVKeyDow = (LV_KEYDOWN*)pNMHDR; // TODO: Add your control notification handler code here TRACE ("\nTouche :%d",pLVKeyDow->wVKey); *pResult = 0; } |
Le principe proposé :
On commence par mémoriser les éléments de la ligne en question.
On supprime cette ligne, et on la réinsère à l'offset initial -1 ou +1 suivant le sens de déplacement souhaité.
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | void MoveListCtrlLig(int nIndex,bool bUp) { // nb de colonne de la listCtrl. int nCount = m_MyListCtrl.GetItemCount(); CStringArray strArray; int i=0; for (i=0;i < nCount;i++) { // sauvegarde des éléments dans une CStringArray strArray.Add(m_MyListCtrl.GetItemText(nIndex,i)); } // suppression de la ligne m_MyListCtrl.DeleteItem(nIndex); int nNewIndex=nIndex+(bUp?-1:1); // insertion au nouvel emplacement. m_MyListCtrl.Insertitem(LVIF_TEXT,nNewIndex , strArray[0], 0, 0,0, 0); // insertion des colonnes suivantes for (i=1;i <nCount;i++) { m_MyListCtrl->SetItemText(nNewIndex,i, strArray[i]); } } |
Pour récupérer la première ligne sélectionnée on procédera comme suit :
Code c++ : | Sélectionner tout |
1 2 3 4 5 | int nItem=-1; POSITION pos = m_MyListCtrl.GetFirstSelectedItemPosition(); if (pos! NULL) nItem= m_MyListCtrl.GetNextSelectedItem(pos); |
Note: pour les autres lignes il suffira de poursuive les appels à la fonction GetNextSelectedItem
L'exemple typique sera l'adaptation d'un contrôle CListCtrl à la taille de la fenêtre principale, les colonnes devant aussi s'adapter à l'expansion.
Voici comment procéder:
Pour le dimensionnement du contrôle on considèrera qu'il représente x pourcent de la fenêtre.
Pour gérer le dimensionnement dynamique :
Au début du dimensionnement de la fenêtre le message WM_ENTERSIZEMOVE avec wparam==SC_SIZE est envoyé, Il faut alors calculer la taille relative du contrôle dans la fenêtre.
Au niveau du contrôle il faudra mémoriser la taille relative des colonnes par rapport à la CListCtrl.
Ajuster la taille du contrôle :
Deux solutions :
- On peut effectuer le traitement dynamiquement avec le message WM_SIZE.
- Le traitement peut être fait en fin de dimensionnement en interceptant le message WM_EXITSIZEMOVE avec wparam==SC_SIZE.
Dans les deux cas il restera à appliquer les ratios calculés sur le contrôle par rapport à la fenêtre, puis on agira de même pour les colonnes de la CListCtrl.
Note : ces messages devront être implémentés manuellement, ils n'apparaissent pas dans classwizard
Exemple de traitement :
Code C++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | ///////////////////////////////////////////////////////////////////////////// LRESULT CResizeCListCtrlView::OnEnterSizeMove (WPARAM wParam, LPARAM lParam) { // les dimensions sont sauvegardées la premiere fois. if(!m_RectInitialWindow.Width()) { // la taille de la fenetre. GetClientRect(m_RectInitialWindow); // la taill du controle m_ListCtrl.GetClientRect(m_RectInitialCtrl); ScreenToClient(m_RectInitialCtrl); // les colonnes du controle HDITEM hdi; hdi.mask = HDI_WIDTH; for(int i=0;i<m_ListCtrl.GetHeaderCtrl()->GetItemCount();i++) { m_ListCtrl.GetHeaderCtrl()->GetItem(i,&hdi); m_arWithCol.Add(hdi.cxy); } } return (LRESULT)0; } ///////////////////////////////////////////////////////////////////////////// LRESULT CResizeCListCtrlView::OnExitSizeMove (WPARAM wParam, LPARAM lParam) { double dRatiox,dRatioy; CRect RectCtrl,RectWindow; // calcul des ratios pour le controle dRatiox=static_cast<double>(m_RectInitialWindow.Width())/static_cast<double>(m_RectInitialCtrl.Width()); dRatioy=static_cast<double>(m_RectInitialWindow.Height())/static_cast<double>(m_RectInitialCtrl.Height()); // ratios sur les colonnes. HDITEM hdi; hdi.mask = HDI_WIDTH; CArray<double,double> arWitdthCol; for(int i=0;i<m_arWithCol.GetSize();i++) arWitdthCol.Add(static_cast<double>(m_RectInitialCtrl.Width())/static_cast<double>(m_arWithCol[i])); // apres changement de taille. double nx,ny; GetClientRect(RectWindow); nx=static_cast<double>(RectWindow.Width())/dRatiox; ny=static_cast<double>(RectWindow.Height())/dRatioy; RectCtrl=m_RectInitialCtrl; RectCtrl.right=RectCtrl.left+static_cast<int>(nx); RectCtrl.bottom=RectCtrl.top+static_cast<int>(ny); m_ListCtrl.SetWindowPos(NULL,0,0,RectCtrl.Width(),RectCtrl.Height(),SWP_NOMOVE | SWP_NOZORDER); // uniquement la taille // dimensionnement des colonnes. for(i=0;i<m_ListCtrl.GetHeaderCtrl()->GetItemCount();i++) { nx=static_cast<double>(RectCtrl.Width())/arWitdthCol[i]; hdi.cxy=static_cast<int>(nx); m_ListCtrl.GetHeaderCtrl()->SetItem(i,&hdi); } return (LRESULT)0; } |
L'exemple en action
Proposer une nouvelle réponse sur la FAQ
Ce n'est pas l'endroit pour poser des questions, allez plutôt sur le forum de la rubrique pour çaLes sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2024 Developpez Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.