基于MFC框架用C++做一个记账本

目录

一、前言

二、主要功能和技术点

1.主要功能

2.主要技术点

三、准备工作

1.SQLite数据库操作工具

2.SqLiteCpp第三方库

3.安装office导入Excel需要的接口

3.1具体步骤

四、主界面

1.自定义窗口背景

1.1消息映射

1.2选择背景图片

1.3绘制背景

1.4静态控件透明化

2.实现统计功能

2.1消息映射

2.2数据统计

3.实现保存、详情功能

3.1消息映射

3.2保存数据到Sqlite数据库

3.3显示详情窗口

4.实现导出Excel功能

4.1消息映射

4.2导出数据到excel中

五、源码


一、前言

之前的文章一直用的是Windows的API以及ATL的框架来绘制控件的,那本次使用MFC框架来做一下小工具。

二、主要功能和技术点

1.主要功能

1)支持对数据的存储、修改以及查询。

2)可以显示本周的数据详情。

3)支持导出本周的数据以及所有的数据为EXCEL文件。

4)支持自定义窗口背景图片。

2.主要技术点

Sqlite数据库的增删改查、GDI/GDI+绘制、EXCEL接口、控件自绘、正则表达式。

三、准备工作

1.SQLite数据库操作工具

详情见《基于Windows系统用C++做一个点名工具》文章。

2.SqLiteCpp第三方库

详情见《基于Windows系统用C++做一个点名工具》文章。

3.安装office导入Excel需要的接口

3.1具体步骤

1)右击【项目】--》 点击【添加】--》点击【新建项】

2)在弹出来的窗口中选择【MFC】-->选择【TypeLib中的MFC类】,点击添加。

3)在弹出来的窗口中的【实现接口的位置】中选择【文件】,再点击【...】在你的EXCEL.EXE所在的路径点击打开。

4)按【>】添加以下七个基础接口:

_Application_Workbook_WorksheetWorksheetsWorkbooksRangeFont

点击确定,可以在头文件中看见已经已经添加完成:

5)分别打开这个七个头文件,将导入的代码注释掉。

同时把CFont类重新改一个名,因为std命名空间中也有相同的类,会冲突。

头文件名也改一下

6)在我们的程序中添加这七个头文件,编译。

7)如果编译报错,那么我们根据报错提示改一下头文件中的代码

①CRange中的VARIANT DialogBox() 改为 VARIANT _DialogBox()

②CWorksheets 和 CWorkbooks中的InvokeHelper方法如果缺少参数则用NULL填充

③出现未定义的标识符XmlMap,使用void代替。

注意:本文用到了单元格填充颜色,所以还需要按以上的方法添加Interior接口,头文件是<Cnterior.h>。

四、主界面

界面由两个窗口组成:输入窗口和数据展示窗口。

1)输入窗口由静态文本、编辑框、菜单栏以及按钮组成

2)数据展示窗口由静态文本、列表视图以及按钮组成

无论是Windows底层api还是基于mfc的框架,都是通过处理窗口消息来实现窗口以及控件的绘制。mfc框架只是在Windows底层api的基础上对其进行了封装而已,所以通过mfc封装的宏来映射窗口消息即可。因为mfc封装了大量的消息映射函数,所以大部分都不需要自己手动映射,我们只需要在控件的属性中点击【黄色小闪电的标志】,然后在下方选择要映射函数即可。

1.自定义窗口背景

1.1消息映射

映射窗口的WM_PAINT消息,进行背景绘制。

BEGIN_MESSAGE_MAP(CAccountDlg, CDialogEx)
ON_WM_PAINT()
END_MESSAGE_MAP()

1.2选择背景图片

//设置筛选器
TCHAR szFilter[] = _T("BMP图片(*.bmp)|*.bmp*|JPG图片(*.jpg)|*.jpg*|PNG图片(*.png)|*.png*|所有文件(*.)|*.*|");
CFileDialog fileDlg(TRUE, _T("doc"), _T(""), OFN_OVERWRITEPROMPT, szFilter, this);
// 构造打开文件对话框
if (IDOK == fileDlg.DoModal())
{//获取选择文件的路径m_bkPicPth = fileDlg.GetPathName();SaveToPathDB(DB_FULLPATH, m_bkPicPth);this->InvalidateRect(0, 1);this->UpdateWindow();
}

1.3绘制背景

使用GDI+用图片来绘制背景,初始状态下默认浅蓝色背景。

PAINTSTRUCT ps;CDC* pDC = BeginPaint(&ps);Gdiplus::Graphics gh(pDC->GetSafeHdc());if (m_bkPicPth.GetLength() > 0){Gdiplus::Image* image = Gdiplus::Image::FromFile(m_bkPicPth.GetString());gh.DrawImage(image, Gdiplus::RectF(ps.rcPaint.left, ps.rcPaint.top,ps.rcPaint.right - ps.rcPaint.left, ps.rcPaint.bottom - ps.rcPaint.top));}else{Gdiplus::SolidBrush brush(Gdiplus::Color(120, 200, 250));gh.FillRectangle(&brush, Gdiplus::RectF(ps.rcPaint.left, ps.rcPaint.top,ps.rcPaint.right - ps.rcPaint.left, ps.rcPaint.bottom - ps.rcPaint.top));}

1.4静态控件透明化

我们如果自绘了背景,那么静态文本的留白会让界面不那么美观,所以我们需要对静态文本控件的背景透明化。那么就需要映射处理WM_CTLCOLORSTATIC消息。但是在mfc中把所有控件的绘制背景的消息都封装成了ON_WM_CTLCOLOR宏,所以我们映射该宏,在消息处理函数中对其进行判断处理即可。

BEGIN_MESSAGE_MAP(CAccountDlg, CDialogEx)ON_WM_CTLCOLOR()
END_MESSAGE_MAP()HBRUSH CAccountDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{if (nCtlColor == CTLCOLOR_STATIC){UINT id = ::GetDlgCtrlID(pWnd->GetSafeHwnd());if(id == IDC_TODAYEDIT)return (HBRUSH)GetStockObject(WHITE_BRUSH);else{SetTextColor(pDC->GetSafeHdc(), RGB(255, 255, 255));SetBkColor(pDC->GetSafeHdc(), RGB(255, 255, 255));SetBkMode(pDC->GetSafeHdc(), TRANSPARENT);return (HBRUSH)GetStockObject(NULL_BRUSH);}}return (HBRUSH)GetStockObject(WHITE_BRUSH);
}

2.实现统计功能

2.1消息映射

响应编辑框的内容改变,映射EN_CHANGE或者EN_UPDATE消息。

BEGIN_MESSAGE_MAP(CAccountDlg, CDialogEx)ON_EN_UPDATE(IDC_BREAKFASTEDIT, &CAccountDlg::OnEnUpdateBreakfastedit)
END_MESSAGE_MAP()

2.2数据统计

//判断字符串是否为合法数字字符串
static bool IsNumCharacterStr(CString str)
{// 定义一个正则表达式,仅匹配正整数或正浮点数std::regex pattern("^\\d+(\\.)?\\d{0,2}$");std::string numStr = WCharToString(str.GetString());return std::regex_match(numStr, pattern);
}TCHAR text[100] = {};
SendDlgItemMessage(itemID, WM_GETTEXT, 100, (LPARAM)text);
if (IsNumCharacterStr(text))
{num = _wtof_l(text, NULL);
}
else
{SendDlgItemMessage(itemID, WM_SETTEXT, NULL, (LPARAM)L"0");num = 0;
}m_totalNum = m_breakfastNum + m_lunchNum + m_dinnerNum +m_fruitNum + m_elecProductNum + m_dailyProductNum +m_relaxationNum + m_medicalNum + m_petNum + m_educationNum +m_trafficNum + m_rentNum + m_otherNum;CString totalStr;
totalStr.Format(L"%.2lf", m_totalNum);
SendDlgItemMessage(IDC_TODAYEDIT, WM_SETTEXT, NULL, (LPARAM)totalStr.GetString());
if (m_totalNum >= 0.01)
{CWnd* cwnd = GetDlgItem(IDC_SAVEBUTTON);cwnd->EnableWindow();
}

3.实现保存、详情功能

3.1消息映射

响应按钮按下,映射BN_CLICKED消息。

BEGIN_MESSAGE_MAP(CAccountDlg, CDialogEx)	ON_BN_CLICKED(IDC_SAVEBUTTON, &CAccountDlg::OnBnClickedSavebutton)ON_BN_CLICKED(IDC_DETAILBUTTON, &CAccountDlg::OnBnClickedDetailbutton)
END_MESSAGE_MAP()

3.2保存数据到Sqlite数据库

//保存数据到本地数据库
static BOOL SaveToDB(CString path, ACCOUNTDATA data)
{try{std::string fullPath = UnicodeToUtf8(path.GetString());Database db(fullPath, OPEN_READWRITE);std::string sql = R"(INSERT OR REPLACE INTO ACCOUNT (week, breakfast,lunch,dinner,fruit,elecProduct,dailyProduct,relaxation,medical,pet,education,traffic,rent,other,total,date)VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?))";Statement query2(db, sql);int i = 1;//绑定数据query2.bind(i++, data.week);query2.bind(i++, data.breakfast);query2.bind(i++, data.lunch);query2.bind(i++, data.dinner);query2.bind(i++, data.fruit);query2.bind(i++, data.elecProduct);query2.bind(i++, data.dailyProduct);query2.bind(i++, data.relaxation);query2.bind(i++, data.medical);query2.bind(i++, data.pet);query2.bind(i++, data.education);query2.bind(i++, data.traffic);query2.bind(i++, data.rent);query2.bind(i++, data.other);query2.bind(i++, data.total);query2.bind(i, UnicodeToUtf8(data.date.GetString()));//执行query2.exec();}catch (const std::exception& e){MessageBoxA(NULL, e.what(), "Save to db failed", MB_OK | MB_ICONERROR);return FALSE;}return TRUE;
}if (SaveToDB(DB_FULLPATH, data))
{SelectInDB(DB_FULLPATH, m_startDate, m_endDate, m_accountVec);int size = m_accountVec.size() - 1;GetTypeTotal(m_accountVec.at(size), m_accountVec);this->MessageBox(_T("保存成功"), _T("Success"), MB_OK | MB_ICONINFORMATION);CWnd* cwnd = GetDlgItem(IDC_SAVEBUTTON);cwnd->EnableWindow(0);
}

3.3显示详情窗口

	this->ShowWindow(SW_HIDE);CDetailDlg detailDlg;float dpi = GetDpiForOwnWindow(this->GetSafeHwnd());detailDlg.Init(m_accountVec, m_curDate, m_bkPicPth, dpi);detailDlg.DoModal();this->ShowWindow(SW_SHOW);

4.实现导出Excel功能

4.1消息映射

同样是响应按钮按下的消息,映射BN_CLICKED消息。

BEGIN_MESSAGE_MAP(CDetailDlg, CDialogEx)ON_BN_CLICKED(IDC_EXPORTALLBUTTON, &CDetailDlg::OnBnClickedExportallbutton)
END_MESSAGE_MAP()

4.2导出数据到excel中

CApplication app;
CWorkbooks books;
CWorkbook book;
CWorksheets sheets;
CWorksheet sheet;
CRange range;COleVariant	covOptional((long)DISP_E_PARAMNOTFOUND, VT_ERROR);
books = app.get_Workbooks();
book = books.Add(covOptional);
sheets = book.get_Worksheets();
sheet = sheets.get_Item(COleVariant((short)1));
range.AttachDispatch(sheet.get_Cells());//得到全部单元格
int rowNum = m_mylist.GetItemCount();	//所有数据行(不包括列头)
CHeaderCtrl* pHead = m_mylist.GetHeaderCtrl();
int colNum = pHead->GetItemCount();		//所有列头//写入列头
TCHAR colName[MAX_PATH] = { 0 };
for (int col = 0; col < colNum; col++)
{range.put_Item(_variant_t((long)(1)), variant_t((long)(col + 1)),_variant_t(columnhead[col]));
}//写入数据
for (int i = 0; i < rowNum; i++)
{CString text;for (int j = 0; j < colNum; j++){//从excel的第二行开始写入列表数据。range.put_Item(_variant_t((long)(i + 2)), variant_t((long)(j + 1)),_variant_t(text.GetString()));}
}

设置指定范围的单元格背景颜色

	range.AttachDispatch(sheet.get_Range(_variant_t(rowStr), _variant_t(colStr)));Cnterior it;it.AttachDispatch(range.get_Interior());it.put_Color(_variant_t(RGB(140, 255, 170)));

注意:无论是从列表上获取数据还是从容器中获取数据保存到excel中,excel的表格行列索引都是从1开始的。

五、源码

见压缩包。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/34230.html

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

相关文章

qemu搭建aarch64

qemu工具搭建aarch64系统 下载准备 下载qemu: https://qemu.weilnetz.de/w64/2022/qemu-w64-setup-20220831.exe 下载固件&#xff1a;https://publishing-ie-linaro-org.s3.amazonaws.com/releases/components/kernel/uefi-linaro/16.02/release/qemu64/QEMU_EFI.fd?Signat…

Zookeeper3.6.3集群安装

Zookeeper3.6.3三节点集群安装 为保证集群高可用&#xff0c;Zookeeper 集群的节点数最好是奇数&#xff0c;最少有三个节点&#xff0c;所以这里搭建一个三个节点的集群。(在一个节点模拟三节点&#xff0c;真实的三节点把ip替换一下即可&#xff0c;按照hadoop案件把网络打通…

下一代 RAG 技术来了!微软正式开源 GraphRAG

省流总结 优点&#xff1a;检索准确度高 缺点&#xff1a;单个19w字构建用时4分30s、gpt4 token花费12美元 概述 7 月 2 日&#xff0c;微软开源了 GraphRAG&#xff0c;一种基于图的检索增强生成 (RAG) 方法&#xff0c;可以对私有或以前未见过的数据集进行问答。在 GitHub…

MySQL索引(四):字符串索引

前缀索引 MySQL是支持前缀索引的&#xff0c; 也就是说&#xff0c; 你可以定义字符串的一部分作为索引。 默认地&#xff0c;如果你创建索引的语句不指定前缀长度&#xff0c; 那么索引就会包含整个字符串。 使用前缀索引的优缺点&#xff1a; 1&#xff09;优点&#xff1a…

获取剪切板的图片 -> File -> Base64 -> Blob -> url -> Image,以及它们之间的各种相互转换

&#x1f9d1;‍&#x1f4bb; 写在开头 点赞 收藏 学会&#x1f923;&#x1f923;&#x1f923; 一、获取剪切板的图片&#xff08;拿到 File 对象&#xff09; js粘贴事件paste简单解析及遇到的坑 - 云社区 - 腾讯云 (tencent.com) document.addEventListener(paste, f…

实战八:模拟京东购物流程

问题描述&#xff1a; 从键盘录入5个商品信息&#xff08;1001手机&#xff09;添加到商品列表中&#xff0c;展示商品信息,提示用户选择商品&#xff0c;用户选中的商品添加到购物车中&#xff08;购物车中的商品要逆序)&#xff0c;用户选中的商品不存在需要有相应提示&#…

Selenium安装WebDriver最新Chrome驱动(含116/117/118/119)

目录 1、确认浏览器的版本 2、找到对应的chromedriver版本 3、解压chromedriver文件&#xff0c;放置chrome的安装目录下 4、设置系统属性 5、确认chromedriver是否安装成功及解决方式 1、确认浏览器的版本 在浏览器的地址栏&#xff0c;输入chrome://version/&#x…

攻防世界cat-题解

1.打开题目很想是一个命令执行&#xff0c;但是使用命令之心的语句&#xff0c;无法执行命令&#xff0c;看了wp&#xff0c;是在命令执行的时候&#xff1b;被编码了&#xff0c;我们把url%80使它溢出&#xff0c;把显示出来的代码下载到本地分析 分析代码我们可以知道这个是一…

SSM框架

目录 一. Maven入门和进阶 1.Maven简介和快速入门 (1) Maven介绍 (2) Maven的主要作用理解 ①场景概念 ②依赖管理 ③构建管理 (3)Maven安装和配置 ①软件安装 ②环境变量 ③命令测试 ④配置文件&#xff1a; ⑤idea配置本地maven 2.基于IDEA的Maven工程创建 (1…

AI 名人堂:李飞飞

Fei-Fei Li&#xff08;李飞飞&#xff09;&#xff0c;斯坦福大学计算机科学系教授&#xff0c;斯坦福人工智能实验室前主任&#xff0c;以其在人工智能领域的开创性工作而闻名。 人工智能教育的倡导者 计算机视觉领域的领军人物 ImageNet的创造者 2AGI.NET AI 教程 2025最…

基于JavaScript实现的操作系统页面置换算法程序

基于JavaScript的操作系统页面置换算法程序 1. 实验目的 页面置换算法是虚拟存储管理实现的关键&#xff0c;通过本次实验理解内存页面调度的机制&#xff0c;在模拟实现FIFO、LRU等页面置换算法的基础上&#xff0c;比较它们的效率及优缺点&#xff0c;从而了解虚拟存储实现…

JAVA-初始JAVA模块化开发

菜鸟为了巩固所写 目录 菜鸟为了巩固所写 一、概述 二、创建步骤 1、打开Intellij IDEA,创建一个名为MyJavaModuleApp的Java项目。 2、向示例项目中添加”模块描述符“文件 3、创建多模块的IntelliJ 项目 4、IntelliJ项目添加“新模块”对话框 解释1&#xff1a;模块声…

Java 中tableaw 实战教程

java中tableaw库通过简单的API实现过滤、连接、绘制和操作表格数据。支持CSV&#xff0c;数据库&#xff0c;Excel等数据源。 安装依赖 tableaw是用于分析表格数据的开源Java库&#xff0c;构建在Java 8流之上。它可以从GitHub下载&#xff0c;也可以作为Maven或Gradle项目的…

力扣206. 反转链表题解

文章目录 题目描述&#xff1a;试析&#xff1a;1.迭代法2.递归法3.双指针法4.数组法 力扣206. 反转链表 题目描述&#xff1a; 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5] …

Jdk21引入jsoup运行报错:java.lang.NoClassDefFoundError

文章目录 背景抽象类中代码问题分析尝试最终解决 背景 在富文本编译内容中&#xff0c;有些文件是存储到阿里云 oss 中的&#xff0c;所以链接做 STS 临时访问有时效性&#xff0c;每次返回的时候&#xff0c;需要通过STS来签名替换掉其中的链接访问&#xff0c;所以用到 jsoup…

Kafka 物理存储机制

优质博文&#xff1a;IT-BLOG-CN 一个商业化消息队列的性能好坏&#xff0c;其文件存储机制设计是衡量一个消息队列服务技术水平和最关键指标之一。下面将从Kafka文件存储机制和物理结构角度&#xff0c;分析Kafka是如何实现高效文件存储&#xff0c;及实际应用效果。Kafka的基…

【Linux】内核打印函数`printk`详解

在Linux内核开发过程中&#xff0c;printk是一个极其重要的函数&#xff0c;用于将信息输出到内核日志中。通过printk&#xff0c;开发者可以在内核中打印调试信息、错误信息以及其他类型的日志&#xff0c;这对于诊断问题、追踪执行流程以及监控系统状态都非常有帮助。本文将详…

校园失物招领借助 SpringBoot:塑造校园互助寻物新风尚

3系统分析 3.1可行性分析 开发者在进行开发系统之前&#xff0c;都需要进行可行性分析&#xff0c;保证该系统能够被成功开发出来。 3.1.1技术可行性 开发该校园失物招领系统所采用的技术是vue和MYSQL数据库。计算机专业的学生在学校期间已经比较系统的学习了很多编程方面的知识…

SpringCloud微服务学习笔记(二)_Docker

文章目录 docker拆封原则docker在linux上的安装部署mysql部署mysql命令解读 常见命令数据卷挂载以宿主目录自定义挂载Dockerfile语法自定义镜像容器网络互联部署java应用,后端部署前端DockerCompose(快速部署) docker 简化环境配置流程 单体架构 架构简单部署成本低团队协作成…

02.ABAP工作台介绍

总学习目录请点击下面连接 SAP ABAP开发从0到入职&#xff0c;冷冬备战-CSDN博客 目录 1.知识库和对象浏览器 目的&#xff1a; 知识库有那些对象 知识库对象结构 如何去知识库查找对象 第一个是知识库信息系统 2.知识库和对象浏览器操作 3.开发abap程序 创建新的AB…