MFC小型工具通用界面框架CLIST控件+右键菜单功能
MFC-小型工具通用界面框架
0x1 场景
由于工作需要我会写代码开发工具给客户或者同事用。代码都能实现,但写个黑乎乎的命令行工具给别人用确实显得不够专业,用别人写好的成型工具又担心有后门。
所以掌握积累几个MFC的常用控件随时调用,代码量堆起来了就是不断反复利用的过程了。未来还是会把精力用在实现实际功能上,以成为界面工程师作为目标学习很容易找不到工作。
这篇文章主要分享给大家使用MFC做GUI的技巧,选用它是因为我更熟悉C++。而且VS是非常强大的调试器,除了下载就可以用之外,还能看反汇编、内存、堆栈。
环境:VS2017
效果
0x2 技术点
- CListCtrl,界面增加列名、跟随界面调整、增加内容
- 菜单使用,删除单条、删除多条
0x3 代码
1、CListCtrl控件
界面使用部分
1、【对话框编辑器】-【List Control】,增加一个List控件拖到指定位置
2、【属性】-【View】-【Report】,把显示方式改成报表形式
3、【类向导】-【成员变量】-【添加变量】-【m_List_xxx】,绑定一个操作的变量
代码部分
- 添加List控件的列表头,增加列 InsertColumn();
m_List_xxx.InsertColumn(i, // 插入的列
g_Column_Message_Data[i].title, // 插入的标题
LVCFMT_CENTER, // 插入的样式
g_Column_Message_Data[i].nWidth); // 插入的宽度
封装函数,定义结构体部分:
定义一个结构体,成员是标题【WCHAR *title】、宽度【int nWidth】。然后初始化每一列的列名占据叫什么名字,占据多宽。
/////////////////////////////////////////// 列表控件
typedef struct
{
WCHAR *title; //列表的名称
int nWidth; //列表的宽度
}COLUMNSTRUCT;
COLUMNSTRUCT g_Column_Message_Data[] =
{
{ _T("信息类型"), 80 },
{ _T("时间"), 100 },
{ _T("信息内容"), 300 }
};
//变量声明
int g_Column_Message_Count = 3; //列表的个数
int g_Column_Message_Width = 0; //列总宽度,初始化为0。在后面的函数中会赋值
新建一个函数【int GUI_InitList(void)】,在【OnInitDialog()】里调用。初始化List显示的列表名
int CAPTFilecheckDlg::GUI_InitList(void)
{
// init online list
m_CList_dirlist.SetExtendedStyle(LVS_EX_FULLROWSELECT);
for (int i = 0; i < g_Column_Online_Count; i++)
{
m_CList_dirlist.InsertColumn(i, g_Column_Online_Data[i].title, LVCFMT_CENTER, g_Column_Online_Data[i].nWidth);
g_Column_Online_Width += g_Column_Online_Data[i].nWidth; //得到总宽度
}
m_CList_MESSAGE.SetExtendedStyle(LVS_EX_FULLROWSELECT);
for (int i = 0; i < g_Column_Message_Count; i++)
{
m_CList_MESSAGE.InsertColumn(i, g_Column_Message_Data[i].title, LVCFMT_CENTER, g_Column_Message_Data[i].nWidth);
g_Column_Message_Width += g_Column_Message_Data[i].nWidth; //得到总宽度
}
return 0;;
}
- 跟随界面调整大小
最大化的时候界面跟着变大。
【类向导】-【消息】-【WM_SIZE】-【添加处理程序】,增加一个【OnSize】的消息函数
void CMFCClistDlg::OnSize(UINT nType, int cx, int cy)
{
CDialogEx::OnSize(nType, cx, cy);
// TODO: 在此处添加消息处理程序代码
// 命中目标
double dcx = cx; //对话框的总宽度
if (m_List_xxx.m_hWnd != NULL)
{
CRect rc;
rc.left = 1; //列表的左坐标
rc.top = 20; //列表的上坐标
rc.right = cx - 1; //列表的右坐标
rc.bottom = cy - 20; //列表的下坐标
m_List_xxx.MoveWindow(rc);
for (auto i = 0; i < g_Column_Message_Count; i++) { //遍历每一个列
double dd = g_Column_Message_Data[i].nWidth; //得到当前列的宽度
dd /= g_Column_Message_Width; //看一看当前宽度占总长度的几分之几
dd *= dcx; //用原来的长度乘以所占的几分之几得到当前的宽度
auto lenth = dd; //转换为int 类型
m_List_xxx.SetColumnWidth(i, (lenth)); //设置当前的宽度
}
}
}
- 增加列内容InsertItem()/SetItemText()
m_List_xxx.InsertItem(0, L"ok"); // 第一行的第一列内容
m_List_xxx.SetItemText(0, 1, L"2"); // 第一行的第二列内容
m_List_xxx.SetItemText(0, 2, L"hello world"); // 第一行的第三列内容
封装函数部分1:
void CAPTFilecheckDlg::GUI_ShowMessage(bool bIsOK, CString strMsg)
{
CString strIsOK, strTime;
CTime t = CTime::GetCurrentTime();
strTime = t.Format("%H:%M:%S");
if (bIsOK)
{
strIsOK = "执行成功";
}
else {
strIsOK = "执行失败";
}
m_CList_MESSAGE.InsertItem(0, strIsOK);
m_CList_MESSAGE.SetItemText(0, 1, strTime);
m_CList_MESSAGE.SetItemText(0, 2, strMsg);
}
调用方法:
GUI_ShowMessage(true, L"mesage");
GUI_ShowMessage(true, L"mesage1");
GUI_ShowMessage(true, L"mesage2");
2、菜单控件
-
新建菜单
【资源视图】-【添加资源】-【Menu】 -
控件增加右键的说明
【删除单条】
【删除全部】
【增加内容】 -
添加右键菜单
在【List Control】控件右键属性找到【NM_RCLICK】,双击绑定右键事件,根据需要再增加改动。
void CMFCClistDlg::OnNMRClickList1(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
// TODO: 在此添加控件通知处理程序代码
CMenu popup;
popup.LoadMenu(IDR_MENU1);
CMenu* pM = popup.GetSubMenu(0);
CPoint p;
GetCursorPos(&p);
int count = pM->GetMenuItemCount();
//如果没有选中
if (m_List_xxx.GetSelectedCount() == 0)
{
for (int i = 2; i < count; i++)
{ //菜单全部变灰
pM->EnableMenuItem(i, MF_BYPOSITION | MF_DISABLED | MF_GRAYED);
}
}
pM->TrackPopupMenu(TPM_LEFTALIGN, p.x, p.y, this);
*pResult = 0;
}
- 绑定事件
在每个安全事件上添加事件处理程序,在消息类型【COMMAND】、类列表【xxxxDlg】,指定事件函数比如删除单条就绑定【GUI_Dir_Delete()】、删除全部就绑定【GUI_Dir_DeleteALL_Items()】。
-
- 右键删除单行功能实现,GetItemCount()/DeleteItem():
删除单条要先获取选中的条目,然后遍历
void CMFCClistDlg::GUI_Dir_Delete()
{
// TODO: 在此添加命令处理程序代码
int i, iState;
// 当前选择的条目
int nItemSelected = m_List_xxx.GetSelectedCount();
// 检索在列表视图控件中的项的数目。
int nItemCount = m_List_xxx.GetItemCount();
// 如果没有选择值,就退出这个函数,不执行删除判断。
if (nItemSelected < 1)
return;
// 遍历当前所有的列,找到选中的那条删除。
for (i = nItemCount - 1; i >= 0; i--)
{
iState = m_List_xxx.GetItemState(i, LVIS_SELECTED); // 获取选择状态
if (iState != 0)
{
m_List_xxx.DeleteItem(i); // 删除
}
}
}
-
- 右键删除所有行功能实现,DeleteAllItems()/SetRedraw()/RedrawWindow();
// 删除所有特征
void CMFCClistDlg::GUI_Dir_DeleteALL_Items()
{
// TODO: 在此添加命令处理程序代码
m_CList_dirlist.DeleteAllItems();
m_CList_dirlist.SetRedraw(FALSE);
//do erase and insert operation
m_CList_dirlist.SetRedraw(TRUE);
m_CList_dirlist.RedrawWindow();
}
-
- 右键增加内容功能实现
结合前面增加内容的函数,可以绑定一个【增加内容】的函数,【GUI_Add_Check_Dir()】。
函数作用主要是打开文件浏览器,然后选中路径,增加到LIST控件中。
void CMFCClistDlg::GUI_Add_Check_Dir()
{
// TODO: 在此添加命令处理程序代码
CString m_strFileOut = _T("");
TCHAR servPath[MAX_PATH];//用来存放文件夹路径
BROWSEINFO bi;
LPITEMIDLIST pidl;
bi.hwndOwner = this->m_hWnd;
bi.pidlRoot = NULL;
bi.pszDisplayName = servPath;
bi.lpszTitle = _T("选择文件路径");
bi.ulFlags = BIF_RETURNONLYFSDIRS;
bi.lpfn = NULL;
bi.lParam = NULL;
bi.iImage = NULL;
if ((pidl = SHBrowseForFolder(&bi)) != NULL)
{
//得到文件夹的全路径,不要的话,只得本文件夹名
if (SHGetPathFromIDList(pidl, servPath))
{
// 增加内容
GUI_ShowMessage(true, servPath);
}
}
// 把变量内容更新到对话框
UpdateData(FALSE);
}
0x4 总结
界面功能并不会太复杂,知道怎么得到结果,怎么插入数据和扩展就足够了。
主要功能:
CListCtrl控件使用,右键菜单功能删除单行、删除全部、增加自定义内容。
扩展性:
主要考虑在右键菜单栏增加处理函数,实现主要功能,然后得到结果返回到界面上。
0x5 参考
MSDN VS2017
https://docs.microsoft.com/zh-cn/cpp/mfc/reference/clistctrl-class?f1url=https%3A%2F%2Fmsdn.microsoft.com%2Fquery%2Fdev15.query%3FappId%3DDev15IDEF1%26l%3DZH-CN%26k%3Dk(AFXCMN%2FCListCtrl%3A%3AInsertItem)%3Bk(CListCtrl%3A%3AInsertItem)%3Bk(InsertItem)%3Bk(DevLang-C%2B%2B)%3Bk(TargetOS-Windows)%26rd%3Dtrue&view=vs-2017#insertcolumn
VS2010/MFC编程入门之二十九(常用控件:列表视图控件List Control 下)
http://www.jizhuomi.com/software/197.html