FAQ VC++ et MFCConsultez toutes les FAQ

Nombre d'auteurs : 20, nombre de questions : 546, 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


SommaireContrôlesCListCtrl (24)
précédent sommaire suivant
 

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
38
  
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 :

Mis à jour le 20 mai 2006 farscape

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));
La fonction GetItem charge la structure HDITEM en fonction du contenu du mask.
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));

Mis à jour le 20 mai 2006 farscape

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

Mis à jour le 5 avril 2013 farscape

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

Mis à jour le 12 février 2006 farscape

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

Mis à jour le 5 avril 2013 farscape

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() ;

Mis à jour le 5 avril 2013 farscape

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

Mis à jour le 5 avril 2013 farscape

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);  */ 
}

Mis à jour le 5 avril 2013 farscape

Code c++ : Sélectionner tout
1
2
3
4
5
  
BOOL GetLstCtrlCheck (CListCtrl &rListCtrl,WPARAM nItemIndex) 
{     
    return (ListView_GetCheckState(rListCtrl. GetSafeHwnd( ), nItemIndex));  
}

Mis à jour le 5 avril 2013 farscape

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 !!!! 
}
voir MSDN pour le shift right qui permet de récupérer la valeur de l'état de l'item.

Mis à jour le 5 avril 2013 farscape

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

Mis à jour le 7 juillet 2008 farscape

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

Mis à jour le 5 avril 2013 farscape

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

Mis à jour le 5 avril 2013 farscape

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_)
le .cpp

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 ); 
}
Cette classe permet de paramétrer la couleur des lignes pairs/impairs ainsi que la couleur du bandeau de sélection.

Note:Le contrôle devra avoir l'option owner draw fixed cochée dans les ressources.

Mis à jour le 20 mai 2006 farscape

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
47
  
///////////////////////////////////////////////////////////////////////////// 
// 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
229
  
///////////////////////////////////////////////////////////////////////////// 
// 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
12
  
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); 
    //&#8230;
Pour un CListCtrl :

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
  
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 :

Mis à jour le 20 mai 2006 farscape

Pour pouvoir afficher des images sur toutes les colonnes d'une ClistCtrl il faudra modifier son style .

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
1
2
  
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

Mis à jour le 20 mai 2006 farscape

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

Mis à jour le 4 avril 2005 farscape

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); 
         }
Ensuite, il faut mettre le style étendu de la liste dans l'init de votre contrôle.

Code c++ : Sélectionner tout
1
2
3
  
// Set the extended style       
m_list.SetExtendedStyle(LVS_EX_SUBITEMIMAGES);
Ensuite, mettre tout ça dans le OnInitDialog() de la classe qui contient le CListCtrl.

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);
Ceci va afficher l'icône d'index IndexIcon dans la liste, sur l'item d'index IndexItem, sur le subitem d'index IndexSubItem.

Mis à jour le 19 septembre 2005 tut

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 code de la touche est stocké dans la variable wVkey de la structure LV_KEYDOWN.
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.

Mis à jour le 20 mai 2006 farscape

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 );
Exemple:

Code c++ : Sélectionner tout
1
2
3
4
  
m_listCtrl.SetItemState(iItem,  
               0,              // nState  
               LVIS_SELECTED); // nMask
iItem représente l'index de la ligne dans la CListCtrl.

Mis à jour le 20 mai 2006 farscape

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 code de la touche est stocké dans la variable wVkey de la structure LV_KEYDOWN.

Mis à jour le 22 janvier 2007 farscape

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

Mis à jour le 22 janvier 2007 farscape

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

Mis à jour le 22 janvier 2007 farscape

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
66
  
///////////////////////////////////////////////////////////////////////////// 
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

Mis à jour le 17 septembre 2007 farscape

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 ça


Réponse à la question

Liens sous la question
précédent sommaire suivant
 

Les 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 © 2017 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.

 
Contacter le responsable de la rubrique C++