基于MFC的简单计算器的实现
对于实验项目的实现,采用MFC做计算器的图形界面,然后将求解表达式的方法封装成类,放到MFC中进行调用最终呈现出最后的效果展示。
求解算数表达式解题思路
对于算数表达式的求解,最快也是最准确的求解方式就是借助计算器。当然我们也很容易联想到,计算机的内部是怎么实现的,使用了什么数据结构,采用了什么比较新颖的算法。这些问题都是很容易联想到的。接下来我们就对拿到一个算数表达式该怎么解决进行分析。
1.首先,判断该算数表达式是不是合法的。比如出现运算符是连续出现的、表达式是以小数点开头、小数点和运算符一起相继出现等等这些都是不合法的。
2. 其次,对于求解表达式采用什么数据结构,很容易能想得到的就是借助于堆栈和队列进行,也可以借助于二叉树进行
3. 还有就是如何处理运算符优先级的问题
4. 最后就是是否存在数据类型的转换,如整型转换为string或者double类型等等。
求值表达式代码的实现
1.表达式合法性的判断
- 思路
为了将在编写程序的过程中更加的简洁迅速,故将字符型单词符号转换成int型数据,其对应的转化关系如下表所示。
我们用这样的表示方式进行转换,故此我们需要新的数据结构即的方式vector<pair<string, int>>此结构类似于int,只不过这个数据类型里面包含两个数据,即string和int两种数据类型。用这种数据类型将单词符号依次转换为对应的数字序号,存储到其中。在依次转换的过程中来判断表达式是否是合法的。参考博客.单词符号 对应数字 + 1 - 2 * 3 / 4 数字 5 ( 6 ) 7 - 代码
#include <stack>
#include <cstring>
#include <vector>
#include <utility>
#include <iostream>
using namespace std;
int word_analysis(vector<pair<string, int>>& word, const string s)
{
for (int i = 0; i < s.length(); i++) //遍历
{
//如果是运算符则加入vector容器
if (s[i] == '(' || s[i] == ')' || s[i] == '+' || s[i] == '-' || s[i] == '*' || s[i] == '/')
{
//判断此时是括号时,再有两个不能出现的算数运算符
if (s[i] == '(')
{
if (s[i + 1] == ')' || s[i + 1] == '+' || s[i + 1] == '-' || s[i + 1] == '*' || s[i + 1] == '/')
return -1;
}
if (s[i] == ')')
{
if (s[i - 1] == '(' || s[i - 1] == '+' || s[i - 1] == '-' || s[i - 1] == '*' || s[i - 1] == '/')
return -1;
}
//此时不是括号是左右两边不能是存在算数运算符
if (s[i] != ')'&&s[i] != '(')
{
if (s[i - 1] == '+' || s[i - 1] == '-' || s[i - 1] == '*' || s[i - 1] == '/')
return -1;
}
string temp;
temp.push_back(s[i]);
switch (s[i])
{
case '+':
word.push_back(make_pair(temp, 1));
break;
case '-':
word.push_back(make_pair(temp, 2));
break;
case '*':
word.push_back(make_pair(temp, 3));
break;
case '/':
word.push_back(make_pair(temp, 4));
break;
case '(':
word.push_back(make_pair(temp, 6));
break;
case ')':
word.push_back(make_pair(temp, 7));
break;
}
}
else if (s[i] >= '0'&&s[i] <= '9')
{
string temp;
//如果是数字就是直接输入,但不是只有一个数字
while (s[i] >= '0'&&s[i] <= '9')
{
temp.push_back(s[i]);
++i;
}
//表达式前面的数字输入完成时
if (s[i] == '.')
{
++i; //如果是小数点就直接跳过
if (s[i] >= '0'&&s[i] <= '9')
{
temp.push_back('.');
while (s[i] >= '0'&&s[i] <= '9')
{
temp.push_back(s[i]);
++i;
}
}
//表示 . 后面不是数字表示表达式是错误的
else return -1;
}
word.push_back(make_pair(temp, 5));
--i;
}
//表达式是以数字开头,表示表达式时错误的
else return -1;
}
return 0;
}
int main()
{
vector<pair<string, int>> word; //pair<string,int>表示一个数据类型,这个类型有两个变量
string s = "(1.5+5.789)*82-10/2";
int err_num = word_analysis(word, s);
if (err_num == -1)
{
cout << "表达式输出错误!" << endl;
}
else cout << "表达式没有错误" << endl;
return 0;
}
运行结果展示:
2.表达存储过程及计算过程
- 思路
对于合法表达式,我们采用将中缀表达式转换成后缀表达式,此过程接触堆栈和队列进行,然后将后缀表达式通过堆栈进行求值。 - 实现代码
1)中缀转后缀
#include <iostream>
#include <string>
#include <stack>
using namespace std;
// 获取运算符的优先级
int is_prior(char c)
{
switch (c)
{
case '+':
case '-':
return 1;
case '*':
case '/':
return 2;
default:
return 0;
}
}
// 判断是否是运算符
bool is_Operator(char c)
{
switch (c)
{
case '+':
case '-':
case '*':
case '/':
return true;
default:
return false;
}
}
// 中缀转后缀
string getPostfix(const string& s)
{
string b; // 输出
stack<char> s1; // 操作符栈
for (int i = 0; i < s.size(); ++i) //遍历字符串进行中缀转后缀
{
char c = s[i];
if (is_Operator(c))
{
while (!s1.empty() && is_Operator(s1.top()) && is_prior(s1.top()) >= is_prior(c))
{
b.push_back(s1.top());
s1.pop();
}
s1.push(c);
}
else if (c == '(')
{
s1.push(c);
}
else if (c == ')')
{
while (s1.top() != '(')
{
b.push_back(s1.top());
s1.pop();
}
s1.pop();
}
else
{
b.push_back(c);
}
}
while (!s1.empty())
{
b.push_back(s1.top());
s1.pop();
}
return b;
}
int main()
{
string s = "a+b*c+(d*e+f)*g";
string postfix = getPostfix(s);
cout << s << endl << postfix << endl;
return 0;
}
测试结果展示:
2)后缀求值
后缀表达是转换完成之后就进行最终的求解运算
void popTwoNumbers(stack<int>& s, int& first, int& second)
{
first = s.top();
s.pop();
second = s.top();
s.pop();
}
// 计算后缀表达式的值
int expCalculate(const string& postfix)
{
int first, second;
stack<int> s;
for (int i = 0; i < postfix.size(); ++i)
{
char c = postfix[i];
switch (c)
{
case '+':
popTwoNumbers(s, first, second);
s.push(second + first);
break;
case '-':
popTwoNumbers(s, first, second);
s.push(second - first);
break;
case '*':
popTwoNumbers(s, first, second);
s.push(second*first);
break;
case '/':
popTwoNumbers(s, first, second);
s.push(second / first);
break;
default:
s.push(c - '0');
break;
}
}
int result = s.top();
s.pop();
return result;
}
最终结合上述步骤进行测试:
int main()
{
string expr = "5+2*(6-3)-4/2";
string postfix = getPostfix(expr);
int result = expCalculate(postfix);
cout << "The result is: " << result << endl;
return 0;
}
结果如图所示:
最后将上述所有代码封装成一个类,一个是.h文件一个.cpp文件添加到MFC工程中即可
MFC界面的设计
基础设置
使用工具箱中button和Edit Control按钮进行设计一个最初的模板。如图所示:
代码如下:
再次之前需要把编辑器窗口添加一个mEdit变量,目的是可以更好的使用。
void CMFCApplication1Dlg::OnEnChangeEdit1()
{
// TODO: 如果该控件是 RICHEDIT 控件,它将不
// 发送此通知,除非重写 CDialogEx::OnInitDialog()
// 函数并调用 CRichEditCtrl().SetEventMask(),
// 同时将 ENM_CHANGE 标志“或”运算到掩码中。
// TODO: 在此添加控件通知处理程序代码
static CFont Sfont;
static CFont Bfont;
//CFont font;//不加static 貌似改变不了字体大小
Sfont.DeleteObject();
Sfont.CreatePointFont(200, "隶书");
//Bfont.CreatePointFont(150, "宋体");
GetDlgItem(IDC_EDIT1)->SetFont(&Sfont);
//GetDlgItem(IDC_BUTTON1)->SetFont(&Bfont);
}
void CMFCApplication1Dlg::OnBnClickedButton5()
{
// TODO: 在此添加控件通知处理程序代码
CString cs;
mEdit.GetWindowText(cs); //获取
cs = cs + _T("5");
mEdit.SetWindowText(cs);
}
void CMFCApplication1Dlg::OnBnClickedButton3()
{
// TODO: 在此添加控件通知处理程序代码
CString cs;
mEdit.GetWindowText(cs); //获取
cs = cs + _T("3");
mEdit.SetWindowText(cs);
}
CString opt;//表示运算符号
int num1;
int num2;
int result;
void CMFCApplication1Dlg::OnBnClickedButton1()
{
// TODO: 在此添加控件通知处理程序代码
CString cs;
mEdit.GetWindowText(cs); //获取
cs = cs + _T("1");
mEdit.SetWindowText(cs);
}
void CMFCApplication1Dlg::OnBnClickedButton2()
{
// TODO: 在此添加控件通知处理程序代码
CString cs;
mEdit.GetWindowText(cs); //获取
cs = cs + _T("2");
mEdit.SetWindowText(cs);
}
void CMFCApplication1Dlg::OnBnClickedButton4()
{
// TODO: 在此添加控件通知处理程序代码
CString cs;
mEdit.GetWindowText(cs); //获取
cs = cs + _T("4");
mEdit.SetWindowText(cs);
}
void CMFCApplication1Dlg::OnBnClickedButton6()
{
// TODO: 在此添加控件通知处理程序代码
CString cs;
mEdit.GetWindowText(cs); //获取
cs = cs + _T("6");
mEdit.SetWindowText(cs);
}
void CMFCApplication1Dlg::OnBnClickedButton7()
{
// TODO: 在此添加控件通知处理程序代码
CString cs;
mEdit.GetWindowText(cs); //获取
cs = cs + _T("7");
mEdit.SetWindowText(cs);
}
void CMFCApplication1Dlg::OnBnClickedButton8()
{
// TODO: 在此添加控件通知处理程序代码
CString cs;
mEdit.GetWindowText(cs); //获取
cs = cs + _T("8");
mEdit.SetWindowText(cs);
}
void CMFCApplication1Dlg::OnBnClickedButton9()
{
// TODO: 在此添加控件通知处理程序代码
CString cs;
mEdit.GetWindowText(cs); //获取
cs = cs + _T("9");
mEdit.SetWindowText(cs);
}
void CMFCApplication1Dlg::OnBnClickedButton11()
{
// TODO: 在此添加控件通知处理程序代码
//CString cs;
//mEdit.GetDlgItemText(IDC_EDIT1, cs); //获取
//num1 = _tstoi(cs);//字符串转换成整数
//opt = "+";
//SetDlgItemText(IDC_EDIT1,_T(" ")); //清空屏幕
CString cs;
mEdit.GetWindowText(cs); //获取
cs = cs + _T("+");
mEdit.SetWindowText(cs);
}
void CMFCApplication1Dlg::OnBnClickedButton12()
{
// TODO: 在此添加控件通知处理程序代码
// 更改背景颜色
CString str;
mEdit.GetWindowText(str);
CFile file;
file.Open("D://2.txt", CFile::modeCreate | CFile::modeNoTruncate | CFile::modeWrite);
// 写入文件
//memset(WriteBuf, 'a', sizeof(WriteBuf));
file.SeekToEnd();
file.Write(str, strlen(str));
file.Write(_T("\r\n"), 2);
// 关闭文件
file.Close();
CT2CA pszConvertedAnsiString(str);
string exp_str(pszConvertedAnsiString); // 从 LPCSTR 构造 string
if (exp_str != " ")
{
Expression e(exp_str);
if (e.test())
{
string tmp;
stringstream ss;
ss << e.calculate();
ss >> tmp;
str = tmp.c_str();
}
else
{
str = "输入错误";
}
mEdit.SetWindowText(str);
CFile file;
file.Open("D://2.txt", CFile::modeCreate | CFile::modeNoTruncate | CFile::modeWrite);
file.SeekToEnd();
file.Write(str, strlen(str));
file.Write(_T("\r\n"), 2);
// 关闭文件
file.Close();
}
}
void CMFCApplication1Dlg::OnBnClickedButton13()
{
// TODO: 在此添加控件通知处理程序代码
mEdit.SetWindowText(_T(""));
}
void CMFCApplication1Dlg::OnBnClickedButton14()
{
// TODO: 在此添加控件通知处理程序代码
CString cs;
mEdit.GetWindowText(cs); //获取信息
cs.Delete(cs.GetLength() - 1);
mEdit.SetWindowText(cs); //显示信息
}
void CMFCApplication1Dlg::OnBnClickedButton19()
{
// TODO: 在此添加控件通知处理程序代码
CString cs;
mEdit.GetWindowText(cs); //获取
cs = cs + _T("(");
mEdit.SetWindowText(cs);
}
void CMFCApplication1Dlg::OnBnClickedButton20()
{
// TODO: 在此添加控件通知处理程序代码
CString cs;
mEdit.GetWindowText(cs); //获取
cs = cs + _T(")");
mEdit.SetWindowText(cs);
}
void CMFCApplication1Dlg::OnBnClickedButton10()
{
// TODO: 在此添加控件通知处理程序代码
CString cs;
mEdit.GetWindowText(cs); //获取
cs = cs + _T("-");
mEdit.SetWindowText(cs);
}
void CMFCApplication1Dlg::OnBnClickedButton17()
{
// TODO: 在此添加控件通知处理程序代码
CString cs;
mEdit.GetWindowText(cs); //获取
cs = cs + _T("*");
mEdit.SetWindowText(cs);
}
void CMFCApplication1Dlg::OnBnClickedButton18()
{
// TODO: 在此添加控件通知处理程序代码
CString cs;
mEdit.GetWindowText(cs); //获取
cs = cs + _T("/");
mEdit.SetWindowText(cs);
}
void CMFCApplication1Dlg::OnBnClickedButton16()
{
// TODO: 在此添加控件通知处理程序代码
CString cs;
mEdit.GetWindowText(cs); //获取
cs = cs + _T(".");
mEdit.SetWindowText(cs);
}
void CMFCApplication1Dlg::OnBnClickedButton15()
{
// TODO: 在此添加控件通知处理程序代码
CString cs;
mEdit.GetWindowText(cs); //获取
cs = cs + _T("0");
mEdit.SetWindowText(cs);
}
void CMFCApplication1Dlg::OnEnChangeEdit2()
{
// TODO: 如果该控件是 RICHEDIT 控件,它将不
// 发送此通知,除非重写 CDialogEx::OnInitDialog()
// 函数并调用 CRichEditCtrl().SetEventMask(),
// 同时将 ENM_CHANGE 标志“或”运算到掩码中。
// TODO: 在此添加控件通知处理程序代码
static CFont Sfont;
static CFont Bfont;
//CFont font;//不加static 貌似改变不了字体大小
Sfont.DeleteObject();
Sfont.CreatePointFont(200, "隶书");
//Bfont.CreatePointFont(150, "宋体");
GetDlgItem(IDC_EDIT1)->SetFont(&Sfont);
}
void CMFCApplication1Dlg::OnBnClickedButton21()
{
// TODO: 在此添加控件通知处理程序代码
//CString cs;
//GetDlgItemText(IDC_EDIT1, cs);
//SetDlgItemText(IDC_EDIT2, cs);
CFile SourceFile;//数据文件
CString SourceData;//定义一临时变量保存一条记录
CString strtmp;
CFileException ex;
SourceFile.Open("D://2.txt", CFile::modeRead | CFile::shareDenyWrite, &ex);
CArchive ar(&SourceFile, CArchive::load);
while (NULL != ar.ReadString(SourceData))//循环读取文件,直到文件结束
{
strtmp += SourceData + "\r\n";
if (SourceData == "")
continue;//跳过文件头部的提示信息
}
SetDlgItemText(IDC_EDIT2, strtmp);
printf("\r\n");
}
HBRUSH CMFCApplication1Dlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr = CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor);
if (pWnd->GetDlgCtrlID() == (IDC_BUTTON3)) //主界面
{
//pDC->SetBkMode(TRANSPARENT);
pDC->SetTextColor(RGB(30, 100, 20)); //设置文本颜色
pDC->SetBkColor(RGB(200, 230, 215)); //字体背景色
return HBRUSH(GetStockObject(HOLLOW_BRUSH));
}
// TODO: 在此更改 DC 的任何特性
// TODO: 如果默认的不是所需画笔,则返回另一个画笔
return hbr;
}
void CMFCApplication1Dlg::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
CDialogEx::OnDrawItem(nIDCtl, lpDrawItemStruct);
CDC dc;
dc.Attach(lpDrawItemStruct->hDC);//得到绘制的设备环境CDC
ASSERT(lpDrawItemStruct->CtlType == ODT_BUTTON);
CString strText;
((CButton *)GetDlgItem(nIDCtl))->GetWindowText(strText);
SetBkMode(lpDrawItemStruct->hDC, TRANSPARENT);//透明
if (nIDCtl == IDC_BUTTON3)
{
if (GetDlgItem(IDC_BUTTON3)->IsWindowEnabled()) //当按钮不操作 & 按钮可用
{
CBrush brush(RGB(30, 200, 255)); //内背景画刷
CPen m_BoundryPen(0, 2, RGB(80, 80, 80)); //边框画笔
CBrush m_BackgroundBrush = RGB(140, 200, 255); //大背景画刷
dc.FillRect(&(lpDrawItemStruct->rcItem), &m_BackgroundBrush);//利用画刷brush,填充大矩形框
CRect rect = lpDrawItemStruct->rcItem;
CDC *pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
POINT pt;
//画按钮的外边框,它是一个半径为10的圆角矩形
pt.x = 10;
pt.y = 10;
CPen* hOldPen = pDC->SelectObject(&m_BoundryPen);
pDC->RoundRect(&rect, pt);
pDC->SelectObject(hOldPen);
rect.DeflateRect(3, 3, 3, 3); //缩进
CBrush *pOldBrush = pDC->SelectObject(&m_BackgroundBrush);
pDC->Rectangle(rect); //画矩形
pDC->SelectObject(pOldBrush);
pDC->FillRect(rect, &brush); //填充内矩形
//因为这里进行了重绘,所以文字也要重绘
DrawText(lpDrawItemStruct->hDC, strText, strText.GetLength(), &lpDrawItemStruct->rcItem, DT_SINGLELINE | DT_VCENTER | DT_CENTER);
}
if (lpDrawItemStruct->itemState&ODS_SELECTED & GetDlgItem(IDC_BUTTON3)->IsWindowEnabled()) //如果按钮可用 & 点击
{
CBrush brush(RGB(0, 160, 230)); //内背景画刷
CPen m_BoundryPen(0, 1, RGB(80, 80, 80)); //边框画笔
CBrush m_BackgroundBrush = RGB(140, 200, 255); //大背景画刷
dc.FillRect(&(lpDrawItemStruct->rcItem), &m_BackgroundBrush);//利用画刷brush,填充矩形框
CRect rect = lpDrawItemStruct->rcItem;
CDC *pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
POINT pt;
//画按钮的外边框,它是一个半径为5的圆角矩形
pt.x = 10;
pt.y = 10;
CPen* hOldPen = pDC->SelectObject(&m_BoundryPen);
pDC->RoundRect(&rect, pt);
pDC->SelectObject(hOldPen);
rect.DeflateRect(4, 4, 3, 3);
CBrush *pOldBrush = pDC->SelectObject(&m_BackgroundBrush);
pDC->Rectangle(rect);
pDC->SelectObject(pOldBrush);
pDC->FillRect(rect, &brush);
//因为这里进行了重绘,所以文字也要重绘
DrawText(lpDrawItemStruct->hDC, strText, strText.GetLength(), &lpDrawItemStruct->rcItem, DT_SINGLELINE | DT_VCENTER | DT_CENTER);
}
if (!GetDlgItem(IDC_BUTTON3)->IsWindowEnabled()) //不可用,显示灰色
{
CBrush brush(RGB(190, 190, 200)); //内背景画刷
CPen m_BoundryPen(0, 2, RGB(80, 80, 80)); //边框画笔
CBrush m_BackgroundBrush = RGB(140, 200, 255); //大背景画刷
dc.FillRect(&(lpDrawItemStruct->rcItem), &m_BackgroundBrush);//利用画刷brush,填充矩形框
CRect rect = lpDrawItemStruct->rcItem;
CDC *pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
POINT pt;
//画按钮的外边框,它是一个半径为5的圆角矩形
pt.x = 10;
pt.y = 10;
CPen* hOldPen = pDC->SelectObject(&m_BoundryPen);
pDC->RoundRect(&rect, pt);
pDC->SelectObject(hOldPen);
rect.DeflateRect(3, 3, 3, 3);
CBrush *pOldBrush = pDC->SelectObject(&m_BackgroundBrush);
pDC->Rectangle(rect);
pDC->SelectObject(pOldBrush);
pDC->FillRect(rect, &brush);
//因为这里进行了重绘,所以文字也要重绘
pDC->SetTextColor(RGB(235, 255, 255)); //设置文本颜色
DrawText(lpDrawItemStruct->hDC, strText, strText.GetLength(), &lpDrawItemStruct->rcItem, DT_SINGLELINE | DT_VCENTER | DT_CENTER);
}
dc.Detach();
}
}
初始效果图如下:
功能的添加
1.历史记录的功能
- 思路
将计算数据编辑框中的数据一个写入txt文本之中保存下载,然后当点击历史记录功能时,在另外一个编辑框中调用此txt文档,然后进行显示 - 实现代码
在历史记录控件设置代码:
void CMFCApplication1Dlg::OnBnClickedButton21()
{
// TODO: 在此添加控件通知处理程序代码
//CString cs;
//GetDlgItemText(IDC_EDIT1, cs);
//SetDlgItemText(IDC_EDIT2, cs);
CFile SourceFile;//数据文件
CString SourceData;//定义一临时变量保存一条记录
CString strtmp;
CFileException ex;
SourceFile.Open("D://2.txt", CFile::modeRead | CFile::shareDenyWrite, &ex);
CArchive ar(&SourceFile, CArchive::load);
while (NULL != ar.ReadString(SourceData))//循环读取文件,直到文件结束
{
strtmp += SourceData + "\r\n";
if (SourceData == "")
continue;//跳过文件头部的提示信息
}
SetDlgItemText(IDC_EDIT2, strtmp);
printf("\r\n");
}
在**=**控件设置代码如下:
void CMFCApplication1Dlg::OnBnClickedButton12()
{
// TODO: 在此添加控件通知处理程序代码
// 更改背景颜色
CString str;
mEdit.GetWindowText(str);
CFile file;
file.Open("D://2.txt", CFile::modeCreate | CFile::modeNoTruncate | CFile::modeWrite);
// 写入文件
//memset(WriteBuf, 'a', sizeof(WriteBuf));
file.SeekToEnd();
file.Write(str, strlen(str));
file.Write(_T("\r\n"), 2);
// 关闭文件
file.Close();
CT2CA pszConvertedAnsiString(str);
string exp_str(pszConvertedAnsiString); // 从 LPCSTR 构造 string
if (exp_str != " ")
{
Expression e(exp_str);
if (e.test())
{
string tmp;
stringstream ss;
ss << e.calculate();
ss >> tmp;
str = tmp.c_str();
}
else
{
str = "输入错误";
}
mEdit.SetWindowText(str);
CFile file;
file.Open("D://2.txt", CFile::modeCreate | CFile::modeNoTruncate | CFile::modeWrite);
file.SeekToEnd();
file.Write(str, strlen(str));
file.Write(_T("\r\n"), 2);
// 关闭文件
file.Close();
}
}
2.背景颜色的设置
首先将Owner Draw设置为False
然后在进行如下设置
void CMFCApplication1Dlg::OnPaint()
{
CRect rect;
CPaintDC dc(this);
GetClientRect(rect);
dc.FillSolidRect(rect, RGB(131,165,155)); //设置背景颜色
}
3.按钮颜色填充
添加加黑的两项
然后进行下列函数的赋值
HBRUSH CMFCApplication1Dlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr = CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor);
if (pWnd->GetDlgCtrlID() == (IDC_BUTTON3)) //主界面
{
//pDC->SetBkMode(TRANSPARENT);
pDC->SetTextColor(RGB(30, 100, 20)); //设置文本颜色
pDC->SetBkColor(RGB(200, 230, 215)); //字体背景色
return HBRUSH(GetStockObject(HOLLOW_BRUSH));
}
// TODO: 在此更改 DC 的任何特性
// TODO: 如果默认的不是所需画笔,则返回另一个画笔
return hbr;
}
void CMFCApplication1Dlg::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
CDialogEx::OnDrawItem(nIDCtl, lpDrawItemStruct);
CDC dc;
dc.Attach(lpDrawItemStruct->hDC);//得到绘制的设备环境CDC
ASSERT(lpDrawItemStruct->CtlType == ODT_BUTTON);
CString strText;
((CButton *)GetDlgItem(nIDCtl))->GetWindowText(strText);
SetBkMode(lpDrawItemStruct->hDC, TRANSPARENT);//透明
if (nIDCtl == IDC_BUTTON3)
{
if (GetDlgItem(IDC_BUTTON3)->IsWindowEnabled()) //当按钮不操作 & 按钮可用
{
CBrush brush(RGB(30, 200, 255)); //内背景画刷
CPen m_BoundryPen(0, 2, RGB(80, 80, 80)); //边框画笔
CBrush m_BackgroundBrush = RGB(140, 200, 255); //大背景画刷
dc.FillRect(&(lpDrawItemStruct->rcItem), &m_BackgroundBrush);//利用画刷brush,填充大矩形框
CRect rect = lpDrawItemStruct->rcItem;
CDC *pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
POINT pt;
//画按钮的外边框,它是一个半径为10的圆角矩形
pt.x = 10;
pt.y = 10;
CPen* hOldPen = pDC->SelectObject(&m_BoundryPen);
pDC->RoundRect(&rect, pt);
pDC->SelectObject(hOldPen);
rect.DeflateRect(3, 3, 3, 3); //缩进
CBrush *pOldBrush = pDC->SelectObject(&m_BackgroundBrush);
pDC->Rectangle(rect); //画矩形
pDC->SelectObject(pOldBrush);
pDC->FillRect(rect, &brush); //填充内矩形
//因为这里进行了重绘,所以文字也要重绘
DrawText(lpDrawItemStruct->hDC, strText, strText.GetLength(), &lpDrawItemStruct->rcItem, DT_SINGLELINE | DT_VCENTER | DT_CENTER);
}
if (lpDrawItemStruct->itemState&ODS_SELECTED & GetDlgItem(IDC_BUTTON3)->IsWindowEnabled()) //如果按钮可用 & 点击
{
CBrush brush(RGB(0, 160, 230)); //内背景画刷
CPen m_BoundryPen(0, 1, RGB(80, 80, 80)); //边框画笔
CBrush m_BackgroundBrush = RGB(140, 200, 255); //大背景画刷
dc.FillRect(&(lpDrawItemStruct->rcItem), &m_BackgroundBrush);//利用画刷brush,填充矩形框
CRect rect = lpDrawItemStruct->rcItem;
CDC *pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
POINT pt;
//画按钮的外边框,它是一个半径为5的圆角矩形
pt.x = 10;
pt.y = 10;
CPen* hOldPen = pDC->SelectObject(&m_BoundryPen);
pDC->RoundRect(&rect, pt);
pDC->SelectObject(hOldPen);
rect.DeflateRect(4, 4, 3, 3);
CBrush *pOldBrush = pDC->SelectObject(&m_BackgroundBrush);
pDC->Rectangle(rect);
pDC->SelectObject(pOldBrush);
pDC->FillRect(rect, &brush);
//因为这里进行了重绘,所以文字也要重绘
DrawText(lpDrawItemStruct->hDC, strText, strText.GetLength(), &lpDrawItemStruct->rcItem, DT_SINGLELINE | DT_VCENTER | DT_CENTER);
}
if (!GetDlgItem(IDC_BUTTON3)->IsWindowEnabled()) //不可用,显示灰色
{
CBrush brush(RGB(190, 190, 200)); //内背景画刷
CPen m_BoundryPen(0, 2, RGB(80, 80, 80)); //边框画笔
CBrush m_BackgroundBrush = RGB(140, 200, 255); //大背景画刷
dc.FillRect(&(lpDrawItemStruct->rcItem), &m_BackgroundBrush);//利用画刷brush,填充矩形框
CRect rect = lpDrawItemStruct->rcItem;
CDC *pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
POINT pt;
//画按钮的外边框,它是一个半径为5的圆角矩形
pt.x = 10;
pt.y = 10;
CPen* hOldPen = pDC->SelectObject(&m_BoundryPen);
pDC->RoundRect(&rect, pt);
pDC->SelectObject(hOldPen);
rect.DeflateRect(3, 3, 3, 3);
CBrush *pOldBrush = pDC->SelectObject(&m_BackgroundBrush);
pDC->Rectangle(rect);
pDC->SelectObject(pOldBrush);
pDC->FillRect(rect, &brush);
//因为这里进行了重绘,所以文字也要重绘
pDC->SetTextColor(RGB(235, 255, 255)); //设置文本颜色
DrawText(lpDrawItemStruct->hDC, strText, strText.GetLength(), &lpDrawItemStruct->rcItem, DT_SINGLELINE | DT_VCENTER | DT_CENTER);
}
dc.Detach();
}
}
最终效果图展示如下: