| auteur : Farscape | 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.
void CSdiSplitterView:: OnInitialUpdate ()
{
CListView:: OnInitialUpdate ();
CListCtrl& theCtrl = GetListCtrl ();
CString strText;
for (int nc= 0 ;nc< 5 ;nc+ + )
{
strText.Format (TEXT (" ColHeader%d " ),nc);
theCtrl.InsertColumn (nc,strText,LVCFMT_LEFT,70 );
}
theCtrl.ModifyStyle (0 ,LVS_REPORT);
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);
theCtrl.InsertItem (LVIF_TEXT| LVIF_STATE, i, strText,(i% 2 )= = 0 ?LVIS_SELECTED : 0 , LVIS_SELECTED,0 , 0 );
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 : http://farscape.developpez.com/Samples/SdiSplitter.zip
|
| auteur : 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 :
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 :
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));
|
|
| auteur : Farscape | En interceptant avec ClassWizard l'événement LVN_ITEMCHANGED ,complété du test qui suit dans l'exemple.
BEGIN_MESSAGE_MAP (CTestMDIView, CFormView)
ON_NOTIFY (LVN_ITEMCHANGED, IDC_LISTCTRL, OnItemchangedListctrl)
END_MESSAGE_MAP ()
void CTestMdiView:: OnItemchangedList1 (NMHDR* pNMHDR, LRESULT* pResult)
{
NM_LISTVIEW* pNMListView = (NM_LISTVIEW* )pNMHDR;
if (pNMListView- > iItem>= 0 & & (pNMListView- > uNewState & LVIS_SELECTED))
{
TRACE (" \nItem:%d:%s " ,pNMListView- > iItem,
(const char * )m_ListCtrl.GetItemText (pNMListView- > iItem,0 ));
}
* pResult = 0 ;
}
|
|
| auteur : Farscape | Exemple de sélection et de scroll éventuel sur la dernière ligne d'une CListCtrl:
int nCount = pMyListCtrl- > GetItemCount ();
if (nCount > 0 )
{
pMyListCtrl- > SetItemState (nCount- 1 , LVIS_SELECTED, LVIS_SELECTED);
pMyListCtrl- > EnsureVisible (nCount- 1 , FALSE);
}
|
|
| auteur : 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.
BEGIN_MESSAGE_MAP (CTestMdiView, CFormView)
ON_NOTIFY (NM_CLICK, IDC_LIST, OnClickList)
END_MESSAGE_MAP ()
void CTestMdiView:: OnClickList (NMHDR* pNMHDR, LRESULT* pResult)
{
NMITEMACTIVATE * pItem= (NMITEMACTIVATE * )pNMHDR;
TRACE (" \n iItem:%d iSubItem:%d " ,pItem- > iItem,pItem- > iSubItem);
* pResult = 0 ;
}
|
|
| auteur : Farscape | Il suffit de spécifier le style LVS_EX_CHECKBOXES
m_ListCtrl.SetExtendedStyle (m_ListCtrl.GetExtendedStyle () | LVS_EX_FULLROWSELECT | LVS_EX_CHECKBOXES);
|
|
| auteur : Farscape |
void SetLstCtrlCheck (CListCtrl & rListCtrl,WPARAM nItemIndex, BOOL bCheck)
{
ListView_SetCheckState (rListCtrl. GetSafeHwnd ( ), nItemIndex, bCheck);
}
|
|
| auteur : Farscape |
BOOL GetLstCtrlCheck (CListCtrl & rListCtrl,WPARAM nItemIndex)
{
return (ListView_GetCheckState (rListCtrl. GetSafeHwnd ( ), nItemIndex));
}
|
|
| auteur : Farscape | Il faut récupérer la notification de message LVN_ITEMCHANGED sur la ListCtrl :
BEGIN_MESSAGE_MAP (CTestMdiView, CFormView)
ON_NOTIFY (LVN_ITEMCHANGED, IDC_LIST1, OnItemchangedList)
END_MESSAGE_MAP ()
void CTestMdiView:: OnItemchangedList (NMHDR* pNMHDR, LRESULT* pResult)
{
NM_LISTVIEW* pNMListView = (NM_LISTVIEW* )pNMHDR;
* pResult = 0 ;
if (! pNMListView- > uOldState & & ! pNMListView- > uNewState) return ;
BOOL bPrevState= (BOOL)(((pNMListView- > uOldState & LVIS_STATEIMAGEMASK)> > 12 )- 1 );
if (bPrevState< 0 ) bPrevState = 0 ;
BOOL bChecked = (BOOL)(((pNMListView- > uNewState & LVIS_STATEIMAGEMASK)> > 12 )- 1 );
if (bChecked< 0 ) bChecked = 0 ;
if (bPrevState= = bChecked) return ;
}
|
voir MSDN pour le shift right qui permet de récupérer la valeur de l'état de l'item.
|
| auteur : Farscape | Il faut définir une classe dérivée de CListCtrl, intercepter le message WM_NOTIFY et procéder au test suivant :
|
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 )
)
{
* pResult = TRUE;
return TRUE;
}
return CListCtrl:: OnNotify (wParam, lParam, pResult);
}
|
|
| auteur : 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 :
m_ListCtrl.SetExtendedStyle (m_ListCtrl.GetExtendedStyle () | LVS_EX_FULLROWSELECT);
|
|
| auteur : Farscape | Changement de la couleur de fond :
m_MyListControl.SetBkColor (RGB (202 ,202 ,255 ));
|
Changement de la couleur d'écriture :
m_MyListControl.SetTextColor (RGB (120 ,120 ,120 ));
|
Changement de la couleur de fond du texte :
m_MyListControl.SetTextBkColor (RGB (202 ,202 ,255 ));
|
|
| auteur : 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:
# 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
class CListCtrlEx : public CListCtrl
{
public :
CListCtrlEx ();
private :
COLORREF m_cBkColor[3 ];
COLORREF m_cTxtColor[3 ];
public :
virtual voidDrawItem (LPDRAWITEMSTRUCT lpDrawItemStruct);
void SetLineColor (COLORREF Bkclr,COLORREF TxtColor,bool bLinePair= true )
{
m_cBkColor[! bLinePair]= Bkclr;
m_cTxtColor[! bLinePair]= TxtColor;
}
void SetSelColor (COLORREF Bkclr,COLORREF TxtColor)
{
m_cBkColor[2 ]= Bkclr;
m_cTxtColor[2 ]= TxtColor;
}
public :
virtual ~ CListCtrlEx ();
protected :
DECLARE_MESSAGE_MAP ()
} ;
# endif / / ! defined ( AFX_LISTCTRLEX_H__BB73C7E9_F905_4D3A_A0B7_AB9515DA9DA1__INCLUDED_ )
|
le .cpp
# ifdef _DEBUG
# define new DEBUG_NEW
# undef THIS_FILE
static char THIS_FILE[] = __FILE__;
# endif
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)
END_MESSAGE_MAP ()
void CListCtrlEx:: DrawItem (LPDRAWITEMSTRUCT lpDrawItemStruct)
{
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- > itemState & ODS_FOCUS)) | |
( (lpDrawItemStruct- > itemState & ODS_SELECTED))
);
if ( sel )
{
CBrush brush ( m_cBkColor[2 ]);
pBrush = pDC- > SelectObject ( & brush );
pDC- > FillRect (& rect, & brush);
pDC- > SetTextColor (m_cTxtColor[2 ]);
pDC- > SetBkColor (m_cBkColor[2 ] );
pDC- > SelectObject ( pBrush );
}
else
{
CBrush brush (m_cBkColor[(item % 2 )! = 0 ]);
pBrush = pDC- > SelectObject ( & brush );
pDC- > FillRect (& rect, & brush);
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.
|
| auteur : Farscape |
m_listctrl.InsertItem (LVIF_TEXT| LVIF_STATE| LVIF_IMAGE, i, strText,0 , 0 ,- 1 , 0 );
|
Inconvénient la ClistCtrl ne 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 ?
|
| auteur : 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 :
void CSdiSplitterView:: OnDblclk (NMHDR* pNMHDR, LRESULT* pResult)
{
NMITEMACTIVATE* pActive= (NMITEMACTIVATE * )pNMHDR;
TRACE (" item:%d subitem:%d " ,pActive- > iItem,pActive- > iSubItem);
* pResult = 0 ;
}
|
|
| auteur : tut | Ca se fait en deux étapes :
1 - Il faut d'abord associer une liste d'images à votre contrôle :
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.
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 :
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.
|
| auteur : Farscape | Il suffit d'intercepter le message LVN_KEYDOWN sur la CListCtrl, on obtiendra la fonction suivante :
void CTestSDIView:: OnKeydownListCtrl (NMHDR* pNMHDR, LRESULT* pResult)
{
LV_KEYDOWN* pLVKeyDow = (LV_KEYDOWN* )pNMHDR;
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.
|
| auteur : Farscape | En appelant la fonction SetItemState :
CListCtrl:: SetItemState
BOOL SetItemState (
int nItem,
UINT nState,
UINT nMask );
|
Exemple:
m_listCtrl.SetItemState (iItem,
0 ,
LVIS_SELECTED);
|
iItem représente l'index de la ligne dans la CListCtrl.
|
| auteur : Farscape | Il suffit d'intercepter le message LVN_KEYDOWN sur la CListCtrl, on obtiendra la fonction suivante :
void CTestSDIView:: OnKeydownListCtrl (NMHDR* pNMHDR, LRESULT* pResult)
{
LV_KEYDOWN* pLVKeyDow = (LV_KEYDOWN* )pNMHDR;
TRACE (" \nTouche :%d " ,pLVKeyDow- > wVKey);
* pResult = 0 ;
}
|
Le code de la touche est stocké dans la variable wVkey de la structure LV_KEYDOWN.
|
| auteur : 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é.
void MoveListCtrlLig (int nIndex,bool bUp)
{
int nCount = m_MyListCtrl.GetItemCount ();
CStringArray strArray;
int i= 0 ;
for (i= 0 ;i < nCount;i+ + )
{
strArray.Add (m_MyListCtrl.GetItemText (nIndex,i));
}
m_MyListCtrl.DeleteItem (nIndex);
int nNewIndex= nIndex+ (bUp?- 1 :1 );
m_MyListCtrl.Insertitem (LVIF_TEXT,nNewIndex , strArray[0 ], 0 , 0 ,0 , 0 );
for (i= 1 ;i < nCount;i+ + )
{
m_MyListCtrl- > SetItemText (nNewIndex,i, strArray[i]);
}
}
|
|
| auteur : Farscape | Pour récupérer la première ligne sélectionnée on procédera comme suit :
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
|
| auteur : 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 :
LRESULT CResizeCListCtrlView:: OnEnterSizeMove (WPARAM wParam, LPARAM lParam)
{
if (! m_RectInitialWindow.Width ())
{
GetClientRect (m_RectInitialWindow);
m_ListCtrl.GetClientRect (m_RectInitialCtrl);
ScreenToClient (m_RectInitialCtrl);
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;
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 ());
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]));
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);
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 ;
}
|
|
Consultez les autres F.A.Q.
|
|