赞
踩
实现效果
最近接到一个任务,要让ClistCtrl像Excel一样可以编辑,经过一些摸索,最终实现效果如下。
基本思路
当鼠标单击CListCtrl对象时,控件发出NM_CLICK消息,在这个消息的响应函数中,能够获取用户单击了哪个单元格;然后,动态创建一个CEdit控件,使其和单元格大小、字体、位置、文字完全相同;最后,当用户对编辑框操作完,焦点转向其他控件时,提取CEdit中文字内容,并更新到CListCtrl当中去。
为了集成上述功能,从CListCtrl派生一个自定义类CListCtrlEdit,具体实现代码如下(代码中另外实现了自动弹出提示框功能)。
代码实现
//自定义列表控件头文件
//
#pragma once
class CListCtrlEdit : public CListCtrl
{
public:
CListCtrlEdit();
BOOL EnableTips(BOOL bEnableTips);
protected:
virtual BOOL PreTranslateMessage(MSG* pMsg);
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
afx_msg void OnNMClick(NMHDR *pNMHDR, LRESULT *pResult);
afx_msg void OnEnEdtKillFocus();
DECLARE_MESSAGE_MAP()
private:
CEdit m_wndEdt;
int m_iCurRow; //鼠标单击的行
int m_iCurColunm; //鼠标单击的列
int m_iHoverRow; //鼠标悬停的行
int m_iHoverColumn; //鼠标悬停的列
CToolTipCtrl m_wndToolTip; //提示框控件
BOOL m_bEnableTips; //鼠标移动到某个单元格时,是否给出提示
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
以下为自定义类的实现文件
#include “pch.h”
#include “CListCtrlEdit.h”
#define ID_DYN_CREATE_EDIT 10000
CListCtrlEdit::CListCtrlEdit()
:m_iCurColunm(-1)
,m_iCurRow(-1)
,m_bEnableTips(FALSE)
,m_iHoverColumn(-1)
,m_iHoverRow(-1)
{}
BOOL CListCtrlEdit::EnableTips(BOOL bEnableTips)
{
EnableToolTips(TRUE);
//创建tooltip控件
m_bEnableTips = m_wndToolTip.Create(this, TTS_ALWAYSTIP);
if (m_bEnableTips)
{
m_wndToolTip.Activate(TRUE);
m_wndToolTip.SetDelayTime(TTDT_INITIAL, 500);
}
return m_bEnableTips;
}
BEGIN_MESSAGE_MAP(CListCtrlEdit, CListCtrl)
ON_NOTIFY_REFLECT(NM_CLICK, &CListCtrlEdit::OnNMClick)
ON_EN_KILLFOCUS(ID_DYN_CREATE_EDIT,&CListCtrlEdit::OnEnEdtKillFocus)
ON_WM_MOUSEMOVE()
END_MESSAGE_MAP()
void CListCtrlEdit::OnNMClick(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast(pNMHDR);
// TODO: 在此添加控件通知处理程序代码
//进行单击检测,这个结构已经被扩展为能够适应子项的单击检测。 LVHITTESTINFO cHitTest; cHitTest.pt = pNMItemActivate->ptAction; if (-1 != SubItemHitTest(&cHitTest)) //检测给定坐标位于哪个单元格上 { if (cHitTest.flags & LVHT_ONITEMLABEL) { m_iCurRow = cHitTest.iItem; m_iCurColunm = cHitTest.iSubItem; //获取单元格信息 CRect rect; //单元格的尺寸 GetSubItemRect(m_iCurRow, m_iCurColunm, LVIR_LABEL, rect); //注意,这个坐标是相对于ListCtrl左上角的 CString sValue = GetItemText(m_iCurRow, m_iCurColunm); //获取当前单元格的内容 //动态创建编辑框 if (!m_wndEdt.m_hWnd) { m_wndEdt.Create(WS_CHILD | ES_CENTER | ES_AUTOHSCROLL | ES_WANTRETURN | ES_MULTILINE, rect, this, _ID_DYN_CREATE_EDIT_); m_wndEdt.SetFont(this->GetFont(), FALSE); //设置编辑框的字体 } m_wndEdt.SetWindowText(sValue); m_wndEdt.MoveWindow(rect); m_wndEdt.ShowWindow(SW_SHOW); int iLength = sValue.GetLength(); m_wndEdt.SetSel(0, iLength, FALSE); //设置光标选中所有文字 m_wndEdt.SetFocus(); } } *pResult = 0;
}
void CListCtrlEdit::OnEnEdtKillFocus()
{
m_wndEdt.ShowWindow(SW_HIDE);
if (m_iCurColunm < 0 || m_iCurRow < 0)
return;
CString sEdt;
m_wndEdt.GetWindowText(sEdt);
SetItemText(m_iCurRow, m_iCurColunm, sEdt); //更新List Control的单元格显示
}
BOOL CListCtrlEdit::PreTranslateMessage(MSG* pMsg)
{
//为了让tool tip控件不错过重要消息,比如鼠标单击,必须将该消息转发给你的tool tip控件。最好的办法是在
//PreTranslateMessage函数中调用CToolTipCtrl::RelayEvent进行转发。
//以下为MSDN中原文:
//In order for the tool tip control to be notified of important messages, such as WM_LBUTTONXXX messages,
//you must relay the messages to your tool tip control. The best method for this relay is to make a call
//to CToolTipCtrl::RelayEvent, in the PreTranslateMessage function of the owner window.
//The following example illustrates one possible method (assuming the tool tip control is called m_ToolTip):
if (m_bEnableTips)
{
if (pMsg->message == WM_LBUTTONDOWN ||
pMsg->message == WM_LBUTTONUP ||
pMsg->message == WM_MOUSEMOVE)
{
m_wndToolTip.RelayEvent(pMsg);
}
}
return CListCtrl::PreTranslateMessage(pMsg);
}
void CListCtrlEdit::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
if (m_bEnableTips)
{
//判断当前鼠标所在的位置
CString str;
LVHITTESTINFO cHit;
cHit.pt = point;
SubItemHitTest(&cHit);
if (cHit.iItem != m_iHoverRow || cHit.iSubItem != m_iHoverColumn) { //保存当前鼠标所在的行、列 m_iHoverRow = cHit.iItem; m_iHoverColumn = cHit.iSubItem; if (m_iHoverRow != -1 && m_iHoverColumn != -1) { str = GetItemText(m_iHoverRow, m_iHoverColumn); m_wndToolTip.AddTool(this, str); m_wndToolTip.Pop(); //Removes a displayed tool tip window from the view. } else { m_wndToolTip.AddTool(this, _T("请单击某个单元格进行编辑")); m_wndToolTip.Pop(); } } } CListCtrl::OnMouseMove(nFlags, point);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
在主对话框资源编辑器中添加一个CListCtrl控件,并在控件属性表中将其改为Report类型,最后在对话框头文件中将该控件类型修改为
public:
CListCtrlEdit m_wndLstEdit;
1
2
之后,在主对话框的OnInitDialog函数中完成对列表控件的初始化。
BOOL CEditableListCtrlDlg::OnInitDialog()
{
CDialog::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: 在此添加额外的初始化代码 CRect rect; m_wndLstEdit.SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES | LVS_EX_ONECLICKACTIVATE); m_wndLstEdit.GetClientRect(rect); m_wndLstEdit.InsertColumn(0, _T("项目"), LVCFMT_RIGHT, rect.Width() / 2, 0); m_wndLstEdit.InsertColumn(1, _T("值"), LVCFMT_CENTER, rect.Width() / 2, 1); //这个地方特别注意,CListCtrl中第1列无法直接设置对齐样式,需要用以下方法 LVCOLUMN lvc; lvc.mask = LVCF_FMT; m_wndLstEdit.GetColumn(0, &lvc); lvc.fmt &= ~LVCFMT_JUSTIFYMASK; lvc.fmt |= LVCFMT_CENTER; m_wndLstEdit.SetColumn(0, &lvc); m_wndLstEdit.InsertItem(0, _T("河北")); m_wndLstEdit.SetItemText(0, 1, _T("8")); m_wndLstEdit.InsertItem(1, _T("山东")); m_wndLstEdit.SetItemText(1, 1, _T("10")); m_wndLstEdit.InsertItem(2, _T("河南")); m_wndLstEdit.SetItemText(2, 1, _T("")); m_wndLstEdit.EnableTips(TRUE); return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
Ok, that is all!
原文链接:添加链接描述
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。