MFC 自定义网格控件

news2024/12/18 19:51:59

一、什么是 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);

	// Col
	m_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.coloradmin.cn/o/2261757.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

idea无法识别文件,如何把floder文件恢复成model

前景&#xff1a; 昨天&#xff0c;我在之前的A1214模块包下新增了一个demo类&#xff0c;然后又新建了一个A1216模块&#xff0c;写了算法题&#xff0c;后面打算用git提交&#xff0c;发现之前的A1214模块下的demo类和新建的模块源文件都已经被追踪了&#xff0c;都是绿色的&…

2024三掌柜赠书活动第三十六期:深度学习高手笔记系列

目录 前言 理解深度学习基础 数据预处理技巧 关于《深度学习高手笔记》 编辑推荐 内容简介 作者简介 图书目录 媒体评论 《深度学习高手笔记》全书速览 结束语 前言 不用多讲&#xff0c;近两年的技术圈关于AI相关的技术讨论层出不穷&#xff0c;而深度学习作为人工…

【技术干货】移动SDK安全风险及应对策略

移动SDK&#xff08;软件开发工具包&#xff09;已经成为应用开发中不可或缺的一部分。通过SDK&#xff0c;开发者能够快速集成分析、广告调度、音视频处理、社交功能和用户身份验证等常见功能&#xff0c;而无需从零开始构建。这不仅能节省时间和资源&#xff0c;还能提高开发…

【一文概述】常见的几种内外网数据交换方案介绍

一、内外网数据交换的核心需求 内外网数据交换的需求核心在于“安全、效率、合规”&#xff0c;而应用场景的多样性使得不同企业需要定制化的解决方案。通过结合业务特性和安全等级要求&#xff0c;企业能够选择适合的技术方案来实现高效、安全的内外网数据交换。 1、数据安全…

【Linux 篇】Docker 容器星河与镜像灯塔:Linux 系统下解锁应用部署奇幻征程

文章目录 【Linux 篇】Docker 容器星河与镜像灯塔&#xff1a;Linux 系统下解锁应用部署奇幻征程前言一 、docker上部署mysql1. 拉取mysql镜像2. 创建容器3. 远程登录mysql 二 、docker上部署nginx1. 拉取nginx镜像2. 在dockerTar目录下 上传nginx.tar rz命令3. 创建nginx容器4…

Pytorch | 从零构建Vgg对CIFAR10进行分类

Pytorch | 从零构建Vgg对CIFAR10进行分类 CIFAR10数据集Vgg网络结构特点性能应用影响 Vgg结构代码详解结构代码代码详解特征提取层 _make_layers前向传播 forward 训练和测试训练代码train.py测试代码test.py训练过程和测试结果 代码汇总vgg.pytrain.pytest.py 前面文章我们构建…

实战 | 某院校小程序记录

更多大厂面试经验的视频分享看主页和专栏 目录&#xff1a; 前言&#xff1a; 渗透思路 1.绕过前端 2.信息泄露 3.爆破用户账号密码 4.信息泄露2 结束 前言&#xff1a; 遇到一个学校小程序的站点&#xff0c;只在前端登录口做了校验&#xff0c;后端没有任何校验&#x…

k8s kubernetes

文章目录 CGroupk8s运行时k8s组件k8s组件安装kubeadm命令kubectl命令k8s官网代码 CGroup 在 Linux 上&#xff0c;控制组&#xff08;CGroup&#xff09;用于限制分配给进程的资源。kubelet 和底层容器运行时都需要对接控制组来强制执行 为 Pod 和容器管理资源 并为诸如 CPU、…

uniapp中vuex(全局共享)的应用

一、Vuex概述 1.1 官方解释 Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。 它采用集中式存储管理 应用的所有组件的状态&#xff0c;并以相应的规则保证状态以一种可预测的方式发生变化 - Vuex 也集成到 Vue 的官方调试工具 devtools extension&#xff0c;提供了诸…

React简单入门 - [Next.js项目] - 页面跳转、AntD组件、二级目录等

须知 1Next.js 官网(英文)Next.js by Vercel - The React Framework2Next.js 文档(中文)简介 | Next.js 中文文档3React官网(中文)https://react.docschina.org/learn4Ant Design组件总览组件总览 - Ant Design5tailwindcss类名大全 官网英Justify Content - TailwindCS…

【十进制整数转换为其他进制数——短除形式的贪心算法】

之前写过一篇用贪心算法计算十进制转换二进制的方法&#xff0c;详见&#xff1a;用贪心算法计算十进制数转二进制数&#xff08;整数部分&#xff09;_短除法求二进制-CSDN博客 经过一段时间的研究&#xff0c;本人又发现两个规律&#xff1a; 1、不仅仅十进制整数转二进制可…

企业内训|阅读行业产品运营实战训练营-某运营商数字娱乐公司

近日&#xff0c;TsingtaoAI公司为某运营商旗下数字娱乐公司组织的“阅读行业产品运营实战训练营”在杭州落下帷幕。此次训练营由TsingtaoAI资深互联网产品专家程靖主持。该公司的业务骨干——来自内容、市场、业务、产品与技术等跨部门核心岗位、拥有8-10年实战经验的中坚力量…

pinctrl子系统学习笔记

一、背景 cpu的gpio引脚可以复用成多个功能&#xff0c;如可以配置成I2C或者普通GPIO模式。配置方式一般是通过写引脚复用的配置寄存器&#xff0c;但是不同芯片厂商配置寄存器格式内容各不相同&#xff0c;设置引脚复用无法做到通用且自由的配置&#xff0c;只能在启动初始化…

免费开源了一个图床工具 github-spring-boot-starter

文章目录 第一步&#xff0c;新建一个SpringBoot项目第二步&#xff0c;在pom文件里面引入jar包第三步&#xff0c;配置你的github信息github.authorization1、进入github官网&#xff0c;登录账号&#xff0c;点击头像&#xff0c;选择setting2、选择[Developer Settings](htt…

JVM系列之内存区域

每日禅语 有一位年轻和尚&#xff0c;一心求道&#xff0c;多年苦修参禅&#xff0c;但一直没有开悟。有一天&#xff0c;他打听到深山中有一古寺&#xff0c;住持和尚修炼圆通&#xff0c;是得道高僧。于是&#xff0c;年轻和尚打点行装&#xff0c;跋山涉水&#xff0c;千辛万…

自动驾驶AVM环视算法--python版本的俯视碗型投影模式

c语言版本和算法原理的可以查看本人的其他文档。《自动驾驶AVM环视算法--3D碗型投影模式的exe测试工具》本文档进用于展示部分代码的视线&#xff0c;获取方式网盘自行获取&#xff08;非免费介意勿下载&#xff09;&#xff1a;链接: https://pan.baidu.com/s/1STjUd87_5wDk_C…

【并发容器】源码级ConcurrentHashMap详解(java78)

1. ConcurrentHashMap 为什么要使用ConcurrentHashmap 在多线程的情况下&#xff0c;使用HashMap是线程不安全的。另外可以使用Hashtable&#xff0c;其是线程安全的&#xff0c;但是Hashtable的运行效率很低&#xff0c;之所以效率低下主要是因为其实现使用了synchronized关…

程序设计考题汇总(四:SQL练习)

文章目录 查询结果限制返回行数 查询结果限制返回行数 select device_id from user_profile LIMIT 2;

Alan Chhabra:MongoDB AI应用程序计划(MAAP) 为客户提供价值

MongoDB全球合作伙伴执行副总裁 Alan Chhabra 每当有人向我问询MongoDB&#xff0c;我都会说他们很可能在不觉之间已经与MongoDB有过交集。事实上&#xff0c;包括70%财富百强在内的许多世界领先企业公司都在使用MongoDB。我们在MongoDB所做的一切都是为了服务客户&#xff0c…

centos使用mkisofs构建无人值守镜像(附官方学习文档)

安装mkisofs yum install -y mkisofs 挂载镜像并确认 并拷贝文件(/mnt 为我们的工作目录) 1.3 准备自动应答文件(保存为 ins.ks) 修改系统引导 实际上就是添加inst.ks 这个引导参数 传递应答文件 传统模式引导