MFC 自定义网格控件

news/2024/12/19 13:48:34/

一、什么是 Custom Control?

Custom Control(自定义控件) 是 MFC(Microsoft Foundation Classes)框架中提供的一种控件类型,用于实现自定义的外观和功能。当标准控件(例如 CEditCButtonCListCtrl 等)无法满足特定需求时,可以使用 Custom Control 来实现个性化的控件。

Custom Control 的核心特点:

  • 基于 CWnd:通过继承 CWnd 类,实现完全自定义的绘制和行为。
  • 灵活性高:开发者可以自定义控件的绘制外观、消息响应、用户交互等。
  • 适用于特殊需求:如自定义按钮、进度条、图表、绘图区域等。

二、Custom Control 的使用方法

1. 在资源编辑器中添加 Custom Control

  • 打开 资源视图,编辑对应的对话框模板。
  • 从工具箱中选择 Custom Control,并放置到对话框上。
  • 在控件的 属性窗口 中:
    • Class:指定自定义控件的类名(如 MyCustomControl)。
    • ID:设置控件的唯一标识符(如 IDC_MY_CUSTOM)。

2. 创建自定义控件类

要实现自定义控件,需要创建一个继承自 CWnd 的类,并重写消息处理函数。

示例代码:

头文件:MyCustomControl.h

#pragma once
#include "afxwin.h"class CMyCustomControl : public CWnd
{
public:CMyCustomControl();virtual ~CMyCustomControl();protected:DECLARE_MESSAGE_MAP()afx_msg void OnPaint();                // 自定义绘制afx_msg void OnLButtonDown(UINT nFlags, CPoint point); // 鼠标点击事件
};

实现文件:MyCustomControl.cpp

#include "pch.h"
#include "MyCustomControl.h"BEGIN_MESSAGE_MAP(CMyCustomControl, CWnd)ON_WM_PAINT()ON_WM_LBUTTONDOWN()
END_MESSAGE_MAP()CMyCustomControl::CMyCustomControl() {}CMyCustomControl::~CMyCustomControl() {}void CMyCustomControl::OnPaint()
{CPaintDC dc(this); // 设备上下文CRect rect;GetClientRect(&rect);dc.FillSolidRect(rect, RGB(240, 240, 240)); // 背景颜色dc.SetTextColor(RGB(0, 0, 255));           // 文本颜色dc.DrawText(_T("自定义控件示例"), &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
}void CMyCustomControl::OnLButtonDown(UINT nFlags, CPoint point)
{AfxMessageBox(_T("自定义控件被点击!"));CWnd::OnLButtonDown(nFlags, point);
}

3. 在对话框类中绑定 Custom Control

在对话框类中,将自定义控件绑定到资源中的 Custom Control。

示例代码:

头文件:MyDialog.h

#include "MyCustomControl.h"class CMyDialog : public CDialogEx
{...
private:CMyCustomControl m_myCustomControl; // 自定义控件对象
};

DoDataExchange 绑定控件:

void CMyDialog::DoDataExchange(CDataExchange* pDX)
{CDialogEx::DoDataExchange(pDX);DDX_Control(pDX, IDC_MY_CUSTOM, m_myCustomControl); // 控件绑定
}

4. 在代码中动态创建 Custom Control

除了在资源编辑器中定义,Custom Control 也可以在代码中动态创建:

BOOL CMyDialog::OnInitDialog()
{CDialogEx::OnInitDialog();CRect rect(10, 10, 200, 50); // 控件位置和大小m_myCustomControl.Create(NULL, WS_CHILD | WS_VISIBLE, rect, this, 1234);return TRUE;
}

三、CGridCtrl 简介

CGridCtrl 是基于 Custom Control 的 MFC 自定义网格控件,通常用于显示表格数据。它是 CodeProject 社区提供的开源组件,可以实现类似 Excel 风格的表格功能。

CGridCtrl 官网:https://www.codeproject.com/Articles/8/MFC-Grid-control#History

CGridCtrl 的核心功能包括:

  1. 行列管理:动态添加、删除行列。
  2. 单元格管理:设置单元格文本、背景色、字体、边框等。
  3. 选择模式:支持单行选择、多行选择、单元格选择等。
  4. 数据编辑:单元格支持编辑功能,启用列表模式。
  5. 只读单元格:特定单元格可设置为只读,防止用户修改。
  6. 自适应列宽:支持自动扩展列宽适配内容。
  7. 高亮与排序:自动高亮选中行,并支持表格数据排序。

四、CGridCtrl 使用注意事项

1. SetItemTextSetRowCount 的关系

调用 SetItemText 函数设置单元格内容时,必须确保指定的行索引不超过当前的 SetRowCount 设置的行数。如果超出设置的行数范围,单元格内容将不会显示,也不会自动扩展行数。

2. 调用 ExpandColumnsToFitExpandLastColumn

在填充表格数据时,如果表格出现滚动条,默认情况下最后一列的宽度可能无法正确填充,导致内容显示不全或者异常。为确保显示正常,必须调用:

  • ExpandColumnsToFit:调整所有列的宽度,使其适应当前表格的可视区域。
  • ExpandLastColumn:将最后一列扩展到表格的剩余宽度,确保视觉上的完整性。

五、CGridCtrl 特殊单元格类型使用

1. 设置下拉框单元格(CGridCellCombo)

通过设置单元格类型为 CGridCellCombo,可以实现在单元格中显示下拉框,用户可选择选项。

示例代码:

if (m_gridUserManager.SetCellType(i, 3, RUNTIME_CLASS(CGridCellCombo))) {CGridCellCombo* pCell = static_cast<CGridCellCombo*>(m_gridUserManager.GetCell(i, 3));pCell->SetOptions(permissions); // 设置下拉框选项列表pCell->SetStyle(CBS_DROPDOWNLIST); // 设置为只可选择的下拉框
}
  • SetOptions:设置下拉框的选项内容。
  • SetStyle(CBS_DROPDOWNLIST):设置为下拉选择模式,用户不能输入自由文本。

2. 设置复选框单元格(CGridCellCheck)

通过设置单元格类型为 CGridCellCheck,可以在单元格中显示复选框,用户可勾选。

示例代码:

if (m_grid.SetCellType(nRowIndex, 1, RUNTIME_CLASS(CGridCellCheck))) {auto* pCell = static_cast<CGridCellCheck*>(m_grid.GetCell(nRowIndex, 1));pCell->SetCheck(TRUE); // 设置复选框为选中状态
}
  • SetCheck(TRUE):设置复选框的初始状态为选中。
  • 通过 SetCheck(FALSE) 可以将复选框状态设置为未选中。

注意事项:

  • 在调用 SetCellType 之前,需要确保表格已设置足够的行列数。
  • 通过 RUNTIME_CLASS 设置单元格类型时,需要确保所用的单元格类(如 CGridCellComboCGridCellCheck)已经包含在项目中。

六、CGridCtrl 使用示例

1. 初始化表格控件

Custom Control 指定自定义控件的类名(MFCGridCtrl)。
以下代码展示如何初始化一个 CGridCtrl 控件,创建 4 列表格,并设置标题和基础样式:

void CRecipeListDlg::InitRecipeLise()
{if (m_grid.GetSafeHwnd() == NULL) {return;}int nRows = 1;int nCols = 4;int nFixRows = 1;int nFixCols = 0;int nRowIdx = 0;int nColIdx = 0;m_grid.DeleteAllItems();m_grid.SetVirtualMode(FALSE);m_grid.GetDefaultCell(TRUE, FALSE)->SetBackClr(g_nGridFixCellColor); // 设置固定行背景色m_grid.GetDefaultCell(FALSE, TRUE)->SetBackClr(g_nGridFixCellColor); // 设置固定列背景色m_grid.GetDefaultCell(FALSE, FALSE)->SetBackClr(g_nGridCellColor);	 // 设置单元格背景色m_grid.SetFixedTextColor(g_nGridFixFontColor);						 // 设置固定行列字体颜色m_grid.SetRowCount(nRows);m_grid.SetColumnCount(nCols);m_grid.SetFixedRowCount(nFixRows);m_grid.SetFixedColumnCount(nFixCols);// Colm_grid.SetColumnWidth(nColIdx, 10);m_grid.SetItemText(nRowIdx, nColIdx++, _T("No."));m_grid.SetColumnWidth(nColIdx, 10);m_grid.SetItemText(nRowIdx, nColIdx++, _T("名称"));m_grid.SetColumnWidth(nColIdx, 50);m_grid.SetItemText(nRowIdx, nColIdx++, _T("描述"));m_grid.SetColumnWidth(nColIdx, 30);m_grid.SetItemText(nRowIdx, nColIdx++, _T("创建时间"));m_grid.SetFixedRowSelection(FALSE);     // 设置固定行不可选中m_grid.SetFixedColumnSelection(FALSE);  // 设置固定列不可选中m_grid.SetEditable(TRUE);				// 设置单元格可编辑m_grid.SetRowResize(FALSE);				// 设置行不可调整大小m_grid.SetColumnResize(TRUE);			// 设置列可调整大小m_grid.ExpandColumnsToFit(TRUE);		// 自动调整列宽,适合固定表格大小并希望所有列均匀分布的情况m_grid.SetListMode(TRUE);				// 启用列表模式m_grid.EnableSelection(TRUE);			// 启用选择m_grid.SetSingleRowSelection(TRUE);		// 自动整行高亮(限制为单行选择)m_grid.ExpandLastColumn();				// 最后一列填充网格FillRecipeLise();
}

2. 填充数据到表格

FillRecipeLise 函数实现从文件夹和文本文件读取数据,并填充到表格:

void CRecipeListDlg::FillRecipeLise()
{// 在设置行数和数据填充时,批量处理操作,避免逐行刷新表格。// 开头调用 SetRedraw(FALSE),结束后调用 SetRedraw(TRUE)。m_grid.SetRedraw(FALSE);// 动态行数检查:在清空旧数据时,确保不会越界。// 清除数据行,保留表头for (int i = m_grid.GetRowCount() - 1; i > 0; --i) {m_grid.DeleteRow(i);}// 1. 遍历文件夹下所有XML文件std::string strRecipePath = CToolUnits::getRecipePath();std::vector<CString> vecFile = CToolUnits::GetFileNamesInDirectory(strRecipePath.c_str(), _T(".xml"));// 2. 读取 RecipeList.txt 文件std::map<CString, std::pair<CString, CString>> recipeData; // {配方名, {描述, 创建时间}}std::ifstream inFile(strRecipePath + "\\RecipeList.txt");if (inFile.is_open()) {std::string line;while (std::getline(inFile, line)) {if (line.empty()) continue; // 跳过空行std::istringstream ss(line);std::string name, description, createTime;// CSV格式解析(逗号分隔)if (std::getline(ss, name, ',') &&std::getline(ss, description, ',') &&std::getline(ss, createTime)) {recipeData[CString(name.c_str())] = std::make_pair(CString(description.c_str()), CString(createTime.c_str()));}}inFile.close();}// 3. 更新表格数据int rowIdx = 1;m_grid.SetRowCount(static_cast<int>(vecFile.size()) + 1);for (const auto& fileName : vecFile) {// 从 RecipeList.txt 数据中查找对应的描述和创建时间CString description = _T("");CString createTime = _T("");auto it = recipeData.find(fileName);if (it != recipeData.end()) {description = it->second.first;  // 配方描述createTime = it->second.second;  // 创建时间}// 填充表格数据m_grid.SetItemText(rowIdx, 0, CString(std::to_string(rowIdx).c_str())); // No.m_grid.SetItemText(rowIdx, 1, fileName);								// 配方名称m_grid.SetItemText(rowIdx, 2, description);								// 配方描述m_grid.SetItemText(rowIdx, 3, createTime);								// 创建时间// 禁止编辑m_grid.SetItemState(rowIdx, 0, GVIS_READONLY);m_grid.SetItemState(rowIdx, 1, GVIS_READONLY);m_grid.SetItemState(rowIdx, 3, GVIS_READONLY);++rowIdx;}// 适合内容不固定的情况,列宽自适应内容长度更自然。m_grid.ExpandColumnsToFit(FALSE);  // 自动调整列宽m_grid.ExpandLastColumn();		   // 最后一列填充网格// 刷新网格控件m_grid.SetRedraw(TRUE);m_grid.Invalidate();m_grid.UpdateWindow();
}

七、总结

  1. Custom Control 提供了实现自定义控件的基础能力,适合高度个性化需求。
  2. CGridCtrl 是一个基于 Custom Control 的表格控件,提供了灵活的表格展示与管理功能。
  3. CGridCtrl 支持设置特殊单元格类型,如下拉框(CGridCellCombo)和复选框(CGridCellCheck)。

http://www.ppmy.cn/news/1556391.html

相关文章

ip地址暴露了怎么办?手机怎样改ip地址以保障安全

在数字化时代&#xff0c;IP地址作为我们连接互联网的“身份证”&#xff0c;其安全性至关重要。然而&#xff0c;有时我们的IP地址可能会因各种原因暴露&#xff0c;从而引发隐私泄露、网络攻击等风险。本文将为您详细解析IP地址暴露后的应对措施&#xff0c;特别是针对手机用…

python之求平面离散点集围成的面积

鞋带公式&#xff08;Shoelace Formula&#xff09;是一种计算多边形面积的数学公式&#xff0c;特别适用于已知顶点坐标的多边形。这个公式的名字来源于计算过程中的交叉相乘&#xff0c;类似于系鞋带时的交叉方式。 假设一个多边形有 个顶点&#xff0c;顶点的坐标依次为&am…

基于微信小程序的小区疫情防控ssm+论文源码调试讲解

第2章 程序开发技术 2.1 Mysql数据库 为了更容易理解Mysql数据库&#xff0c;接下来就对其具备的主要特征进行描述。 &#xff08;1&#xff09;首选Mysql数据库也是为了节省开发资金&#xff0c;因为网络上对Mysql的源码都已进行了公开展示&#xff0c;开发者根据程序开发需…

深入理解数据库 JOIN 操作

数据库中的JOIN操作是关系型数据库查询中的重要组成部分&#xff0c;它允许我们将多个表的数据结合起来&#xff0c;形成一个新的结果集。你可能已经在不同场景下使用过它&#xff0c;但今天我们将深入探讨JOIN的各种类型、实际应用以及如何高效使用它。 什么是 JOIN 操作&…

跑步训练(蓝桥杯2020试题A)

【问题描述】 小明要进行一个跑步训练。初始时&#xff0c;小明体力充沛&#xff0c;体力值计为10000。小明跑步时每分钟损耗600体力值。小明休息时每分钟增加300体力值。体力值的损耗和增加都是均匀变化的。 小明打算跑一分钟&#xff0c;休息一分钟&#xff0c;再跑一分钟&am…

【zlm】 webrtc源码讲解三(总结)

目录 setsdp onwrite ​编辑 play 参考 setsdp onwrite play 参考 【zlm】 webrtc源码讲解_zlm webrtc-CSDN博客 【zlm】 webrtc源码讲解&#xff08;二&#xff09;_webrtc 源码-CSDN博客

深度学习在日志分析中的应用:智能运维的新前沿

在现代信息技术环境中&#xff0c;系统日志记录了系统运行的详细信息&#xff0c;是保障系统稳定运行的重要数据来源。通过对日志数据的分析&#xff0c;可以及时发现系统异常和潜在问题&#xff0c;提升运维效率和系统稳定性。随着深度学习技术的迅速发展&#xff0c;深度学习…

回归预测 | MATLAB实现SVM-Adaboost集成学习结合支持向量机多输入单输出回归预测

回归预测 | MATLAB实现SVM-Adaboost集成学习结合支持向量机多输入单输出回归预测 目录 回归预测 | MATLAB实现SVM-Adaboost集成学习结合支持向量机多输入单输出回归预测基本介绍程序设计基本介绍 SVM-Adaboost集成学习是一种将支持向量机(SVM)与AdaBoost算法相结合的集成学习…