当前位置: 首页 > 工具软件 > Pop List View > 使用案例 >

VC++学习-控件篇(列表控件list control)

邓鸿彩
2023-12-01

转载用于后续查看
编程环境:VS2013,MFC
List Control控件,是个列表,对应的类为CListCtrl。下面没有说明的,默认风格都为report。

一、基本的使用

1.初始化风格
对于style的(报表风格、单选模式等)设置;对于ExtendedStyle(网格添加、item前添加check box等)的设置;例:
对于一个关联变量为m_Errorlist的List Control初始化:
LVS_ICON: 为每个item显示大图标
LVS_SMALLICON: 为每个item显示小图标
LVS_LIST: 显示一列带有小图标的item
LVS_REPORT: 显示item详细资料
直观的理解:windows资源管理器,“查看”标签下的“大图标,小图标,列表,详细资料”

LONG lStyle;
lStyle = GetWindowLong(m_Errorlist.m_hWnd, GWL_STYLE);//获取当前窗口style
lStyle &= ~LVS_TYPEMASK; //清除显示方式位
lStyle |= LVS_REPORT; //设置style
lStyle |= LVS_SINGLESEL;//单选模式
SetWindowLong(m_Errorlist.m_hWnd, GWL_STYLE, lStyle);//设置style
 
DWORD dwStyle = m_Errorlist.GetExtendedStyle();
dwStyle |= LVS_EX_FULLROWSELECT;//选中某行使整行高亮(只适用与report风格的listctrl)
dwStyle |= LVS_EX_GRIDLINES;//网格线(只适用与report风格的listctrl)
dwStyle |= LVS_EX_CHECKBOXES;//item前生成checkbox控件
m_Errorlist.SetExtendedStyle(dwStyle); //设置扩展风格

2.插入列、行
插入列:参数分别为(列位置0、列标题为Amp Enable、位置居中、列宽度、列索引号0)

m_ErrorList.InsertColumn(0, "Amp Enable", LVCFMT_CENTER, EnableListCtrlRect.Width() * 1 / 1, 0);

插入行:参数分别为(行索引0、行标题)

m_Errorlist.InsertItem(0, "caption");

3.插入子项:参数分别为(行索引号、列索引号、子项内容)

m_ListCtr.SetItemText(0,1,"content");
int nRow = m_ListCtrl.GetItemCount();//列表控件行数
int nCol = m_ListCtrl.GetHeaderCtrl()->GetItemCount();//列表控件列标题数量
for (int i = 0; i < nRow; i++)
{
   for (int j = 0; j < nCol; j++)
    {
      CString str = m_ListCtrl.GetItemText(i, j);//获取i行j列数据
     }
}

清除列表控件标题

m_ListCtrl.DeleteColumn(i);

4、已知选中item
选中style中的Show selection always选项,设置为true,或者在上面第2点中设置LVS_SHOWSELALWAYS
设置为true后,当选中某一个item,这个item就会一直被选中,知道选中下一个或者都不选。
5、选中和取消选中一行

int nIndex = 0;
//选中
m_list.SetItemState(nIndex, LVIS_SELECTED|LVIS_FOCUSED, LVIS_SELECTED|LVIS_FOCUSED);
//取消选中
m_list.SetItemState(nIndex, 0, LVIS_SELECTED|LVIS_FOCUSED);

6、得到listctrl中所有行的checkbox的状态

m_list.SetExtendedStyle(LVS_EX_CHECKBOXES);
CString str;
for(int i=0; i<m_list.GetItemCount(); i++)
{
    if( m_list.GetItemState(i, LVIS_SELECTED) == LVIS_SELECTED || m_list.GetCheck(i))
    {
       str.Format(_T("第%d行的checkbox为选中状态"), i);
       AfxMessageBox(str);
    }
}

7、得到listctrl中所有选中行的序号
方法一:

CString str;
for(int i=0; i<m_list.GetItemCount(); i++)
{
    if( m_list.GetItemState(i, LVIS_SELECTED) == LVIS_SELECTED )
     {
         str.Format(_T("选中了第%d行"), i);
         AfxMessageBox(str);
     }
}

方法二:

POSITION pos = m_list.GetFirstSelectedItemPosition();
if (pos == NULL)
    TRACE0("No items were selected!\n");
else
{
 while (pos)
 {
      int nItem = m_list.GetNextSelectedItem(pos);
      TRACE1("Item %d was selected!\n", nItem);
      // you could do your own processing on nItem here
  }
}

8、得到item的信息

TCHAR szBuf[1024];
LVITEM lvi;
lvi.iItem = nItemIndex;
lvi.iSubItem = 0;
lvi.mask = LVIF_TEXT;
lvi.pszText = szBuf;
lvi.cchTextMax = 1024;
m_list.GetItem(&lvi);

9、得到listctrl的所有列的header字符串内容

LVCOLUMN lvcol;
char  str[256];
int   nColNum;
CString  strColumnName[4];//假如有4列

nColNum = 0;
lvcol.mask = LVCF_TEXT;
lvcol.pszText = str;
lvcol.cchTextMax = 256;
while(m_list.GetColumn(nColNum, &lvcol))
{ 
   strColumnName[nColNum] = lvcol.pszText;
   nColNum++;
}

10、使listctrl中一项可见,即滚动滚动条

m_list.EnsureVisible(i, FALSE);

11、得到listctrl列数

int nHeadNum = m_list.GetHeaderCtrl()->GetItemCount();

12、删除所有列
方法一:

while ( m_list.DeleteColumn (0))

因为删除了第一列后,后面的列会依次向上移动。
方法二:

int nColumns = 4;
for (int i=nColumns-1; i>=0; i--)
m_list.DeleteColumn (i);

13、得到单击的listctrl的行列号
添加listctrl控件的NM_CLICK消息相应函数

void CTest6Dlg::OnClickList1(NMHDR* pNMHDR, LRESULT* pResult)
{
      // 方法一:
      /*
      DWORD dwPos = GetMessagePos();
      CPoint point( LOWORD(dwPos), HIWORD(dwPos) );
      m_list.ScreenToClient(&point);
      LVHITTESTINFO lvinfo;
      lvinfo.pt = point;
      lvinfo.flags = LVHT_ABOVE;
      int nItem = m_list.SubItemHitTest(&lvinfo);
      if(nItem != -1)
      {
          CString strtemp;
          strtemp.Format("单击的是第%d行第%d列", lvinfo.iItem, lvinfo.iSubItem);
          AfxMessageBox(strtemp);
       }
       */
   
       // 方法二:
       /*
       NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
       if(pNMListView->iItem != -1)
       {
            CString strtemp;
            strtemp.Format("单击的是第%d行第%d列",
            pNMListView->iItem, pNMListView->iSubItem);
            AfxMessageBox(strtemp);
       }
       */
       *pResult = 0;
}

14、判断是否点击在listctrl的checkbox上
添加listctrl控件的NM_CLICK消息相应函数

void CTest6Dlg::OnClickList1(NMHDR* pNMHDR, LRESULT* pResult)
{
     DWORD dwPos = GetMessagePos();
     CPoint point( LOWORD(dwPos), HIWORD(dwPos) );
   
     m_list.ScreenToClient(&point);
   
     LVHITTESTINFO lvinfo;
     lvinfo.pt = point;
     lvinfo.flags = LVHT_ABOVE;
     
     UINT nFlag;
     int nItem = m_list.HitTest(point, &nFlag);
     //判断是否点在checkbox上
     if(nFlag == LVHT_ONITEMSTATEICON)
     {
         AfxMessageBox("点在listctrl的checkbox上");
     } 
     *pResult = 0;
}

15、右键点击listctrl的item弹出菜单
添加listctrl控件的NM_RCLICK消息相应函数

void CTest6Dlg::OnRclickList1(NMHDR* pNMHDR, LRESULT* pResult)
{
    NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
    if(pNMListView->iItem != -1)
    {       
        m_pRClickMenu = new CMenu();
        ASSERT(m_pRClickMenu != NULL);
        m_pRClickMenu->CreatePopupMenu();
        m_pRClickMenu->AppendMenu(MF_STRING, IDC_BTN_SEND_EMAIL, "发送邮件");
        m_pRClickMenu->AppendMenu(MF_STRING, IDC_BTN_COPY, "复制到剪切板");
        // DWORD dwPos = GetMessagePos();
        // CPoint point( LOWORD(dwPos), HIWORD(dwPos) );
        CPoint point;
        GetCursorPos(&point);
        m_pRClickMenu->TrackPopupMenu(TPM_LEFTALIGN, point.x, point.y, this, NULL);
     } 
     *pResult = 0;
}

16、item切换焦点时(包括用键盘和鼠标切换item时),状态的一些变化顺序
添加listctrl控件的LVN_ITEMCHANGED消息相应函数

void CTest6Dlg::OnItemchangedList1(NMHDR* pNMHDR, LRESULT* pResult)
{
     NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
     // TODO: Add your control notification handler code here
   
     CString sTemp;
 
      if((pNMListView->uOldState & LVIS_FOCUSED) == LVIS_FOCUSED && 
     (pNMListView->uNewState & LVIS_FOCUSED) == 0)
      {
           sTemp.Format("%d losted focus",pNMListView->iItem);
      }
     else if((pNMListView->uOldState & LVIS_FOCUSED) == 0 &&
      (pNMListView->uNewState & LVIS_FOCUSED) == LVIS_FOCUSED)
     {
         sTemp.Format("%d got focus",pNMListView->iItem);
      } 

      if((pNMListView->uOldState & LVIS_SELECTED) == LVIS_SELECTED &&
      (pNMListView->uNewState & LVIS_SELECTED) == 0)
      {
           sTemp.Format("%d losted selected",pNMListView->iItem);
      }
      else if((pNMListView->uOldState & LVIS_SELECTED) == 0 &&
       (pNMListView->uNewState & LVIS_SELECTED) == LVIS_SELECTED)
      {
           sTemp.Format("%d got selected",pNMListView->iItem);
       }
     *pResult = 0;
}

17、listctrl内容进行大数据量更新时,避免闪烁。

m_list.SetRedraw(FALSE);
//更新内容
m_list.SetRedraw(TRUE);
// m_list.Invalidate();
// m_list.UpdateWindow();

18、listctrl第一列始终居左,解决办法
把第一列当成虚列,然后插入第二列及数据,最后删除第一列,以后列还是从0开始。

pListCtrl->InsertColumn(0, "", LVCFMT_CENTER, 0); 
pListCtrl->InsertColumn(1, "姓名", LVCFMT_CENTER, 80);
pListCtrl->InsertColumn(2, "性别", LVCFMT_CENTER, 100);
pListCtrl->InsertColumn(3, "国籍", LVCFMT_CENTER, 100);
pListCtrl->DeleteColumn(0);

19、使listctrl可编辑
做法是:当单击listctrl时使整行高亮显示,双击listctrl时让编辑框移动到你双击所在列的位置上,此时编辑框获得焦点并且将listctrl的值更新到编辑框上,
此时可以修改内容,当鼠标点击其他位置时,编辑框失去焦点,同时将编辑框的内容更新到listctrl中,从而完成对listctrl子项的修改。

  1. 首先在对话框中添加一个CEdit控件,并定义CEdit变量m_edit。当对话框初始化函数中隐藏该控件。
m_edit.ShowWindow(SW_HIDE);
  1. 然后对CListCtrl添加消息NM_DBLCLK(双击事件),在消息中添加代码
NM_LISTVIEW* pNMListView=(NM_LISTVIEW*)pNMHDR;
CRect rc;
if(pNMListView->iItem!=-1)
{
   m_row=pNMListView->iItem;//m_row为被选中行的行序号(int类型成员变量)
   m_column=pNMListView->iSubItem;//m_column为被选中行的列序号(int类型成员变量)
   m_list.GetSubItemRect(pNMListView->iItem, pNMListView->iSubItem,LVIR_LABEL,rc);//取得子项的矩形
   rc.left+=3;
   rc.top+=2;
   rc.right+=3;
   rc.bottom+=2;
   char * ch=new char [128];
   m_list.GetItemText(pNMListView->iItem, pNMListView->iSubItem,ch,128);//取得子项的内容
   m_edit.SetWindowText(ch);//将子项的内容显示到编辑框中
   m_edit.ShowWindow(SW_SHOW);//显示编辑框
   m_edit.MoveWindow(&rc);//将编辑框移动到子项上面,覆盖在子项上
   m_edit.SetFocus();//使编辑框取得焦点
   m_edit.CreateSolidCaret(1,rc.Height()-5);//创建一个光标
   m_edit.ShowCaret();//显示光标
   m_edit.SetSel(-1);//使光标移到最后面
}
*pResult = 0;
  1. 然后,添加CEdit失去焦点时的消息EN_KILLFOCUS。在该消息中添加代码使编辑框中的内容更新到CListCtrl中。
 CString str;
 m_edit.GetWindowText(str);//取得编辑框的内容
 m_list.SetItemText(m_row,m_column,str);//将该内容更新到CListCtrl中
 m_edit.ShowWindow(SW_HIDE);//隐藏编辑框

二、Edit Control的添加(键盘输入的支持)

完成效果:双击List Control的子项,会在子项处出现编辑框;输入焦点消失后,将编辑后的内容更新到子项处。实现List Control的可编辑功能。

1.在List Control所在对话框内添加一个编辑框控件,并设为不可见;

2.对List Control添加控件的双击事件NM_DBLCLK,在触发函数内写:

void CAboutListControlDlg::OnNMDblclkList1(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
	// TODO: 在此添加控件通知处理程序代码
	NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
	CRect rc;
	m_Row = pNMListView->iItem;//获得选中的行  
	m_Col = pNMListView->iSubItem;//获得选中列  
 
 
	if (pNMListView->iSubItem != 0) //如果选择的是子项;  
	{
		m_ListControl.GetSubItemRect(m_Row, m_Col, LVIR_LABEL, rc);//获得子项的RECT;  
		m_edit.SetParent(&m_ListControl);//转换坐标为列表框中的坐标  
		m_edit.MoveWindow(rc);//移动Edit到RECT坐在的位置;  
		m_edit.SetWindowText(m_ListControl.GetItemText(m_Row, m_Col));//将该子项中的值放在Edit控件中;  
		m_edit.ShowWindow(SW_SHOW);//显示Edit控件;  
		m_edit.SetFocus();//设置Edit焦点  
		m_edit.ShowCaret();//显示光标  
		m_edit.SetSel(-1);//将光标移动到最后  
	}
	*pResult = 0;
}

以上完成了双击显示编辑框控件,并将原子项内容更新到编辑框内;那么接下来要是实现,输入焦点消失后,编辑框的内容更新到子项内,并隐藏编辑框等待下一次双击动作。
3.给Edit Control添加KillFocus事件,并在触发函数内写:

void CAboutListControlDlg::OnEnKillfocusEdit1()
{
	// TODO: 在此添加控件通知处理程序代码
	CString tem;
	m_edit.GetWindowText(tem);    //得到用户输入的新的内容  
	m_ListControl.SetItemText(m_Row, m_Col, tem);   //设置编辑框的新内容  
	m_edit.ShowWindow(SW_HIDE);                //应藏编辑框 
}

三、List Control对于任意控件添加的支持(重点来了!)

以CButton类控件的添加为例!

基本思路:1.生成控件(静态/动态)2.将控件贴到List Control的指定位置 3.如有滚动条,跟随滚动条实现位置更新,且不屏闪(很重要)

1.生成控件:动态控件的生成在下一篇博客:MFC—动态生成任意数量的控件并添加消息响应 会具体介绍;

2.贴控件到指定位置处:(两种方式)

第一种:(只针对动态生成)生成时,将父窗口指针定义为ListControl且Rect为目标处的位置;

CButton *btn = new CButton();
btn->Create(str, bn_dwStyle, Rect, GetDlgItem(IDC_LIST1), IDC_Dynamic_Button0 + ButtonNum);

第二种:(动态/静态都可)控件生成后,这里rect1同上;

btn->SetParent(&m_ListCtr);//将button设为list ctr的子窗口,即转换为list ctr的坐标!
btn->MoveWindow(rect1);//将button移动到对应位置处

3.List Control存在滚动条情况下,保证贴上去的控件位置跟随滚动条刷新,且防止屏闪

**位置刷新:**试了很多方法,但其实最简单实用的就是开一个Timer,在OnTimer()中对所贴上去的所有控件,调用 MoveWindow()+Invalidate()函数即可实时重绘/更新窗口;

**防屏闪:**为了防止重绘一直在进行,导致屏幕的闪烁,最好的办法就是在合适的条件下开启和关闭位置刷新的Timer;

展开来说就是:对ListControl添加触发函数(LVN_BEGINSCROLL),在函数体内Set上述的位置刷新Timer;也即只有在拖动滚动条时才会触发位置刷新,这是在合适的时候开启Timer;

再开一个一直在工作的Timer,在此Timer内不停获取滚动条(水平/垂直)的位置,并不断判断本次触发与上次触发时滚动条位置的变化;如果没有产生变化,则意味着滚动条的拖动停止,此时Kill位置刷新的Timer即可,这是在合适的时间关闭Timer。

附上获取滚动条位置的函数:

m_ListCtr.GetScrollPos(SB_HORZ)//水平滚动条
m_ListCtr.GetScrollPos(SB_VERT)//垂直滚动条

转载自:特立独行的ju
来源:CSDN
原文:https://blog.csdn.net/qq_42281526/article/details/80774912

ClistCtrl控件右键菜单栏

NM_RCLICK

右键控件—>添加事件处理程序,选择NM_RCLICK,添加消息函数
在资源Menu中添加右键菜单(命名为IDR_MENU2),菜单下添加所需功能选项,并且对菜单下每一功能添加相应的函数。

///列表控件右键响应事件
void CFASTENERDlg::OnNMRClickUserlist(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
	//防止在空白区点击弹出菜单
	if (m_userlist.GetSelectedCount() <= 0)
	{
		return;
	}
	// TODO:  在此添加控件通知处理程序代码
	NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
	if (pNMListView->iItem != -1)
	{
		DWORD dwPos = GetMessagePos();
		CPoint point(LOWORD(dwPos), HIWORD(dwPos));
		CMenu m_Menu2, *pop;//声明右键菜单栏CMenu变量 
		//显示右键菜单栏
		m_Menu2.LoadMenu(IDR_MENU2);//IDR_MENU2
		pop = m_Menu2.GetSubMenu(0);
		pop->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this);
	}
	*pResult = 0;
}

参考:https://blog.csdn.net/u010439291/article/details/44226921

参考实例1
参考原文:https://blog.csdn.net/love3s/article/details/8009293

void CCardInfoDlg::OnNMRClickListCard(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
	//防止在空白区点击弹出菜单
	if (m_LstCtrl.GetSelectedCount() <= 0)
	{
		return;
	}
	//下面的这段代码, 不单单适应于ListCtrl
	CMenu menu, *pPopup;
	menu.LoadMenu(IDR_MENU_CARD_INFO);
	pPopup = menu.GetSubMenu(0);
	CPoint myPoint;
	ClientToScreen(&myPoint);
	GetCursorPos(&myPoint); //鼠标位置
	pPopup->TrackPopupMenu(TPM_LEFTALIGN|TPM_RIGHTBUTTON, myPoint.x, myPoint.y,GetParent());
}

这里需要注意一个地方, TrackPopupMenu的最后一个参数, 按钮如果想要响应事件, 则必须指定为 事件所在类, 一般指定为this,如果用GetParent的话, ListCtrl只能弹出菜单, 但却响应不了事件, 当然, 除非你为ListCtrl创建类, 并在类里边实现菜单的各个事件

参考实例2
原文:https://blog.csdn.net/oBuYiSeng/article/details/49759413
1、在资源视图中的添加一个MENU,如图
2、给要添加右键菜单的ListCtrl子类,添加消息
按 ctrl + shift + x 打开类向导,选择要添加的子类,在消息中找到 =NM_RCLICK消息,双击=NM_RCLICK,就会在右侧出现OnNMRClick,然后点击编辑代码即可
3、编辑代码

void CRunSerListCtr::OnNMRClick(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
	// TODO:  在此添加控件通知处理程序代码
	*pResult = 0;
 
	NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
	if (pNMListView->iItem != -1)
	{
		DWORD dwPos = GetMessagePos();
		CPoint point(LOWORD(dwPos), HIWORD(dwPos));
		CMenu menu;
		//添加线程操作
		VERIFY(menu.LoadMenu(IDR_MENU1));			//这里是我们在1中定义的MENU的文件名称
		CMenu* popup = menu.GetSubMenu(0);
		ASSERT(popup != NULL);
		popup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this);
 
		//下面的两行代码主要是为了后面的操作为准备的
		//获取列表视图控件中第一个被选择项的位置  
		POSITION m_pstion = GetFirstSelectedItemPosition();
		//该函数获取由pos指定的列表项的索引,然后将pos设置为下一个位置的POSITION值
		m_nIndex = GetNextSelectedItem(m_pstion);
		CString str;
		str.Format(L" m_nIndex = %d", m_nIndex);
		MessageBox(str, str, 0);
	}
}

CListCtrl控件的创建

如果一个CListCtrl控件对象和dialog上的控件已经绑定,那么可以跳过这个这一步,因为绑定了的对象已经完成了创建。
但是如果要在dialog上凭空创建出来一个列表控件,那么就需要了解CListCtrl类的成员函数:Create
先看这个函数的原型:
BOOL Create( DWORD dwStyle, const RECT& rect, CWnd pParentWnd, UINT nID );*
第一个参数:
用来声明这个列表的风格,具体风格如下 :
LVS_ALIGNLEFT Specifies that items are left-aligned in icon and small icon view.
指定条款是小图标且左对齐
LVS_ALIGNTOP Specifies that items are aligned with the top of the control in icon and small icon view.
指定条款是顶端对其且为小图标
LVS_AUTOARRANGE Specifies that icons are automatically kept arranged in icon view and small icon view.
指定图标自动保存在图标视图中,且为小图标视图
LVS_EDITLABELS Allows item text to be edited in place. The parent window must process the LVN_ENDLABELEDIT notification message.
允许条目适当的编辑,父窗口必须指定LVN_ENDLABELEDIT消息
LVS_ICON Specifies icon view.
指定图标视图
LVS_LIST Specifies list view.
指定列表视图
LVS_NOCOLUMNHEADER Specifies that a column header is not displayed in report view. By default, columns have headers in report view.
指明列的头结点不在报告视图中显示,默认时,列的头结点会在报告中显示出来
LVS_NOLABELWRAP Displays item text on a single line in icon view. By default, item text can wrap in icon view.
在图标视图中单独的显示出来。默认情况下,项文本能和图标同时显示
LVS_NOSCROLL Disables scrolling. All items must be within the client area.
关闭滚动条,全部的条目必须在客户区内显示
LVS_NOSORTHEADER Specifies that column headers do not work like buttons. This style is useful if clicking a column header in report view does not carry out an action, such as sorting.
表明列头不会像一个按钮那样。这个选项在report风格下如果不打算让其实现一些功能会很有用,比如点击后排序
LVS_OWNERDRAWFIXED Enables the owner window to paint items in report view. The list view control sends a WM_DRAWITEM message to paint each item; it does not send separate messages for each subitem. The itemData member of theDRAWITEMSTRUCT structure contains the item data for the specified list view item.
这个我貌似用不到……不翻译了
LVS_REPORT Specifies report view.
指明报告窗口
LVS_SHAREIMAGELISTS Specifies that the control does not take ownership of the image lists assigned to it (that is, it does not destroy the image lists when it is destroyed). This style enables the same image lists to be used with multiple list view controls.
指明这个控件并不获取图像列表的所有权。这就意味着,在这个对象析构的时候斌不会析构图像列表。这个风格允许同样的图像列表用于多个列表控件
LVS_SHOWSELALWAYS Always show the selection, if any, even if the control does not have the focus.
总是显示被选中的选项,即便窗口未被锁定
LVS_SINGLESEL Allows only one item at a time to be selected. By default, multiple items can be selected.
一次只能选择一个选项,默认的情况下,能同时选择多个条目
LVS_SMALLICON Specifies small icon view.
指定小图标视图
LVS_SORTASCENDING Sorts items based on item text in ascending order.
按照递增进行排序
LVS_SORTDESCENDING Sorts items based on item text in descending order.
按照递减排序
一下翻译这么多,有点累,看来我的英语还是太差,不然怎么会觉得累~

第二个参数:rect
这个参数就是你所创建的CListCtrl的位置,以及大小。不再做过多介绍了

第三个参数:pParentWnd
这个参数用来指定父窗口,一般是一个CDialog对象指针,需要注意的是,千万不能是NULL。

第四个参数:nID
控件ID,这个不用多说了,每个看控件都有的。

CListCtrl风格修改
但是如果你已经创建好了一个ListCtrl控件,那么你为其声明了一个CListCtrl控件,那么上面的Create函数你就可以跳过去了~
但是,风格总是要设置的,所以在这里,有一个函数可以调用,专门用来设置风格~

LONG SetWindowLong(
  HWND hWnd,       // handle to window
  int nIndex,      // offset of value to set
  LONG dwNewLong   // new value
);

CListCtrl的窗口句柄是public的,能直接获取。
第二个参数添加:GWL_STYLE,目的就是为了设置新的风格。
第三个参数就是风格的组合了
这个函数的具体内容请看MSDN,我这就不再多说了~
CListView的扩展风格
CListCtrl可以设置的风格:
LVS_EX_GRIDLINES //绘制表格
LVS_EX_SUBITEMIMAGES
LVS_EX_CHECKBOXES //带复选框
LVS_EX_TRACKSELECT //自动换行
LVS_EX_HEADERDRAGDROP
LVS_EX_FULLROWSELECT //选择整行
LVS_EX_ONECLICKACTIVATE//单击激活
LVS_EX_TWOCLICKACTIVATE//双击激活
LVS_EX_FLATSB//扁平滚动条
LVS_EX_REGIONAL
LVS_EX_INFOTIP
LVS_EX_UNDERLINEHOT
LVS_EX_UNDERLINECOLD
LVS_EX_MULTIWORKAREAS//多工作区
以上风格设置可以使用下面的CListCtrl成员函数来设置:
DWORD SetExtendedStyle( DWORD dwNewStyle );

void CMyRisingDlg::ListCtrlInitial()
{
    CRect lc_rect;
    DWORD dwStyle = ::GetWindowLong(m_LCTable.m_hWnd, GWL_STYLE); 
    dwStyle &= ~(LVS_TYPEMASK);
    dwStyle &= ~(LVS_EDITLABELS);
    SetWindowLong(m_LCTable.m_hWnd, GWL_STYLE,dwStyle |LVS_REPORT | LVS_NOLABELWRAP | LVS_SHOWSELALWAYS);
    m_LCTable.SetExtendedStyle(LVS_EX_ONECLICKACTIVATE|LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);
    m_LCTable.GetWindowRect(&lc_rect);
    DWORD dwListLength = lc_rect.Width() / 3;
    m_imagelist.Create(16, 16, ILC_COLORDDB|ILC_MASK, 0, 1);

    m_imagelist.Add(AfxGetApp()->LoadIcon(IDI_ICON1));
    m_imagelist.Add(AfxGetApp()->LoadIcon(IDI_ICON2));
    m_LCTable.SetImageList(&m_imagelist, LVSIL_SMALL);
    m_LCTable.InsertColumn(0, _T("Name"), LVCFMT_LEFT | LVCFMT_IMAGE, dwListLength);
    m_LCTable.InsertColumn(1, _T("PathName"), LVCFMT_LEFT, dwListLength);
    m_LCTable.InsertColumn(2, _T("Create Time"), LVCFMT_LEFT, dwListLength + 3);

    m_LCTable.InsertItem(0, LPSTR_TEXTCALLBACK);21 
}

很显然,这个函数的功能是初始化一个ListCtrl。从代码里面能够看出来,在初始化上,其实主要的就是风格设置。然后最一行的函数调用是为了做虚拟列表,CListCtrl的虚拟列表内容我会在新的随笔中介绍。

上面的几个比较主要的成员函数调用,SetExtendedStyle函数就不再多说了,可以参考样式以及上面的各种风格,或者直接去看MSDN。

然后下面的SetImageList是为了给列表上主要的首子项添加上文件图标,正在做,还未完成。不过上面的程序界面截图中能看到首列有图标,那个只是目前应付一下的~

接下来要比较重点的介绍InsertClumn函数:

这个函数是用来完成列的插入的。或者说,建立了一个索引,或者链表头。由此函数调之后才能调用InsertItem函数,否则是无效的,函数刚一调用,估计就直接返回个0了。

这个函数看下MSDN的介绍:

int InsertColumn( int nCol, const LVCOLUMN* pColumn );
int InsertColumn( int nCol, LPCTSTR lpszColumnHeading, int nFormat = LVCFMT_LEFT, int nWidth = -1, int nSubItem = -1 );
Return Value
The index of the new column if successful or -1 otherwise.

这个函数有两种形式,但是我只介绍第二种,原因为啥你应该很清楚~。这里第一个参数就是列号了,不多说。第二个参数是列名,也不用太罗嗦,第三个是格式,需要简单说一下。

nFormat参数有一下的几种形式:LVCFMT_LEFT, LVCFMT_RIGHT, or LVCFMT_CENTER.另外还有LVCFMT_IMAGE,默认情况下是从左往右的,也就是我们的书写顺序。另外需要注意的是,其实这个参数一般来讲的话都是直接使用LEFT参数的,原因就是如果你使用了center或者right参数,会直接导致一个后果:就是这列的全部内容,也都是对应的居中或者置右。另外如果使用了LVCFMT_IMAGE参数,那么就会在最左端添加图片的基础上,默认为LEFT风格,这点还是需要注意的。

目前除了InsertItem函数之外,其余的基础操作设置就差不多了,至于其他的一些操作我等会也还需要再看~

然后接下来讲下如何添加首项的图片。

我觉得添加这个图片的目的主要还是为了提供更好的用户体验。因为如果有图片的话,对大多数用户来说会提供一个很便利的途径来知道这个文件的类型属性。毕竟大多数的用户是不会去根据文件后缀来判断这个文件到底是干什么的~

废话不多说,先看一个类:CImageList.

接着贴MSDN:

An “image list” is a collection of same-sized images, each of which can be referred to by its zero-based index. Image lists are used to efficiently manage large sets of icons or bitmaps. All images in an image list are contained in a single, wide bitmap in screen device format. An image list may also include a monochrome bitmap that contains masks used to draw images transparently (icon style). The Microsoft Win32 application programming interface (API) provides image list functions that enable you to draw images, create and destroy image lists, add and remove images, replace images, merge images, and drag images.

大意如下:

一个图像列表是同样大小图像的集合,每个成员都能够通过以0为起始的目录来访问(其实就是顺序表访问)。图像列表控件能够用来有效的管理大数量的图标或位图。在图像列表中的所有图片都在屏幕设备格式中包含一个单独的,宽(这里不知道怎么翻译)的位图。一个图像列表控件也可能包含一个包含绘制透明图像任务的单色位图(这里不是很确定)。

上面我贴的那个函数里CImageList的使用也比较明了,简单介绍下:

对象创建完之后并不需要立即初始化,还不错。然后首先要调用其中的Create函数,就是创建一个初始化一个列表并付给对象,MSDN基本就这个意思。这里再贴一下函数原型:

BOOL Create( int cx, int cy, UINT nFlags, int nInitial, int nGrow );
BOOL Create( UINT nBitmapID, int cx, int nGrow, COLORREF crMask );
BOOL Create( LPCTSTR lpszBitmapID, int cx, int nGrow, COLORREF crMask );
BOOL Create( CImageList& imagelist1, int nImage1, CImageList& imagelist2, int nImage2, int dx, int dy );
BOOL Create( CImageList* pImageList );
Return Value
Nonzero if successful; otherwise 0.

很显然,在我的程序里面调用的函数是其中的第一个,所以我这就只介绍第一个了~前两个参数很明显,就是这个图片的尺寸。我在这里的建议还是,提供1616的图片比较合适。我曾经想过做88的,结果,太小了……实在不合适。这个尺寸是我做列表比较推荐的。第三个参数很显然就是个标志位,用来设置风格的。

具体风格如下:
ILC_COLOR Use the default behavior if none of the other ILC_COLOR* flags is specified. Typically, the default is ILC_COLOR4; but for older display drivers, the default is ILC_COLORDDB.
ILC_COLOR4 Use a 4-bit (16 color) device-independent bitmap (DIB) section as the bitmap for the image list.
ILC_COLOR8 Use an 8-bit DIB section. The colors used for the color table are the same colors as the halftone palette.
ILC_COLOR16 Use a 16-bit (32/64k color) DIB section.
ILC_COLOR24 Use a 24-bit DIB section.
ILC_COLOR32 Use a 32-bit DIB section.
ILC_COLORDDB Use a device-dependent bitmap.
ILC_MASK Uses a mask. The image list contains two bitmaps, one of which is a monochrome bitmap used as a mask. If this value is not included, the image list contains only one bitmap.

我上面的函数在这里使用的是后两个参数的组合。DDB的意思是设备相关位图,与之相反的就是大名鼎鼎的DIB了,不过这里没有。我的MSDN版本比较老,是给VC6.0做的,有兴趣的可以去MSDN网站上看看,新版的CImageList有没有提供DIB的。

不过我觉得,似乎我的那个代码里,ILC-MASK参数貌似没什么用处,回头删掉。

然后nInitial参数,就是Imagelist对象中初始化后就包含的图像数目,MSDN:Number of images that the image list initially contains.
最后一个参数:nGrow 原文:Number of images by which the image list can grow when the system needs to resize the list to make room for new images. This parameter represents the number of new images the resized image list can contain.
其实这个参数倒不是很重要,但是,能牵扯到效率问题。如果每次新添加一个图片就要申请一块新的内存,是不是有点效率太低了?所以不如一次申请多个,这样效率更高一下些。但是如果你的列表一般只需要10个,但是你每次resize之后会变成100个,就造成了浪费。所以还是需要注意一下的,但是小列表设置为5以内的数应该就行了。

这个函数介绍完了,就要接着讲添加的问题。不过这个还是比较简单的。可以直接调用add成员函数。

int Add( CBitmap* pbmImage, CBitmap* pbmMask );
int Add( CBitmap* pbmImage, COLORREF crMask );
int Add( HICON hIcon );
Return Value
Zero-based index of the first new image if successful; otherwise – 1.

这里提供了三个函数,前两个都是和Bitmap相关的,暂且不谈,只谈第三个。
第三个函数,很简单,只要一个图标资源的句柄就可以了。获取这个句柄资源,在MFC中有一种很简单的方法:直接调用App类的LoadIcon函数。

AfxGetApp()->LoadIcon(IDI_ICON1)

里面直接添加上工程中建立好的图标资源ID就好了。
不过这个成员函数只限CWinApp类的对象使用。另外一种方法是调用SDK的函数,其中hInstance就是句柄,这里应该直接添加app的m_hwnd,应该是的,我还没试过。第二参数要使用MAKEINTRESOURCE宏转一下就可以了。
接下来就是如何将图片绑定到列表头部了。
在两个对象都创建完成后,只要调用CListCtrl函数的一个成员函数就可以了:
CImageList* SetImageList( CImageList* pImageList, int nImageList );
Return Value
A pointer to the previous image list.
Parameters
pImageList
Pointer to the image list to assign.
nImageList
Type of image list. It can be one of these values:
LVSIL_NORMAL Image list with large icons.
LVSIL_SMALL Image list with small icons.
LVSIL_STATE Image list with state images.
不用说太多,我这里第二个参数直接使用的LVSIL_SMALL选项,毕竟16*16的图片绝对不大。
然后这样就实现了绑定,但是此时你需要知道,虽然两个对象绑定了,但是ImageList对象依旧能够独立于ListCtrl进行修改操作。
那么绑定了之后呢?还是要看InsertItem函数。
InsertItem函数形式有好几个,这里需要使用这种形式:
int InsertItem( int nItem, LPCTSTR lpszItem, int nImage );
这个形式的函数专门添加了nImage这个参数,目的就是为了实现列表的添加。
nImage的选择是对应ImageList中的图像寻址功能,如果在ImageList中添加好了图片,那么InsertItem函数就会根据这个参数的值取寻找对应的图片,然后直接将其添加到列表中。


来源:博客园
转载自:https://www.cnblogs.com/matrix-r/p/3159054.html

单击list获取选中的行列

NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
		if (pNMListView->iItem != -1)
		{
			m_row = pNMListView->iItem;//m_row为被选中行的行序号(int类型成员变量)
			m_column = pNMListView->iSubItem;//m_column为被选中行的列序号(int类型成员变量)
			CString strtemp;
			strtemp.Format(_T("单击的是第%d行第%d列"), pNMListView->iItem, pNMListView->iSubItem);
			AfxMessageBox(strtemp);
		}

实例

VS2010连接ACCESS2016 DataGrid

https://wenku.baidu.com/view/c03f1fa3dc3383c4bb4cf7ec4afe04a1b071b03a.html

Listcotrl设置

(参考《Visual C++从入门到精通》中国水利水电出版社)
Report、Single selection、Auto arrange、No label warp、Client edge。。。

 类似资料: