关于MFC的使用总结之八——CGridListCtrlEx鼠标响应事件

写在前面

MFC是一种用c++设计交互界面的的开发工具,在二三十年前比较流行,但目前比较少用了。所以现在使用MFC进行开发,相关资料的查找也比较困难。最近做了一个与MFC有关的项目,其中用到的相关知识总结于此。在使用工具时,最好的资料其实就是那个库本身,查看库内的代码总会有意想不到的收获。本人用的是VS2017专业版里面的MFC。本章时在总结七的基础上修改的。文末附代码地址。

点击表格触发事件

在主对话框中选中List Control鼠标右键选中类向导
在这里插入图片描述
由于List Control绑定的类是导入的CGridListCtrlEx,所以类名选择CGridListCtrlEx,选中左边的消息,可以看到CGridListCtrlEx的类里面其实已经存在鼠标响应事件了。
在这里插入图片描述
这里我们就选择鼠标左键单击事件,然后选择编辑代码,可以看到对应的响应程序

void CGridListCtrlEx::OnLButtonDown(UINT nFlags, CPoint point)
{
	// Find out what subitem was clicked
	int nRow, nCol;
	CellHitTest(point, nRow, nCol);

	// If not left-clicking on an actual row, then don't update focus cell
	if (nRow==-1)
	{
		CListCtrl::OnLButtonDown(nFlags, point);
		return;
	}

	if( GetFocus() != this )
		SetFocus();	// Force focus to finish editing

	int startEdit = OnClickEditStart(nRow, nCol, point, false);
	if (startEdit!=2)
	{
		// Update the focused cell before calling CListCtrl::OnLButtonDown()
		// as it might cause a row-repaint
		SetFocusCell(nCol);

		CListCtrl::OnLButtonDown(nFlags, point);

		// LVN_BEGINDRAG message can be fired when calling parent OnLButtonDown(),
		// this should not result in a start edit operation
		if (GetFocusCell() != nCol)
		{
			SetFocusCell(nCol);
			startEdit = 0;
		}

		// CListCtrl::OnLButtonDown() doesn't change row if clicking on subitem without fullrow selection
		if (!(GetExtendedStyle() & LVS_EX_FULLROWSELECT))
		{
			if (nRow!=GetFocusRow())
			{
				SetFocusRow(nRow);
				if (!(GetKeyState(VK_CONTROL) < 0) && !(GetKeyState(VK_SHIFT) < 0))
				{
					SelectRow(-1, false);
					SelectRow(nRow, true);
				}
			}
		}

		// CListCtrl::OnLButtonDown() doesn't always cause a row-repaint
		// call our own method to ensure the row is repainted
		SetFocusCell(nCol, true);
	}

	if (startEdit!=0)
	{
		// This will steal the double-click event when double-clicking a cell that already have focus,
		// but we cannot guess after the first click, whether the user will click a second time.
		// A timer could be used but it would cause slugish behavior (http://blogs.msdn.com/oldnewthing/archive/2004/10/15/242761.aspx)
		EditCell(nRow, nCol, point);
	}
}

程序中的nRow, nCol就是鼠标点击表格的坐标,向其中添加测试代码在函数最后面

	CString str;
	str.Format(_T("这是第%d行第%d列"), nRow, nCol);
	AfxMessageBox(str); 

运行程序点击copenhagen就可以得到(注意行是从0开始的,列是从1开始的)
在这里插入图片描述
即使将表格进行拖动,显示仍旧不变
在这里插入图片描述
所以,我们只要将nRow, nCol;导入到需要的地方,就可以选择项目,触发各种响应。弱国每一个条目对应一个视频,这样就可以点击一个条目,程序自动播放这个条目所对应的视频。

表格中添加按钮,点击按钮触发事件

还一种是在表格中加入按钮,点击按钮来触发相应的事件。
由于CGridListCtrlEx中没有按钮的部分,需要对其进行扩展。
先新建MButton的类。
其中MButton.h

#pragma once


// CMButton


#define  WM_BN_CLICK  WM_USER + 100
#define  WM_Menu_CLICK  WM_USER + 101
#define  USE_TOPINDEX_BUTTON


class CMButton : public CButton
{
	DECLARE_DYNAMIC(CMButton)

public:
	CMButton();
	CMButton( int nItem, int nSubItem, CRect rect, HWND hParent );
	virtual ~CMButton();

protected:
	DECLARE_MESSAGE_MAP()
public:
	afx_msg void OnBnClicked();
public:
	int m_inItem;
	int m_inSubItem;
	CRect m_rect;
	HWND m_hParent;
	BOOL bEnable;

//	afx_msg BOOL OnEraseBkgnd(CDC* pDC);
};

MButton.cpp

// MButton.cpp : 实现文件
//

#include "stdafx.h"
//#include "NewListCtrl.h"
#include "MButton.h"


// CMButton

IMPLEMENT_DYNAMIC(CMButton, CButton)

CMButton::CMButton()
{
	m_inItem = 0;
	m_inSubItem = 0;
    ZeroMemory(m_rect, sizeof(CRect)) ;
	m_hParent = NULL;
	bEnable = FALSE;
}

CMButton::CMButton( int nItem, int nSubItem, CRect rect, HWND hParent )
{
	m_inItem = nItem;
	m_inSubItem = nSubItem;
	m_rect = rect;
	m_hParent = hParent;
	bEnable = TRUE;
}

CMButton::~CMButton()
{
}


BEGIN_MESSAGE_MAP(CMButton, CButton)
	ON_CONTROL_REFLECT(BN_CLICKED, &CMButton::OnBnClicked)
//ON_WM_ERASEBKGND()
END_MESSAGE_MAP()



// CMButton 消息处理程序



void CMButton::OnBnClicked()
{
	// TODO: 在此添加控件通知处理程序代码
	// TODO: 在此添加控件通知处理程序代码
	::SendMessage(AfxGetMainWnd()->m_hWnd, WM_BN_CLICK, m_inItem, m_inSubItem );   //-m_hParent
}

//BOOL CMButton::OnEraseBkgnd(CDC* pDC)
//{
//	// TODO: 在此添加消息处理程序代码和/或调用默认值
//	
//	return CButton::OnEraseBkgnd(pDC);
//}

加入到CGridListCtrlEx目录中,并在项目中添加,更改CGridListCtrlEx.h和CGridListCtrlEx.cpp,文档太长,放文后。之后就是在GridlistDlg.h添加消息响应的定义

public:
	afx_msg LRESULT onBnCLick(WPARAM wParam, LPARAM lParam);

在GridlistDlg.cpp添加映射

BEGIN_MESSAGE_MAP(CGridlistDlg, CDialogEx)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_MESSAGE(WM_BN_CLICK, onBnCLick)
END_MESSAGE_MAP()

添加函数

LRESULT CGridlistDlg::onBnCLick(WPARAM wParam, LPARAM lParam)
{
	int nRow = (int)wParam;
	int nCol = (int)lParam;

#ifdef USE_TOPINDEX_BUTTON

	int iTopIndex = m_ListCtrl.GetTopIndex();
	TRACE(L"move:before:iTopIndex = %d, nRow = %d\n", iTopIndex, nRow);
	nRow = iTopIndex + nRow;
#endif
	CString str;
	str.Format(_T("nRow =%d, nCol = %d"), nRow, nCol);
	AfxMessageBox(str);
	return 0;
}

修改BOOL CGridlistDlg::OnInitDialog()中的代码

		// Create and attach image list
	m_ImageList.Create(16, 16, ILC_COLOR16 | ILC_MASK, 1, 0);
	m_ImageList.Add(AfxGetApp()->LoadIcon(IDI_ICON1));
	m_ImageList.Add(AfxGetApp()->LoadIcon(IDI_ICON2));
	m_ImageList.Add(AfxGetApp()->LoadIcon(IDI_ICON3));
	m_ImageList.Add(AfxGetApp()->LoadIcon(IDI_ICON4));
	m_ImageList.Add(AfxGetApp()->LoadIcon(IDI_ICON5));
	m_ImageList.Add(AfxGetApp()->LoadIcon(IDI_ICON6));
	int nStateImageIdx = CGridColumnTraitDateTime::AppendStateImages(m_ListCtrl, m_ImageList);	// Add checkboxes

	m_ListCtrl.SetImageList(&m_ImageList, LVSIL_SMALL);

	// Give better margin to editors
	m_ListCtrl.SetCellMargin(2);
	CGridRowTraitXP* pRowTrait = new CGridRowTraitXP;
	m_ListCtrl.SetDefaultRowTrait(pRowTrait);
	m_ListCtrl.EnableVisualStyles(true);

	// Create Columns
	m_ListCtrl.InsertHiddenLabelColumn();	// Requires one never uses column 0

	for (int col = 0; col < m_DataModel.GetColCount(); ++col)
	{
		const CString& title = m_DataModel.GetColTitle(col);
		CGridColumnTrait* pTrait = NULL;
		if (col == 0)	// Country
		{
			CGridColumnTraitCombo* pComboTrait = new CGridColumnTraitCombo;
			const vector<CString>& countries = m_DataModel.GetCountries();
			for (size_t i = 0; i < countries.size(); ++i)
				pComboTrait->AddItem((DWORD_PTR)i, countries[i]);
			pTrait = pComboTrait;
		}
		if (col == 1)	// City
		{
			pTrait = new CGridColumnTraitEdit;
		}
		if (col == 2)	// Year won
		{
			CGridColumnTraitDateTime* pDateTimeTrait = new CGridColumnTraitDateTime;
			pDateTimeTrait->AddImageIndex(nStateImageIdx, _T(""), false);		// Unchecked (and not editable)
			pDateTimeTrait->AddImageIndex(nStateImageIdx + 1, COleDateTime(1970, 1, 1, 0, 0, 0).Format(), true);	// Checked (and editable)
			pDateTimeTrait->SetToggleSelection(true);
			pTrait = pDateTimeTrait;
		}
		if (col == 3)	// Year won
		{
			CGridColumnTraitHyperLink* pHyperLinkTrait = new CGridColumnTraitHyperLink;
			pHyperLinkTrait->SetShellFilePrefix(_T("http://en.wikipedia.org/wiki/UEFA_Euro_"));
			pTrait = pHyperLinkTrait;
		}

		m_ListCtrl.InsertColumnTrait(col + 1, title, LVCFMT_LEFT, 100, col, pTrait);
	}
	//添加按钮
	const CString& title = _T("按钮");
	CGridColumnTrait* pTrait = NULL;
	m_ListCtrl.InsertColumnTrait(5, title, LVCFMT_LEFT, 100, 4, pTrait);
	// Insert data into list-control by copying from datamodel
	int nItem = 0;
	for (size_t rowId = 0; rowId < m_DataModel.GetRowIds(); ++rowId)
	{

		nItem = m_ListCtrl.InsertItem(++nItem, m_DataModel.GetCellText(rowId, 0));
		m_ListCtrl.SetItemData(nItem, rowId);
		for (int col = 0; col < m_DataModel.GetColCount(); ++col)
		{
			int nCellCol = col + 1;	// +1 because of hidden column
			const CString& strCellText = m_DataModel.GetCellText(rowId, col);
			m_ListCtrl.SetItemText(nItem, nCellCol, strCellText);
			if (nCellCol == 3)
			{
				if (strCellText == _T(""))
					m_ListCtrl.SetCellImage(nItem, nCellCol, nStateImageIdx);	// unchecked
				else
					m_ListCtrl.SetCellImage(nItem, nCellCol, nStateImageIdx + 1);	// checked
			}
		}
		m_ListCtrl.createItemButton(rowId, 5, GetParent()->GetSafeHwnd());	//添加按钮
		m_ListCtrl.SetCellImage(nItem, 1, nItem); // Assign flag-images

	}
	CViewConfigSectionWinApp* pColumnProfile = new CViewConfigSectionWinApp(_T("Sample List"));
	pColumnProfile->AddProfile(_T("Default"));
	pColumnProfile->AddProfile(_T("Special"));
	m_ListCtrl.SetupColumnConfig(pColumnProfile);

运行结果如下

按钮运行的结果与点击表格运行结果其实相差不多。在这里插入图片描述
其中按键上面的字符是可以自己设计的,设计的部分位于CGridListCtrlEx.cpp中

void CGridListCtrlEx::createItemButton(int nItem, int nSubItem, HWND hMain)
{
	CRect rect;
	CPoint pt;
	pt.x = 5;
	pt.y = 5;
	if (!EnsureVisible(nItem, TRUE))
		return;

	GetSubItemRect(nItem, nSubItem, LVIR_BOUNDS, rect);

	DWORD dwStyle = WS_CHILD | WS_VISIBLE;
	CMButton *pButton = new CMButton(nItem, nSubItem, rect, hMain);
	m_uID++;

	//调整矩形区域大小
	rect.TopLeft() += pt;
	rect.BottomRight() -= pt;

	pButton->Create(_T(">>>"), dwStyle, rect, this, m_uID);//可更改按键上符号
	m_mButton.insert(make_pair(nItem, pButton));

	return;
}

最终程序

GridlistDlg.h

// GridlistDlg.h: 头文件
//

#pragma once
#include “afxcmn.h”

#include “…\CGridListCtrlEx\CGridListCtrlGroups.h”
#include “CListCtrl_DataModel.h”

// CGridlistDlg 对话框
class CGridlistDlg : public CDialogEx
{
// 构造
public:
CGridlistDlg(CWnd* pParent = nullptr); // 标准构造函数

// 对话框数据
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_GRIDLIST_DIALOG };
#endif

protected:
virtual void DoDataExchange(CDataExchange* pDX);	// DDX/DDV 支持

// 实现
protected:
HICON m_hIcon;

// 生成的消息映射函数
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
DECLARE_MESSAGE_MAP()

private:
CGridListCtrlGroups m_ListCtrl;
CListCtrl_DataModel m_DataModel;
CImageList m_ImageList;

CGridlistDlg(const CGridlistDlg&);
CGridlistDlg& operator=(const CGridlistDlg&);

public:
afx_msg LRESULT onBnCLick(WPARAM wParam, LPARAM lParam);
};

GridlistDlg.cpp

// GridlistDlg.cpp: 实现文件
//

#include "stdafx.h"
#include "Gridlist.h"
#include "GridlistDlg.h"
#include "afxdialogex.h"


#include "..\CGridListCtrlEx\CGridColumnTraitDateTime.h"
#include "..\CGridListCtrlEx\CGridColumnTraitEdit.h"
#include "..\CGridListCtrlEx\CGridColumnTraitCombo.h"
#include "..\CGridListCtrlEx\CGridColumnTraitHyperLink.h"
#include "..\CGridListCtrlEx\CGridRowTraitXP.h"
#include "..\CGridListCtrlEx\ViewConfigSection.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// 用于应用程序“关于”菜单项的 CAboutDlg 对话框

class CAboutDlg : public CDialogEx
{
   
public:
	CAboutDlg();

// 对话框数据
#ifdef AFX_DESIGN_TIME
	enum {
    IDD = IDD_ABOUTBOX };
#endif

	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持

// 实现
protected:
	DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
   
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
   
	CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()


// CGridlistDlg 对话框



CGridlistDlg::CGridlistDlg(CWnd* pParent /*=nullptr*/)
	: CDialogEx(IDD_GRIDLIST_DIALOG, pParent)
{
   
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CGridlistDlg::DoDataExchange(CDataExchange* pDX)
{
   
	CDialogEx::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_LIST1, m_ListCtrl);
}

BEGIN_MESSAGE_MAP(CGridlistDlg, CDialogEx)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_MESSAGE(WM_BN_CLICK, onBnCLick)
END_MESSAGE_MAP()


// CGridlistDlg 消息处理程序

BOOL CGridlistDlg::OnInitDialog()
{
   
	CDialogEx::OnInitDialog();

	// 将“关于...”菜单项添加到系统菜单中。

	// IDM_ABOUTBOX 必须在系统命令范围内。
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != nullptr)
	{
   
		BOOL bNameValid;
		CString strAboutMenu;
		bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
		ASSERT(bNameValid);
		if (!strAboutMenu.IsEmpty())
		{
   
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	// 设置此对话框的图标。  当应用程序主窗口不是对话框时,框架将自动
	//  执行此操作
	SetIcon(m_hIcon, TRUE);			// 设置大图标
	SetIcon(m_hIcon, FALSE);		// 设置小图标

	// TODO: 在此添加额外的初始化代码
		// Create and attach image list
	m_ImageList.Create(16, 16, ILC_COLOR16 | ILC_MASK, 1, 0);
	m_ImageList.Add(AfxGetApp()->LoadIcon(IDI_ICON1));
	m_ImageList.Add(AfxGetApp()->LoadIcon(IDI_ICON2));
	m_ImageList.Add(AfxGetApp()->LoadIcon(IDI_ICON3));
	m_ImageList.Add(AfxGetApp()->LoadIcon(IDI_ICON4));
	m_ImageList.Add(AfxGetApp()->LoadIcon(IDI_ICON5));
	m_ImageList.Add(AfxGetApp()->LoadIcon(IDI_ICON6));
	int nStateImageIdx = CGridColumnTraitDateTime::AppendStateImages(m_ListCtrl, m_ImageList);	// Add checkboxes

	m_ListCtrl.SetImageList(&m_ImageList, LVSIL_SMALL);

	// Give better margin to editors
	m_ListCtrl.SetCellMargin(2);
	CGridRowTraitXP* pRowTrait = new CGridRowTraitXP;
	m_ListCtrl.SetDefaultRowTrait(pRowTrait);
	m_ListCtrl.EnableVisualStyles(true);

	// Create Columns
	m_ListCtrl.InsertHiddenLabelColumn();	// Requires one never uses column 0

	for (int col = 0; col < m_DataModel.GetColCount(); ++col)
	{
   
		const CString& title = m_DataModel.GetColTitle(col);
		CGridColumnTrait* pTrait = NULL;
		if (col == 0)	// Country
		{
   
			CGridColumnTraitCombo* pComboTrait = new CGridColumnTraitCombo;
			const vector<CString>& countries = m_DataModel.GetCountries();
			for (size_t i = 0; i < countries.size(); ++i)
				pComboTrait->AddItem((DWORD_PTR)i, countries[i]);
			pTrait = pComboTrait;
		}
		if (col == 1)	// City
		{
   
			pTrait = new CGridColumnTraitEdit;
		}
		if (col == 2)	// Year won
		{
   
			CGridColumnTraitDateTime* pDateTimeTrait = new CGridColumnTraitDateTime;
			pDateTimeTrait->AddImageIndex(nStateImageIdx, _T(""), false);		// Unchecked (and not editable)
			pDateTimeTrait->AddImageIndex(nStateImageIdx + 1, COleDateTime(1970, 1, 1, 0, 0, 0).Format(), true);	// Checked (and editable)
			pDateTimeTrait->SetToggleSelection(true);
			pTrait = pDateTimeTrait;
		}
		if (col == 3)	// Year won
		{
   
			CGridColumnTraitHyperLink* pHyperLinkTrait = new CGridColumnTraitHyperLink;
			pHyperLinkTrait->SetShellFilePrefix(_T("http://en.wikipedia.org/wiki/UEFA_Euro_"));
			pTrait = pHyperLinkTrait;
		}

		m_ListCtrl.InsertColumnTrait(col + 1, title, LVCFMT_LEFT, 100, col, pTrait);
	}
	const CString& title = _T("按钮");
	CGridColumnTrait* pTrait = NULL;
	m_ListCtrl.InsertColumnTrait(5, title, LVCFMT_LEFT, 100, 4, pTrait);
	// Insert data into list-control by copying from datamodel
	int nItem = 0;
	for (size_t rowId = 0; rowId < m_DataModel.GetRowIds(); ++rowId)
	{
   

		nItem = m_ListCtrl.InsertItem(++nItem, m_DataModel.GetCellText(rowId, 0));
		m_ListCtrl.SetItemData(nItem, rowId);
		for (int col = 0; col < m_DataModel.GetColCount(); ++col)
		{
   
			int nCellCol = col + 1;	// +1 because of hidden column
			const CString& strCellText = m_DataModel.GetCellText(rowId, col);
			m_ListCtrl.SetItemText(nItem, nCellCol, strCellText);
			if (nCellCol == 3)
			{
   
				if (strCellText == _T(""))
					m_ListCtrl.SetCellImage(nItem, nCellCol, nStateImageIdx);	// unchecked
				else
					m_ListCtrl.SetCellImage(nItem, nCellCol, nStateImageIdx + 1);	// checked
			}
		}
		m_ListCtrl.createItemButton(rowId, 5, GetParent(
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值