[MFC 컨트롤] 리스트 컨트롤 색상 다루기
간단하지만 복잡하기도한 리스트 컨트롤 색상 다루기입니다.
칼럼과 행 별로 색상을 컨트롤할 수 있는데요,
일단 코드를 보시면서 말씀드릴게요,
밑의 코드는 제가 프로젝트하던 코드의 일부분입니다 :)
헤더 파일에
afx_msg void OnCustomdrawList(NMHDR* pNMHDR, LRESULT* pResult);
이렇게 추가해 주시구요,
메시지맵을 등록해야 겠죠,
BEGIN_MESSAGE_MAP 밑에 이렇게 입력해줍니다.
ON_NOTIFY(NM_CUSTOMDRAW, IDC_RESULT/*컨트롤 리소스 ID*/, OnCustomdrawList)
그리고 사용자 함수를 추가해서 코딩해 줍니다.
void CNetManagerView::OnCustomdrawTcpList(NMHDR* pNMHDR, LRESULT* pResult)
{
CString strProtocol,strRSTFIN;
BOOL bEmerFlag = FALSE;
NMLVCUSTOMDRAW* pLVCD = (NMLVCUSTOMDRAW*)pNMHDR;
strProtocol = m_ctrlTCPlist.GetItemText(pLVCD->nmcd.dwItemSpec, 1);
strRSTFIN = m_ctrlTCPlist.GetItemText(pLVCD->nmcd.dwItemSpec, 9);
if( (strRSTFIN.Find("RST") != -1) || (strRSTFIN.Find("FIN") != -1) )
{
bEmerFlag=TRUE;
}
*pResult = 0;
if ( CDDS_PREPAINT == pLVCD->nmcd.dwDrawStage )
*pResult = CDRF_NOTIFYITEMDRAW;
else if ( CDDS_ITEMPREPAINT == pLVCD->nmcd.dwDrawStage )
{
if( bEmerFlag ) // TCP && 긴급 플래그 일떄
{
pLVCD->clrText = RGB(255,102,102);
}
pLVCD->clrTextBk = RGB(237,255,255);
if( !bEmerFlag ) // 긴급 플래그가 아닐때
{
pLVCD->clrText = RGB(0, 0, 0);
}
*pResult = CDRF_DODEFAULT;
}
}
프로그램 화면캡쳐의 일부분입니다. 저런식으로 색상을 변경할 수 있습니다.
경우에 따라서 색상을 입히시면 됩니다,
RGB값은 임의로 주시면 되구요, (RGB 색상 표를 참고하세요 )
기본 리스트 컨트롤 배경 색상 및 글자 색상 글자 배경 색상은
API를 제공하고 있습니다.
SetBkColor(), SetTextBkColor() 등을 MSDN을 참고하셔서
사용하시면 됩니다.
[리스트컨트롤-기초05 뽀나스] 리소스 이미지 배경 깔기 & 투명 라벨
리스트 컨트롤 헤더에 인디케이터 집어넣기
리스트 컨트롤을 사용하다가 보면 컬럼을 클릭했을때 보통 정렬 기능을 구현하게 됩니다.
정렬기능을 구현해놓고 보면 내림차순인지 오름차순 정렬인지는 사용자가 리스트안에 있는 데이터를 직접 봐야하는데 이것을 시각적으로 표현하기 위해서 보통
인디케이터를 삽입합니다. 이 기능을 구현하기 위해서 미리 할일은 하나도 없습니다. ^^
리스트 컨트롤의 어떤 속성도 건드릴 필요없이 바로 몇 줄의 코딩으로 가능합니다.
샘플 코드
다음과 같은 함수를 하나 추가하시고요
void ?????::SetIndicatorOnColumn(int Col)
{
CHeaderCtrl* pHeader = ????? (리스트컨트롤 이름).GetHeaderCtrl();
for(int i=0; i<pHeader->GetItemCount(); i++) {
HDITEM hItem;
pHeader->GetItem(i, &hItem);
if(i == Col) {
hItem.mask = HDI_FORMAT | HDI_BITMAP;
hItem.fmt |= HDF_BITMAP | HDF_STRING | HDF_BITMAP_ON_RIGHT;
hItem.hbm = (HBITMAP)LoadImage(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDB_DOWN), IMAGE_BITMAP, 0, 0, LR_LOADMAP3DCOLORS);
pHeader->SetItem(i, &hItem);
}
else {
hItem.mask = HDI_FORMAT ;
hItem.fmt = HDF_STRING;
hItem.hbm = NULL;
pHeader->SetItem(i, &hItem);
}
}
}
OnColumnClick 함수내에 다음과 같은 코드를 추가합니다.
void ????::OnColumnclickList(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
int Col = pNMListView->iSubItem;
this->SetIndicatorOnColumn(Col);
(보통 이곳에는 소트에 관련된 함수를 콜하는 코드가 들어가죠)
....
....
*pResult = 0;
}
다음과 같은 것으로 작업은 완성 (단 비트맵 리소스로 IDB_UP 혹은 IDB_DOWN의 이름으로 화살표를 그려주어야 합니다. 크기는 5*10 정도가 적당한거 같군요.)
참고로 comctl32.dll 6.0버젼부터는 HDF_SORTDOWN, HDF_SORTUP 이라는 플래그가 추가 되었습니다.
리스트 뷰 간격 조절
리스트 컨트롤의 컬럼별로 소트하기
//생성자에서 정/역방향 소팅을 위해
//m_bSort=NULL;
//로 셋팅해주시고
//소트하기위한 정보를 가진 클래스를 하나 만듭니다. 뭐 스트럭쳐로 해도 상관없겠져
//스트럭처로 하시면 public:이부분 없어도 되니..
//생성자와 SORT를 위한 참조 변수도 만들구여
class CSortData{
public:
CSortData(CString str,DWORD dw);
~CSortData(){};
CString m_strText;
DWORD m_dw;
};
CSortData::CSortData(CString str,DWORD dw)
{
m_strText=str;
m_dw=dw;
}
//콜백함수를 정의합니다.
//콜백함수에서 데이터를 비교하고 비교한 값을 리턴합니다.
//lParamSort 인자에는 ASCENDING할것인지 DESCENDING할 것인가하는 정보를 받습니다.
static int CALLBACK MyCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
CSortData *pLc1=(CSortData*)lParam1;
CSortData *pLc2=(CSortData*)lParam2;
int nReturn=pLc1->m_strText.Compare(pLc2->m_strText);
if(lParamSort)
return nReturn;
else
return -nReturn;
}
//리스트 컨트롤 아이템 길이를 구해와서 각각의 아이템에 SetItemData를 이용하여 스트럭처정보를
//넣어줍니다.(나중에 소트하기위해)
//그리고 SortItems함수를 호출하는데 이때 위에서 정의한 콜백함수의 포인터를 인자로 넣어주고
//ASCENDING 혹은 DESCENDING할것인지에 대한 boolean값을 같이 보내어 줍니다.
//그럼 OS가 알아서 콜백함수를 계속 호출해서 비교정보를 받아가겠지여..
//소트가 완료된다음에는
//동적할당한 CSortData 클래스를 메모리에서 삭제해줍니다.
BOOL CTestDlg::SortData(int nCol,BOOL bSort)
{
LkListCtrl *pLc=(LkListCtrl*)GetDlgItem(IDC_LIST_DATA);
int totItems = pLc->GetItemCount();
// Callback Item Setting
for(int i=0; i<totItems; i++)
{
DWORD dw = pLc->GetItemData(i);
CString txt = pLc->GetItemText(i, nCol);
pLc->SetItemData( i, (DWORD) new CSortData(txt,dw) );
}
if(m_bSort)
pLc->SortItems(MyCompare, bSort);
else
{
pLc->SortItems(MyCompare, bSort);
}
// m_bSort=!m_bSort;
for(i=0; i<totItems; i++)
{
CSortData* pItem = (CSortData*)pLc->GetItemData(i);
ASSERT( pItem );
pLc->SetItemData(i, pItem->m_dw);
delete pItem;
}
return TRUE;
}
//컬럼해드를 클릭했을때 우리가 정의한 SortData라는 함수를 호출하도록 클래스 위저드를
//이용하여 통지메세지인 LVN_COLUMNCLICK 를 오버라이딩 합니다. 아니면 직접 코딩하셔도 무방하지여.
//BEGIN_MESSAGE_MAP(CTestDlg,CDialog)
// ON_NOTIFY(LVN_COLUMNCLICK,IDC_LIST_DATA, OnColumnclickListData)
//END_MESSAGE_MAP()
//일케 직접하셔도 되겠져..
//호출시에 몇번째 컬럼인지에 대한 정보와 정방향소트인지, 역방향 소트인지를 결정지어 보내줍니다.
//여기서는 클릭할때마다 바뀌게 해놨습니다.
void CTestDlg:OnColumnclickListData(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
if(!SortData(pNMListView->iSubItem,m_bSort))
return ;
m_bSort=!m_bSort;
*pResult = 0;
}
//자 이렇게 하고 (리스트컨트롤에 자료가 들어있음을 가정하고)
//컴파일해서 컬럼해드를 클릭하여 보면 정/역방향 소팅이 되는것을 눈으로 확인하실수 있으실겁니다.
//약간이라도 도움이 되시길.
//그리고 혹 잘못된 점이 있으면 꼭 알려주세여~
//명절 잘보내시길~~
likehood@hanmail.net
송영배
몇가지 팁
1. 특정 ROW 포커스 주기
2. 특정 ROW로 가기
3. 헤더 컬럼수 얻어오기
4. 컬럼의 너비를 이쁘게 주기
5. 한줄 쭉 선택되게 하기, 그리드 라인 주기
6. 선택한 아이템(ROW)를 지우기
7. 두 아이템을 스왑 하기
8.기존에 선택되어있는 것을 해체하기
1. 특정 ROW 포커스 주기
---------------------------------------------------
리스트 컨트롤에서 어떤 특정 Row를 선택하게 하고 싶을때
( 이때 선택뿐아니라 포커스도 가야 한다.
포커스가 간다는 의미는 다음에 키다운을 하면 바로 부드럽게
다음 아이템을 가리키도록 하는 것이다 )
ListView_SetItemState (pListCtrl->GetSafeHwnd(), // handle to listview
10, // index to listview item
LVIS_FOCUSED | LVIS_SELECTED, // item state
0x000F); //mask
위와 같이 코딩하면 된다.
2. 특정 ROW로 가기
---------------------------------------------------
만일 원하는 Row를 스크롤을 하던 어떻게 하든 화면에 보이게
하고 싶을때
pListCtrl->EnsureVisible(15, TRUE);
이렇게 하면 된다.
3. 헤더 컬럼수 얻어오기
---------------------------------------------------
헤더 컬럼의 갯수를 알고 싶을때
CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
int nColumnCount = pHeader->GetItemCount();
또는 pList->GetHeaderCtrl()을 통해서 얻어올수 도 있다.
4. 컬럼의 너비를 이쁘게 주기
---------------------------------------------------
공백없이 잘 채워 준다.
부모 다이얼로그의 OnSize에서
void CAttrListDlg::OnSize(UINT nType, int cx, int cy)
{
CDialog::OnSize(nType, cx, cy);
// TODO: Add your message handler code here
if( IsWindow( z_AttrList.m_hWnd ))
{
z_AttrList.MoveWindow( 0, 0, cx, cy );
z_AttrList.SetColumnWidth(1 , LVSCW_AUTOSIZE_USEHEADER);
}
}
5. 한줄 쭉 선택되게 하기, 그리드 라인 주기
---------------------------------------------------
z_AttrList.SetExtendedStyle(LVS_EX_GRIDLINES | LVS_EX_FULLROWSELECT);
6. 선택한 아이템(ROW)를 지우기
---------------------------------------------------
CDWordArray m_anDragIndexes; // 선택한 아이템을 기억하는
배열
int CAttrList::fnRemoveItem()
{
m_anDragIndexes.RemoveAll();
POSITION pos = GetFirstSelectedItemPosition();
while (pos)
{
m_anDragIndexes.Add(GetNextSelectedItem(pos));
}
int nSize = m_anDragIndexes.GetSize();
int nRet = -1;
if( nSize )
nRet = m_anDragIndexes[0];
while( nSize-- )
DeleteItem( m_anDragIndexes[nSize] );
m_anDragIndexes.RemoveAll();
return nRet;
}
7. 두 아이템을 스왑 하기
---------------------------------------------------
void CAttrList::fnSwapItem( int nItem1, int nItem2 )
{
int hi = nItem2;
int lo = nItem1;
CStringArray rowText;
LV_ITEM lvitemlo, lvitemhi;
CHeaderCtrl* pHeaderCtrl = GetHeaderCtrl();
int nColCount =pHeaderCtrl->GetItemCount();
rowText.SetSize( nColCount );
int i;
for( i=0; i<nColCount; i++)
rowText[i] = GetItemText(lo, i);
lvitemlo.mask = LVIF_IMAGE | LVIF_PARAM | LVIF_STATE;
lvitemlo.iItem = lo;
lvitemlo.iSubItem = 0;
lvitemlo.stateMask = LVIS_CUT | LVIS_DROPHILITED |
LVIS_FOCUSED | LVIS_SELECTED |
LVIS_OVERLAYMASK | LVIS_STATEIMAGEMASK;
lvitemhi = lvitemlo;
lvitemhi.iItem = hi;
GetItem( &lvitemlo );
GetItem( &lvitemhi );
for( i=0; i<nColCount; i++)
SetItemText(lo, i, GetItemText(hi, i));
lvitemhi.iItem = lo;
SetItem( &lvitemhi );
for( i=0; i<nColCount; i++)
SetItemText(hi, i, rowText[i]);
lvitemlo.iItem = hi;
SetItem( &lvitemlo );
}
8.기존에 선택되어있는 것을 해체하기
-----------------------------------
int nOldItem = GetSelectionMark();
if( nOldItem > 0 )
SetItemState( nOldItem , 0, LVIS_SELECTED);
리스트 컨트롤에 콤보박스 올려놓기
리포트 스타일의 리스트 콘트롤에 행간 높이 조절
리스트 콘트롤의 행간 높이를 조절하고자 할때,
원래 윈도우가 날려주는 메세지가 있지만, CListCtrlView 를 이용하는경우 도움이 전혀 안됩니다.
이경우 마지막 방법은 이미지 리스트를 이용하는 것입니다.
CImageList m_image;
....
CListCtrl &m_list=GetListCtrl();
m_image.Create(1,20,ILC_COLORDDB,1,0);
list.SetImageList(&m_image,LVSIL_SMALL);
.......
을 써서 Y 크기를 조절해서 크기를 바꿀 수 있습니다.
메세지를 생성하는게 제일 좋은데 어쩔 수 없네요
그리고
ListView_SetExtendedListViewStyle(list, LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES|LVS_EX_SUBITEMIMAGES);
을 이용하시면 ROW 전체를 선택하거나, GRID 를 표시해주고, 서브아이템에 이미지 표시하기 등이 가능해
집니다.
BOOL ListCtrl_ModifyExtendedStyle(CListCtrl& p_rListCtrl,
const DWORD p_dwStyleEx,
const bool p_bAdd)
{
HWND t_hWnd = p_rListCtrl.GetSafeHwnd();
DWORD t_dwStyleEx = ListView_GetExtendedListViewStyle(t_hWnd);
if(p_bAdd)
{
if(0 == (p_dwStyleEx & t_dwStyleEx))
{
// add style
t_dwStyleEx |= p_dwStyleEx;
}
}
else
{
if(0 != (p_dwStyleEx & t_dwStyleEx))
{
// remove style
t_dwStyleEx &= ~p_dwStyleEx;
}
}
ListView_SetExtendedListViewStyle(t_hWnd, t_dwStyleEx);
return true;
}
리스트콘트롤에서 원하는 값을 맨위로 오게 스크롤 하기
void CMyListView::SelectListItem(CString text)
{
CListCtrl &list = GetListCtrl();
LVFINDINFO info;
int index;
POINT p;
info.flags = LVFI_PARTIAL|LVFI_STRING;
info.psz = text;
index = list.FindItem(&info) - 1;
list.SetItemState(index, LVIS_SELECTED | LVIS_FOCUSED, LVIF_STATE);
list.GetItemPosition(index, &p);
list.Scroll((CSize)p);
}
콘트롤에 멤버 변수를 설정해 놓았으면 맨 첫줄은 필요없구요
아래 list 변수대신 멤버변수를 사용하시면 됨다.
'NativeCode > mfc' 카테고리의 다른 글
리스트 컨트롤 시스템 아이콘 삽입 (0) | 2010.03.18 |
---|---|
리스트 컨트롤 파일 아이콘 추가하기 (1) | 2010.03.18 |
CTRL 값 얻기 (0) | 2010.03.18 |
배경 그림 넣기 (0) | 2010.03.18 |
부모윈도우 호출 (0) | 2010.03.18 |