实例详解:MFC坐标轴实现
需求:MFC坐标轴实现-----最好有步骤啊,刚刚才接触C++和MFC啊。MFC怎样在特定区域建立坐标轴,x轴自适应,y轴有固定范围,最好有网格。
解决思路:VC 内存绘图,不闪屏,具体代码如下:
// 先上传代码,在.h 文件中:
#pragma once #include <afxtempl.h> #define TEXT_AREA_WIDTH (60) ///< 文字区宽度,单位 像素 #define X_AXIS_GRAD (600) ///< X 轴刻度值 #define Y_AXIS_GRAD (50) ///< X 轴刻度值 class My_Draw : public CStatic { // 构造/析构 函数 public: My_Draw(); virtual ~My_Draw(); public: struct { unsigned char Show_Max_Grid :1; // 是否显示大网格 unsigned char Show_Min_Grid :1; // 是否显示小网格 unsigned char Draw_Enable :1; // 放大/缩小使能位 unsigned char Draw_Line_Choice :1; // 线被选择 unsigned char LButton_Down_Flag :1; // 鼠标左键按下标志 unsigned char LButton_Up_Flag :1; // 鼠标左键弹起标志 unsigned char LButton_Double_Down_Flag :1; // 鼠标左键双击按下标志 }Bool_Flag; unsigned int TextAreaWidth; int xAxisGrad,yAxisGrad; unsigned short xMaxGrad,xMinGrad; unsigned short yMaxGrad,yMinGrad; int Limit_Min,Limit_Max; POINT Mouse_Current_Point; // 鼠标当前坐标 POINT Button_Down_Point; // 记录鼠标左键按下时的坐标 POINT LButton_Double_Down_Point; // 记录鼠标左键双击按下的坐标 POINT Old_LButton_Double_Down_Point; // 画笔列表 CPen* pBrack; // 黑色画笔 CPen* pBlue; // 蓝色画笔 CPen* pYellow; // 黄色画笔 CPen* pGren; // 绿色画笔 CPen* pPink; // 紫色画笔 CPen* pRed; // 红色画笔 CPen* pGray; // 灰色画笔 CRect Draw_Size; CDC memDC; CBitmap memBitmap; CBitmap* pOldBmp; unsigned short SheetMaxH; CWnd *pWnd; void Draw(); void Drawing(CDC *pDC); // 绘制图表 void Draw_xAxis(CDC *pDC); void Draw_yAxis(CDC *pDC); void Draw_Cross_Cursor(CDC *pDC); void My_Draw::SaveBmpToFile(); protected: afx_msg void OnLButtonDown(UINT nFlags, CPoint point); afx_msg void OnMouseMove(UINT nFlags, CPoint point); afx_msg void OnLButtonUp(UINT nFlags, CPoint point); afx_msg void OnLButtonDblClk(UINT nFlags, CPoint point); DECLARE_MESSAGE_MAP() };
// .CPP 文件中:
#include "stdafx.h" #include "Draw_Static_Text.h" #include "conio.h" #include "direct.h" #ifdef _DEBUG #define new DEBUG_NEW #endif // 构造函数 My_Draw::My_Draw() { Bool_Flag.Show_Max_Grid = false; Bool_Flag.Show_Min_Grid = false; Bool_Flag.Draw_Enable = false; Bool_Flag.Draw_Line_Choice = false; Bool_Flag.LButton_Down_Flag = false; Bool_Flag.LButton_Up_Flag = false; Bool_Flag.LButton_Double_Down_Flag = false; TextAreaWidth = TEXT_AREA_WIDTH; xAxisGrad = X_AXIS_GRAD; yAxisGrad = Y_AXIS_GRAD; pBrack = new CPen(); // 黑色画笔 pBlue = new CPen(); // 蓝色画笔 pYellow = new CPen(); // 黄色画笔 pGren = new CPen(); // 绿色画笔 pRed = new CPen(); // 红色画笔 pPink = new CPen(); // 紫色画笔 pGray = new CPen(); // 灰色画笔 pBrack->CreatePen(PS_SOLID,1,RGB(0,0,0)); pBlue->CreatePen(PS_SOLID,1,RGB(0,0,255)); pYellow->CreatePen(PS_SOLID,1,RGB(155,125,0)); pGren->CreatePen(PS_SOLID,1,RGB(0,255,0)); pRed->CreatePen(PS_SOLID,1,RGB(255,0,0)); pPink->CreatePen(PS_SOLID,1,RGB(255,0,255)); pGray->CreatePen(PS_DOT,1,RGB(145,105,105)); #ifdef _DEBUG AllocConsole(); _cprintf("Debuging....\r\n"); #endif } // 析构函数 My_Draw::~My_Draw() { delete pBrack; // 黑色画笔 delete pBlue; // 蓝色画笔 delete pYellow; // 黄色画笔 delete pGren; // 绿色画笔 delete pRed; // 红色画笔 delete pPink; // 紫色画笔 delete pGray; // 灰色画笔 } BEGIN_MESSAGE_MAP(My_Draw, CStatic) ON_WM_LBUTTONDOWN() ON_WM_MOUSEMOVE() ON_WM_LBUTTONUP() ON_WM_LBUTTONDBLCLK() END_MESSAGE_MAP() void My_Draw::SaveBmpToFile() // 保存图表为 Bmp 图片 { CFileDialog dlg(false,NULL,NULL,OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, "位图文件(*.bmp)|*.bmp|",NULL); if (dlg.DoModal()!= IDOK) return; CString filename = dlg.GetFileName() + ".bmp"; // 获取绘制坐标的文本框 this->GetClientRect(&Draw_Size); // 获取窗口大小 CDC *pDC = this->GetDC(); // 获取 dc this->Invalidate(); this->UpdateWindow(); //内存绘图 memDC.CreateCompatibleDC(pDC); memBitmap.CreateCompatibleBitmap(pDC,Draw_Size.right,Draw_Size.bottom); pOldBmp = memDC.SelectObject(&memBitmap); memDC.BitBlt(Draw_Size.left,Draw_Size.top,Draw_Size.right,Draw_Size.bottom,pDC,0,0,SRCCOPY); Drawing(&memDC); // 绘制坐标 BITMAP bmp; memBitmap.GetBitmap(&bmp); // 获得位图信息 FILE *fp; fopen_s(&fp,filename, "w+b"); BITMAPINFOHEADER bih = {0}; // 位图信息头 bih.biBitCount = bmp.bmBitsPixel; // 每个像素字节大小 bih.biCompression = BI_RGB; bih.biHeight = bmp.bmHeight; // 高度 bih.biPlanes = 1; bih.biSize = sizeof(BITMAPINFOHEADER); bih.biSizeImage = bmp.bmWidthBytes * bmp.bmHeight; // 图像数据大小 bih.biWidth = bmp.bmWidth; // 宽度 BITMAPFILEHEADER bfh = {0}; // 位图文件头 bfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); // 到位图数据的偏移量 bfh.bfSize = bfh.bfOffBits + bmp.bmWidthBytes * bmp.bmHeight; // 文件总的大小 bfh.bfType = (WORD)0x4d42; fwrite(&bfh, 1, sizeof(BITMAPFILEHEADER), fp); // 写入位图文件头 fwrite(&bih, 1, sizeof(BITMAPINFOHEADER), fp); // 写入位图信息头 byte * p = new byte[bmp.bmWidthBytes * bmp.bmHeight]; // 申请内存保存位图数据 GetDIBits(memDC.m_hDC, (HBITMAP) memBitmap.m_hObject, 0, Draw_Size.Height(), p, (LPBITMAPINFO) &bih, DIB_RGB_COLORS); //获取位图数据 fwrite(p, 1, bmp.bmWidthBytes * bmp.bmHeight, fp); // 写入位图数据 delete [] p; fclose(fp); memDC.SelectObject(pOldBmp); memDC.DeleteDC(); // 释放 Dc memBitmap.DeleteObject(); } void My_Draw::Draw() { this->Invalidate(); this->UpdateWindow(); this->GetClientRect(&Draw_Size); // 获取窗口大小 CDC *pDC = this->GetDC(); // 获取 dc //内存绘图 memDC.CreateCompatibleDC(pDC); memBitmap.CreateCompatibleBitmap(pDC,Draw_Size.right,Draw_Size.bottom); pOldBmp = memDC.SelectObject(&memBitmap); memDC.BitBlt(Draw_Size.left,Draw_Size.top,Draw_Size.right,Draw_Size.bottom,pDC,0,0,SRCCOPY); Drawing(&memDC); // 绘制坐标 pDC->BitBlt(Draw_Size.left,Draw_Size.top,Draw_Size.right,Draw_Size.bottom,&memDC,0,0,SRCCOPY); memDC.SelectObject(pOldBmp); memDC.DeleteDC(); memBitmap.DeleteObject(); ReleaseDC(pDC); } void My_Draw::Drawing(CDC *pDC) { pDC->SelectObject(pBrack); // 选择当前画笔颜色 pDC->Rectangle(0,0,Draw_Size.Width(),Draw_Size.Height()); // 设定绘制范围 //绘出文字 pDC->SetTextColor(RGB(0,25,255)); pDC->SetBkMode(TRANSPARENT); pDC->TextOutA(5,180,"输"); pDC->TextOutA(5,200,"出"); pDC->TextOutA(5,220,"电"); pDC->TextOutA(5,240,"压"); pDC->TextOutA(11,260,"|"); pDC->TextOutA(5,280,"单"); pDC->TextOutA(5,300,"位"); pDC->TextOutA(3,320,"(V)"); pDC->TextOutA(Draw_Size.Width()/2-40,Draw_Size.Height()-20,"行 程--单位 (%)"); SheetMaxH = Draw_Size.Height()-TextAreaWidth; Draw_xAxis(pDC); Draw_yAxis(pDC); Draw_Cross_Cursor(pDC); ReleaseDC(pDC); } void My_Draw::Draw_xAxis(CDC *pDC) { CString StrScale; // 转换刻度字符串存放点 unsigned short drawCount; // 画王格计数 unsigned short temp; unsigned int girdSize; //绘制x轴坐标 pDC->MoveTo(TextAreaWidth,SheetMaxH); pDC->LineTo(Draw_Size.Width(),SheetMaxH); //绘制箭头 pDC->LineTo(Draw_Size.Width()-10,SheetMaxH-5); pDC->MoveTo(Draw_Size.Width(),SheetMaxH); pDC->LineTo(Draw_Size.Width()-10,SheetMaxH+5); //绘制x轴刻度 drawCount = 0; if(xAxisGrad > Draw_Size.Width()) { xAxisGrad/=10; girdSize = 10; } else girdSize = 1; xMaxGrad = int((Draw_Size.Width() - TextAreaWidth) / xAxisGrad); for(int i=TextAreaWidth; i<=Draw_Size.Width(); i+=xMaxGrad) { pDC->SelectObject(pBrack); pDC->MoveTo(i,SheetMaxH); if(xMaxGrad<2) temp = xMaxGrad*100; else temp = xMaxGrad*10; if((i-TextAreaWidth)%temp==0) { pDC->LineTo(i,SheetMaxH+10); // 整数刻度,绘制长刻度 //输出对应的文本 if(drawCount < 100) { //pDC->SelectObject(font1); //选择当前字体 StrScale.Format(" %d", drawCount*girdSize); pDC->SetTextColor(RGB(25, 155, 12)); pDC->TextOutA(i-10, SheetMaxH+10, StrScale); //修正文本显示坐标 3位数内,显示方式 if(Bool_Flag.Show_Max_Grid) { pDC->SelectObject(pGray); //使用虚线灰色画笔 pDC->MoveTo(i,SheetMaxH); pDC->LineTo(i,20); } } else { StrScale.Format(" %d", drawCount*girdSize); pDC->TextOutA(i-12, SheetMaxH+10, StrScale); //修正文本显示坐标 4位数内,显示方式 if(Bool_Flag.Show_Max_Grid) { pDC->SelectObject(pGray); //使用虚线灰色画笔 pDC->MoveTo(i, SheetMaxH); pDC->LineTo(i, 20); } } } else { pDC->LineTo(i,SheetMaxH+5); //小数刻度,绘制短刻度 } drawCount++; } ReleaseDC(pDC); } void My_Draw::Draw_yAxis(CDC *pDC) { unsigned int drawCount; CString StrScale; //转换刻度字符串存放点 drawCount = 0; //绘制y轴坐标 pDC->MoveTo(TextAreaWidth, SheetMaxH); pDC->LineTo(TextAreaWidth, 10); //绘制箭头 pDC->LineTo(TextAreaWidth-5, 20); pDC->MoveTo(TextAreaWidth, 10); pDC->LineTo(TextAreaWidth+5, 20); yMaxGrad = int(SheetMaxH / yAxisGrad); for(int i=SheetMaxH; i>=20; i-=yMaxGrad) { pDC->SelectObject(pBrack); StrScale.Format("%d", drawCount / 10); pDC->MoveTo(TextAreaWidth, i); if(drawCount % 10==0) { pDC->LineTo(TextAreaWidth-10, i); //整数刻度,绘制长刻度 if(Bool_Flag.Show_Max_Grid) { pDC->SelectObject(pGray); //使用实线灰色画笔 pDC->MoveTo(TextAreaWidth, i); pDC->LineTo(Draw_Size.Width()-10, i); } pDC->SetTextColor(RGB(255, 0, 0)); pDC->TextOutA(TextAreaWidth-25, i-6, StrScale+"V"); //输出对应的文本 } else { pDC->LineTo(TextAreaWidth-5, i); //小数刻度,绘制短刻度 } drawCount++; } ReleaseDC(pDC); } void My_Draw::Draw_Cross_Cursor(CDC *pDC) { int m_intVoltage; int m_intAngle; // 十字光标和显示坐标信息 if(Bool_Flag.LButton_Double_Down_Flag) { if((LButton_Double_Down_Point.x > TextAreaWidth) &&(LButton_Double_Down_Point.x < Draw_Size.Width()) &&(LButton_Double_Down_Point.y > 20) &&(LButton_Double_Down_Point.y < SheetMaxH)) { if(Bool_Flag.Draw_Line_Choice) pDC->SelectObject(pGren); else pDC->SelectObject(pRed); pDC->MoveTo(LButton_Double_Down_Point.x, 1); pDC->LineTo(LButton_Double_Down_Point.x, SheetMaxH-1); pDC->MoveTo(TextAreaWidth+1, LButton_Double_Down_Point.y); pDC->LineTo(Draw_Size.Width()-1,LButton_Double_Down_Point.y); Old_LButton_Double_Down_Point = LButton_Double_Down_Point; m_intVoltage = yAxisGrad; m_intVoltage = (SheetMaxH - LButton_Double_Down_Point.y)/m_intVoltage; m_intVoltage /= 10; m_intAngle = xAxisGrad; m_intAngle = (LButton_Double_Down_Point.x - TextAreaWidth)/m_intAngle; #ifdef _DEBUG _cprintf("Voltage = %0.2f,Angle = %0.2f\r\n",xAxisGrad,m_intAngle); #endif UpdateData(FALSE); } } if(Bool_Flag.LButton_Down_Flag) { pDC->SelectObject(pBrack); pDC->MoveTo(Button_Down_Point); pDC->LineTo(Mouse_Current_Point.x,Button_Down_Point.y); pDC->MoveTo(Mouse_Current_Point.x,Button_Down_Point.y); pDC->LineTo(Mouse_Current_Point); pDC->MoveTo(Mouse_Current_Point); pDC->LineTo(Button_Down_Point.x,Mouse_Current_Point.y); pDC->MoveTo(Button_Down_Point.x,Mouse_Current_Point.y); pDC->LineTo(Button_Down_Point); } ReleaseDC(pDC); } void My_Draw::OnMouseMove(UINT nFlags, CPoint point) { // TODO: 在此添加消息处理程序代码和/或调用默认值 Mouse_Current_Point = point; if(Bool_Flag.Draw_Line_Choice) { Limit_Min = point.x - 5; Limit_Max = point.x + 5; LButton_Double_Down_Point = point; } if((Bool_Flag.LButton_Down_Flag) ||(Bool_Flag.LButton_Up_Flag)) Draw(); CStatic::OnMouseMove(nFlags, point); } void My_Draw::OnLButtonDown(UINT nFlags, CPoint point) { // TODO: 在此添加消息处理程序代码和/或调用默认值 if((Bool_Flag.LButton_Double_Down_Flag) &&(point.x >= Limit_Min) &&(point.x <= Limit_Max)) { if(Bool_Flag.Draw_Line_Choice) Bool_Flag.Draw_Line_Choice = FALSE; else Bool_Flag.Draw_Line_Choice = TRUE; LButton_Double_Down_Point = point; } else { if((Bool_Flag.LButton_Down_Flag == FALSE) &&(Bool_Flag.Draw_Line_Choice == FALSE)) { Bool_Flag.LButton_Down_Flag = true; Button_Down_Point = point; } } Draw(); CStatic::OnLButtonDown(nFlags, point); } void My_Draw::OnLButtonUp(UINT nFlags, CPoint point) { // TODO: 在此添加消息处理程序代码和/或调用默认值 if(Bool_Flag.LButton_Down_Flag) { Bool_Flag.LButton_Up_Flag = true; Bool_Flag.LButton_Down_Flag = FALSE; Draw(); } CStatic::OnLButtonUp(nFlags, point); } void My_Draw::OnLButtonDblClk(UINT nFlags, CPoint point) { // TODO: 在此添加消息处理程序代码和/或调用默认值 LButton_Double_Down_Point = point; Limit_Min = LButton_Double_Down_Point.x - 5; Limit_Max = LButton_Double_Down_Point.x + 5; Bool_Flag.LButton_Double_Down_Flag = TRUE; Draw(); CStatic::OnLButtonDblClk(nFlags, point); }
程序运行效果图
现在说明一下
1. 新建一个 MFC 应用程序
2. 选择“基于对话框”
3. 添加一个 “Picture 控件”
4. 为控件添加一个变量 my_Graw
5. 将源文件包含进来,并在 My_ClassDlg.h 中包含 #include "My Draw.h"
6. 在 My_ClassDlg.h 头文件中将刚才声明的 Picture 控件变量 CStatic my_Graw ; 改成 My_Draw my_Graw;
7. 在 My_ClassDlg.cpp 文件 void CMy_ClassDlg::OnPaint() 尾部添加 my_Graw.Draw();
8. 编译运行
读者可根据自己的需要在响应的地方改下就好了。
示例下载 http://download.csdn.net/detail/longzhishen/7227255