我的知识记录

digSelf

MFC控件的使用:List Control和Tab Control

MFC
2646
2022-01-25
MFC控件的使用:List Control和Tab Control

表单控件的使用

表单的初始化

bool CFirstDlg::InitListControl(CListCtrl* pCtrlBillsLst)
{
	DWORD dwStyle;
	dwStyle = ::GetWindowLong(pCtrlBillsLst->m_hWnd, GWL_STYLE);
	dwStyle |= LVS_REPORT | LVS_SHOWSELALWAYS | LVS_EDITLABELS;

	::SetWindowLong(pCtrlBillsLst->m_hWnd, GWL_STYLE, dwStyle);

	dwStyle = pCtrlBillsLst->GetExtendedStyle();
	dwStyle |= LVS_EX_GRIDLINES | LVS_EX_FULLROWSELECT;
	pCtrlBillsLst->SetExtendedStyle(dwStyle);

	pCtrlBillsLst->SetBkColor(RGB(0xFF, 0xFF, 0xE0));
	pCtrlBillsLst->SetTextBkColor(RGB(0xFF, 0xFF, 0xE0));

	return true;
}

首先,通过DDX将表单控件与一个成员变量进行绑定,然后调用这个初始化函数即可。该初始化函数将表单控件的风格和扩展风格进行修改以可以选中全行,并设置表单的背景颜色。

表单的常用函数

  • InsertColumn 在表头中插入一个表单列,并将字段设置为指定名称
  • InsetItem 在表单中插入一条记录
  • SetItemText 在指定记录中的指定列(字段)中插入指定字符串
  • GetFirstSelectedItemPosition 获取第一个选中项的位置(索引)
  • GetNextSelectedItem 获取下一个选中项的位置
  • GetHeaderCtrl 获取表单控件的表头控件,返回值为CHeaderCtrl*,可以通过表单控件获取一共有几列等,使用CHeaderCtrl::GetItemCount来获取。
  • GetColumnWidth 获取指定列的宽度
  • SetColumnWidth 设置指定列的宽度,其参数有一个LVSCW_AUTOSIZE_USEHEADER可以根据列中的内容,自动设置一个合适的列大小,需要在设置之前禁止掉REDRAW消息,即:SetRedraw(FALSE)以避免闪烁,在计算并设置列宽度后,再发送REDRAW消息,即:SetRedraw(TRUE).

表单的常用操作模板代码

遍历所有选择的表项

由于表单中的项可以多选,因此可以使用GetFirstSelectedItemPositionGetNextSelectedItem配合着进行遍历,代码如下:

// 代码来自MSDN
POSITION pos = m_myListCtrl.GetFirstSelectedItemPosition();
if (pos == NULL)
{
    TRACE(_T("No items were selected!\n"));
}
else
{
    while (pos)
    {
        int nItem = m_myListCtrl.GetNextSelectedItem(pos);
        TRACE(_T("Item %d was selected!\n"), nItem);
        // you could do your own processing on nItem here
    }
}

设置列的宽度自适应

// 获取表头中列的个数,在后面要逐一遍历每一个列进行调整
int GetItemCount(const CListCtrl &listCtrl)
{
    CHeaderCtrl *pHeaderCtrl = listCtrl.GetHeaderCtrl();
    return pHeaderCtrl->GetItemCount();
}

bool AdjustColumnsWidth()
{
	SetRedraw(FALSE);

	int nCount = GetItemCount(m_ctlProcess);
	for (int idx = 0; idx < nCount; ++idx)
	{
		// 将当前表单中的第idx列设置为自适应大小
		// m_ctlProcess是CListCtrl类型的变量,是对话框类中的数据成员,可以将该控件子类化,然后将该方法封装到子类中去,就可以省略这个变量
		m_ctlProcess.SetColumnWidth(idx, LVSCW_AUTOSIZE);
		// 获取当前列的宽度
		int nCurColSize = m_ctlProcess.GetColumnWidth(idx);

		// 将表头中的第idx列设置为自适应大小
		m_ctlProcess.SetColumnWidth(idx, LVSCW_AUTOSIZE_USEHEADER);
		int nCurHeaderColSize = m_ctlProcess.GetColumnWidth(idx);

		// 取最大宽度,设置为当前表单列和表头列的宽度
		m_ctlProcess.SetColumnWidth(idx, max(nCurColSize, nCurHeaderColSize));
	}

	SetRedraw(TRUE);
	return true;
}

表单大小跟随对话框大小

void CFirstDlg::OnSize(UINT nType, int cx, int cy)
{
	CDialog::OnSize(nType, cx, cy);

	// TODO: 在此处添加消息处理程序代码
	if (::IsWindow(m_ctlProcess.GetSafeHwnd()) == FALSE)
	{
		return;
	}
	CRect rc;
	GetClientRect(&rc);

	m_ctlProcess.MoveWindow(rc);
}

WM_SIZE消息函数中,实时获取当前的对话框的大小和位置,然后通过MoveWindow进行同步修改即可。

Tab Control的使用

Tab Control的初始化

首先拖动Tab Control到目标对话框中去,并通过DDX建立关联。创建若干Tab Page对话框,其风格Style要选择为Child;其边框Boarder选择None

然后为上述Page 创建对应的类,并在主对话框中建立成员变量。本文使用CList进行保存这若干个Tab Page

// 在tab control中插入两个tab页
	m_tabControl.InsertItem(0, _T("第一个tab页"));
	m_tabControl.InsertItem(1, _T("第二个tab页"));

	// 对两个tab页创建出内存非模态对话框出来
	m_dlgFirst.Create(DLG_TAB_FIRST, &m_tabControl);
	m_dlgSecond.Create(DLG_TAB_SECOND, &m_tabControl);

	// 将tab control和tab pages放置到主对话框的合适的位置
	CRect rcClient;
	m_tabControl.GetClientRect(&rcClient);

	rcClient.top += 40;
	
	m_dlgFirst.MoveWindow(rcClient);
	m_dlgSecond.MoveWindow(rcClient);

	// 将pages保存起来
	m_lstTabPages.AddTail(&m_dlgFirst);
	m_lstTabPages.AddTail(&m_dlgSecond);

	// 设置当前选择的页面
	m_nCurSelPage = 0;
	ShowPageByIndex(m_nCurSelPage);

其中ShowPageByIndex的代码如下:

bool CTabControlDlg::ShowPageByIndex(int nCurSel)
{
	
	POSITION pos = m_lstTabPages.GetHeadPosition();

	for (int i = 0; i < m_lstTabPages.GetCount(); ++i)
	{
		if (i != nCurSel)
		{
			m_lstTabPages.GetAt(pos)->ShowWindow(SW_HIDE);
		}
		else
		{
			m_lstTabPages.GetAt(pos)->ShowWindow(SW_SHOW);
		}
		m_lstTabPages.GetNext(pos);
	}

	return true;
}

自适应Tab page大小

void CTabControlDlg::OnSize(UINT nType, int cx, int cy)
{
	CDialogEx::OnSize(nType, cx, cy);

	// 如果还没建立该控件,则忽略掉
	if (::IsWindow(m_tabControl.GetSafeHwnd()) == FALSE)
	{
		return;
	}

	CRect rc;
	GetClientRect(&rc);

	// 通过AdjustRect获取调整好后的Tab Control的控件的大小和位置
	m_tabControl.AdjustRect(FALSE, &rc);
	// 通过MoveWindow进行实际的调整
	m_tabControl.MoveWindow(rc);
	
	// 调整Tab Control下的所有子对话框
	m_tabControl.GetClientRect(&rc);
	rc.top += 40;
	POSITION pos = m_lstTabPages.GetHeadPosition();
	for (int i = 0; i < m_lstTabPages.GetCount(); ++i) 
	{
		CDialog* pTabPage = m_lstTabPages.GetAt(pos);
		
		pTabPage->MoveWindow(rc);

		// 自动获取下一个
		m_lstTabPages.GetNext(pos);
	}
}

获取选中页

void CTabControlDlg::OnTcnSelchangeMainTabcontrol(NMHDR* pNMHDR, LRESULT* pResult)
{
	// TODO: 在此添加控件通知处理程序代码
	m_nCurSelPage = m_tabControl.GetCurSel();
	ShowPageByIndex(m_nCurSelPage);
	
	*pResult = 0;
}

重写TCN_SELCHANGE消息,获取当前选中的页面

  • 0