文章目录
一、前言
-
开发环境:Microsoft Visual Studio Community 2022 (64 位) - Preview
-
安装MFC模板:参考
-
要求:能处理线、矩形和椭圆的加载和保存工作,要能有颜色和线宽信息。
-
最终项目结构
二、创建项目
-
选择MFC应用
-
设置项目名称:Test3
-
设置应用程序类型和项目样式
三、编写代码
1.创建自己的形状类
- 头文件:Test3Shape.h
得到
输入代码:
#pragma once
class Test3Shape : public CObject
{
DECLARE_SERIAL(Test3Shape)
private:
double up;
double down;
double left;
double right;
int body;
COLORREF col_area;
COLORREF col_line;
int line_width;
bool isOk;
public:
Test3Shape(); // 无参构造
Test3Shape(double up, double down, double left, double right, int body, COLORREF col_area, COLORREF col_line, int line_width); // 构造函数
virtual void Serialize(CArchive& ar);
void Draw(CDC* pDC); // 绘图函数
void IsSelect(double x, double y); // 判断选择函数
bool CD(); // 返回选择状态函数
void NoSelect(); // 取消选择状态函数
void MoveUP(); // 向上移动
void MoveDOWN(); // 向下移动
void MoveLEFT(); // 向左移动
void MoveRight(); // 向右移动
double GetUP(); // 返回up值
double GetDPWN(); // 返回down值
double GetLEFT(); // 返回left值
double GetRIGHT(); // 返回right值
int GetBODY(); // 返回body值
COLORREF GetCOL_AREA(); // 返回填充颜色
COLORREF GetCOL_LINE(); // 返回描边颜色
int GetLINE_WIDTH(); // 返回线宽
void ad(); // 个数+
void da(); // 个数-
};
- 源文件:Test3Shape.cpp
得到
输入代码:
#include "pch.h"
#include "windef.h"
#include "Test3Shape.h"
IMPLEMENT_SERIAL(Test3Shape, CObject, 1)
Test3Shape::Test3Shape() {}
Test3Shape::Test3Shape(double x, double y, double m, double n, int z, COLORREF c, COLORREF c0, int w) // 构造
{
up = x;
down = y;
left = m;
right = n;
body = z;
col_area = c;
col_line = c0;
line_width = w;
isOk = false;
}
void Test3Shape::Serialize(CArchive& ar)
{
if (ar.IsStoring()) { //序列化,保存信息
ar << up << down << left << right << body << col_area << col_line << line_width << isOk;
}
else //反序列化,读取信息
{
ar >> up >> down >> left >> right >> body >> col_area >> col_line >> line_width >> isOk;
}
}
void Test3Shape::Draw(CDC* pDC) // 绘画
{
CBrush NBrush(col_area);
CPen NPen(PS_SOLID, line_width, col_line);
pDC->SelectObject(NBrush);
pDC->SelectObject(NPen);
switch (body)
{
case 1:
{
pDC->MoveTo(left, up);
pDC->LineTo(right, down);
break;
}
case 2:
{
pDC->Ellipse(left, down, right, up);
break;
}
case 3:
{
pDC->Rectangle(left, down, right, up);
break;
}
}
}
void Test3Shape::IsSelect(double x, double y) // 选择
{
switch (body)
{
case 1: // 直线
{
TRACE("直线n: %p\n");
TRACE("直线n: %p\n");
double k = (up - down) / (left - right);
double b = up - k * left;
if (y == k * x + b)
isOk = true;
break;
}
case 2: // 椭圆
{
double a = (right - left) / 2, b = (up - down) / 2;
a = a > 0 ? a : a * -1;
b = b > 0 ? b : b * -1;
double c, Clx, Cly, Crx, Cry, o_l;
if (a > b)
{
c = sqrt(pow(a, 2) - pow(b, 2));
Clx = (left + right) / 2 - c, Crx = (left + right) / 2 + c, Cly = Cry = (up + down) / 2;
o_l = sqrt(pow(x - Clx, 2) + pow(y - Cly, 2)) + sqrt(pow(x - Crx, 2) + pow(y - Cry, 2));
if (o_l <= 2 * a)
isOk = true;
}
else
{
c = sqrt(pow(b, 2) - pow(a, 2));
Cly = (up + down) / 2 - c, Cry = (up + down) / 2 + c, Clx = Crx = (left + right) / 2;
o_l = sqrt(pow(x - Clx, 2) + pow(y - Cly, 2)) + sqrt(pow(x - Crx, 2) + pow(y - Cry, 2));
if (o_l <= 2 * b)
isOk = true;
}
break;
}
case 3: // 矩形
{
if (y >= up && y <= down && x >= left && x <= right)
isOk = true;
break;
}
}
}
bool Test3Shape::CD() // 返回选中状态
{
return isOk;
}
void Test3Shape::NoSelect() // 取消选中状态
{
isOk = false;
}
void Test3Shape::MoveUP()
{
up -= 10;
down -= 10;
}
void Test3Shape::MoveDOWN()
{
up += 10;
down += 10;
}
void Test3Shape::MoveLEFT()
{
left -= 10;
right -= 10;
}
void Test3Shape::MoveRight()
{
left += 10;
right += 10;
}
double Test3Shape::GetUP() { return up; }
double Test3Shape::GetDPWN() { return down; }
double Test3Shape::GetLEFT() { return left; }
double Test3Shape::GetRIGHT() { return right; }
int Test3Shape::GetBODY() { return body; }
COLORREF Test3Shape::GetCOL_AREA() { return col_area; }
COLORREF Test3Shape::GetCOL_LINE() { return col_line; }
int Test3Shape::GetLINE_WIDTH() { return line_width; }
void Test3Shape::ad() { up++; }
void Test3Shape::da() { up--; }
2.Doc类
- 头文件:Test3Doc.h
输入代码:
// Test3Doc.h: CTest3Doc 类的接口
//
#pragma once
#include "Test3Shape.h"
class CTest3Doc : public CDocument
{
protected: // 仅从序列化创建
CTest3Doc() noexcept;
DECLARE_DYNCREATE(CTest3Doc)
// 特性
public:
CArray<Test3Shape*, Test3Shape*> ShapeArray;
// 操作
public:
// 重写
public:
virtual BOOL OnNewDocument();
virtual void Serialize(CArchive& ar);
#ifdef SHARED_HANDLERS
virtual void InitializeSearchContent();
virtual void OnDrawThumbnail(CDC& dc, LPRECT lprcBounds);
#endif // SHARED_HANDLERS
// 实现
public:
virtual ~CTest3Doc();
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif
protected:
// 生成的消息映射函数
protected:
DECLARE_MESSAGE_MAP()
#ifdef SHARED_HANDLERS
// 用于为搜索处理程序设置搜索内容的 Helper 函数
void SetSearchContent(const CString& value);
#endif // SHARED_HANDLERS
};
- 源文件:Test3Doc.cpp
输入代码:
// Test3Doc.cpp: CTest3Doc 类的实现
//
#include "pch.h"
#include "framework.h"
// SHARED_HANDLERS 可以在实现预览、缩略图和搜索筛选器句柄的
// ATL 项目中进行定义,并允许与该项目共享文档代码。
#ifndef SHARED_HANDLERS
#include "Test3.h"
#endif
#include "Test3Doc.h"
#include <propkey.h>
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// CTest3Doc
IMPLEMENT_DYNCREATE(CTest3Doc, CDocument)
BEGIN_MESSAGE_MAP(CTest3Doc, CDocument)
END_MESSAGE_MAP()
// CTest3Doc 构造/析构
CTest3Doc::CTest3Doc() noexcept
{
// TODO: 在此添加一次性构造代码
}
CTest3Doc::~CTest3Doc()
{
for (int i = 0; i < ShapeArray.GetSize(); ++i)
{
Test3Shape* pShape = ShapeArray.GetAt(i);
delete pShape;
}
}
BOOL CTest3Doc::OnNewDocument()
{
if (!CDocument::OnNewDocument())
return FALSE;
// TODO: 在此添加重新初始化代码
// (SDI 文档将重用该文档)
return TRUE;
}
// CTest3Doc 序列化
void CTest3Doc::Serialize(CArchive& ar)
{
if (ar.IsStoring())
{
// TODO: 在此添加存储代码
for (int i = 0; i < ShapeArray.GetSize(); i++)
{
Test3Shape* CS = ShapeArray[i];
CS->Serialize(ar);
}
}
else
{
// TODO: 在此添加加载代码
ShapeArray.RemoveAll(); // 清空数组
Test3Shape* CS = new Test3Shape();
CS->Serialize(ar);
ShapeArray.Add(CS); // 添加元素到数组
int num = ShapeArray[0]->GetUP();
for (int i = 0; i < num; i++)
{
Test3Shape* CS2 = new Test3Shape();
CS2->Serialize(ar);
ShapeArray.Add(CS2); // 添加元素到数组
}
}
}
#ifdef SHARED_HANDLERS
// 缩略图的支持
void CTest3Doc::OnDrawThumbnail(CDC& dc, LPRECT lprcBounds)
{
// 修改此代码以绘制文档数据
dc.FillSolidRect(lprcBounds, RGB(255, 255, 255));
CString strText = _T("TODO: implement thumbnail drawing here");
LOGFONT lf;
CFont* pDefaultGUIFont = CFont::FromHandle((HFONT) GetStockObject(DEFAULT_GUI_FONT));
pDefaultGUIFont->GetLogFont(&lf);
lf.lfHeight = 36;
CFont fontDraw;
fontDraw.CreateFontIndirect(&lf);
CFont* pOldFont = dc.SelectObject(&fontDraw);
dc.DrawText(strText, lprcBounds, DT_CENTER | DT_WORDBREAK);
dc.SelectObject(pOldFont);
}
// 搜索处理程序的支持
void CTest3Doc::InitializeSearchContent()
{
CString strSearchContent;
// 从文档数据设置搜索内容。
// 内容部分应由“;”分隔
// 例如: strSearchContent = _T("point;rectangle;circle;ole object;");
SetSearchContent(strSearchContent);
}
void CTest3Doc::SetSearchContent(const CString& value)
{
if (value.IsEmpty())
{
RemoveChunk(PKEY_Search_Contents.fmtid, PKEY_Search_Contents.pid);
}
else
{
CMFCFilterChunkValueImpl *pChunk = nullptr;
ATLTRY(pChunk = new CMFCFilterChunkValueImpl);
if (pChunk != nullptr)
{
pChunk->SetTextValue(PKEY_Search_Contents, value, CHUNK_TEXT);
SetChunkValue(pChunk);
}
}
}
#endif // SHARED_HANDLERS
// CTest3Doc 诊断
#ifdef _DEBUG
void CTest3Doc::AssertValid() const
{
CDocument::AssertValid();
}
void CTest3Doc::Dump(CDumpContext& dc) const
{
CDocument::Dump(dc);
}
#endif //_DEBUG
// CTest3Doc 命令
3.View类
头文件:Test3View.h
- 添加按钮
-
输入代码
// Test3View.h: CTest3View 类的接口 // #pragma once class CTest3View : public CView { protected: // 仅从序列化创建 CTest3View() noexcept; DECLARE_DYNCREATE(CTest3View) // 特性 public: CTest3Doc* GetDocument() const; // 操作 public: int num = 0; int numColour = 0; int numLineWidth = 0; bool ff = false; double m_u; double m_d; double m_l; double m_r; double m_x; double m_y; COLORREF col_in = RGB(0, 0, 0); COLORREF col_out = RGB(0, 0, 0); int lineWidth = 0; Test3Shape* CS; void DeleteSelect(double x, double y); // 删除选择函数 Test3Shape* Choose(double x, double y); // 选择函数 // 重写 public: virtual void OnDraw(CDC* pDC); // 重写以绘制该视图 virtual BOOL PreCreateWindow(CREATESTRUCT& cs); protected: // 实现 public: virtual ~CTest3View(); #ifdef _DEBUG virtual void AssertValid() const; virtual void Dump(CDumpContext& dc) const; #endif protected: // 生成的消息映射函数 protected: DECLARE_MESSAGE_MAP() public: afx_msg void CreateLine(); afx_msg void CreateRCircle(); afx_msg void CreateRectangle(); afx_msg void ChooseBlack(); afx_msg void ChooseRed(); afx_msg void ChooseBlue(); afx_msg void lineWidth1pc(); afx_msg void lineWidth5pc(); afx_msg void lineWidth10pc(); afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags); afx_msg void OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags); afx_msg void OnLButtonDown(UINT nFlags, CPoint point); afx_msg void OnLButtonUp(UINT nFlags, CPoint point); afx_msg void OnMouseMove(UINT nFlags, CPoint point); afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); }; #ifndef _DEBUG // Test3View.cpp 中的调试版本 inline CTest3Doc* CTest3View::GetDocument() const { return reinterpret_cast<CTest3Doc*>(m_pDocument); } #endif
源文件:Test3View.cpp
- 类导向
找到上述的 ID_32771 ~ ID_32779 ,对每个进行添加,输入函数名,一一对应。
函数名 | 命令ID |
---|---|
ChooseBlack | ID_32774 |
ChooseBlue | lD_32776 |
ChooseRed | lD_32775 |
CreateLine | ID_32771 |
CreateRCircle | ID_32772 |
CreateRectangle | ID_32773 |
lineWidth10pc | ID_32779 |
lineWidth1pc | ID_32777 |
lineWidth5pc | ID_32778 |
- 输入代码:
// Test3View.cpp: CTest3View 类的实现
//
#include "pch.h"
#include "framework.h"
// SHARED_HANDLERS 可以在实现预览、缩略图和搜索筛选器句柄的
// ATL 项目中进行定义,并允许与该项目共享文档代码。
#ifndef SHARED_HANDLERS
#include "Test3.h"
#endif
#include "Test3Doc.h"
#include "Test3View.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// CTest3View
IMPLEMENT_DYNCREATE(CTest3View, CView)
BEGIN_MESSAGE_MAP(CTest3View, CView)
ON_COMMAND(ID_32771, &CTest3View::CreateLine)
ON_COMMAND(ID_32772, &CTest3View::CreateRCircle)
ON_COMMAND(ID_32773, &CTest3View::CreateRectangle)
ON_COMMAND(ID_32774, &CTest3View::ChooseBlack)
ON_COMMAND(ID_32775, &CTest3View::ChooseRed)
ON_COMMAND(ID_32776, &CTest3View::ChooseBlue)
ON_COMMAND(ID_32777, &CTest3View::lineWidth1pc)
ON_COMMAND(ID_32778, &CTest3View::lineWidth5pc)
ON_COMMAND(ID_32779, &CTest3View::lineWidth10pc)
ON_WM_KEYDOWN()
ON_WM_KEYUP()
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONUP()
ON_WM_MOUSEMOVE()
ON_WM_CREATE()
END_MESSAGE_MAP()
// CTest3View 构造/析构
CTest3View::CTest3View() noexcept
{
// TODO: 在此处添加构造代码
}
CTest3View::~CTest3View()
{
}
BOOL CTest3View::PreCreateWindow(CREATESTRUCT& cs)
{
// TODO: 在此处通过修改
// CREATESTRUCT cs 来修改窗口类或样式
return CView::PreCreateWindow(cs);
}
// CTest3View 绘图
void CTest3View::DeleteSelect(double x, double y)
{
CTest3Doc* pDoc = GetDocument();
for (int i = 0; i < pDoc->ShapeArray.GetSize(); ++i)
{
Test3Shape* pShape = pDoc->ShapeArray.GetAt(i);
pShape->IsSelect(x, y);
if (pShape->CD())
{
pDoc->ShapeArray.RemoveAt(i);
pDoc->ShapeArray[0]->da();
}
}
}
Test3Shape* CTest3View::Choose(double x, double y)
{
CTest3Doc* pDoc = GetDocument();
Test3Shape* pShape = NULL;
for (int i = 0; i < pDoc->ShapeArray.GetSize(); ++i)
{
pShape = pDoc->ShapeArray.GetAt(i);
pShape->IsSelect(x, y);
}
return pShape;
}
void CTest3View::OnDraw(CDC* pDC)
{
CTest3Doc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
// TODO: 在此处为本机数据添加绘制代码
for (int i = 0; i < pDoc->ShapeArray.GetSize(); ++i)
{
Test3Shape* pShape = (pDoc->ShapeArray).GetAt(i);
pShape->Draw(pDC);
}
}
// CTest3View 诊断
#ifdef _DEBUG
void CTest3View::AssertValid() const
{
CView::AssertValid();
}
void CTest3View::Dump(CDumpContext& dc) const
{
CView::Dump(dc);
}
CTest3Doc* CTest3View::GetDocument() const // 非调试版本是内联的
{
ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CTest3Doc)));
return (CTest3Doc*)m_pDocument;
}
#endif //_DEBUG
// CTest3View 消息处理程序
void CTest3View::CreateLine()
{
// TODO: 在此添加命令处理程序代码
num = 1;
}
void CTest3View::CreateRCircle()
{
// TODO: 在此添加命令处理程序代码
num = 2;
}
void CTest3View::CreateRectangle()
{
// TODO: 在此添加命令处理程序代码
num = 3;
}
void CTest3View::ChooseBlack()
{
// TODO: 在此添加命令处理程序代码
numColour = 1;
}
void CTest3View::ChooseRed()
{
// TODO: 在此添加命令处理程序代码
numColour = 2;
}
void CTest3View::ChooseBlue()
{
// TODO: 在此添加命令处理程序代码
numColour = 3;
}
void CTest3View::lineWidth1pc()
{
// TODO: 在此添加命令处理程序代码
numLineWidth = 1;
}
void CTest3View::lineWidth5pc()
{
// TODO: 在此添加命令处理程序代码
numLineWidth = 2;
}
void CTest3View::lineWidth10pc()
{
// TODO: 在此添加命令处理程序代码
numLineWidth = 3;
}
void CTest3View::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
switch (nChar)
{
case VK_DELETE:
{
DeleteSelect(m_x, m_y);
InvalidateRect(NULL);
break;
}
case VK_CONTROL:
{
CS = Choose(m_x, m_y);
ff = true;
break;
}
case VK_UP:
{
if (ff)
{
CS->MoveUP();
CS->NoSelect();
InvalidateRect(NULL);
}
break;
}
case VK_DOWN:
{
if (ff)
{
CS->MoveDOWN();
CS->NoSelect();
InvalidateRect(NULL);
}
break;
}
case VK_LEFT:
{
if (ff)
{
CS->MoveLEFT();
CS->NoSelect();
InvalidateRect(NULL);
}
break;
}
case VK_RIGHT:
{
if (ff)
{
CS->MoveRight();
CS->NoSelect();
InvalidateRect(NULL);
}
break;
}
}
CView::OnKeyDown(nChar, nRepCnt, nFlags);
}
void CTest3View::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
switch (nChar)
{
case VK_CONTROL:
{
ff = false;
break;
}
}
CView::OnKeyUp(nChar, nRepCnt, nFlags);
}
void CTest3View::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
m_u = point.y;
m_l = point.x;
CView::OnLButtonDown(nFlags, point);
}
void CTest3View::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
m_d = point.y;
m_r = point.x;
if (num != 0)
{
CTest3Doc* pDoc = GetDocument();
Test3Shape* pShape;
if (numColour == 1) // 设置填充颜色为黑色
col_in = RGB(0, 0, 0);
if (numColour == 2) // 设置填充颜色为红色
col_in = RGB(255, 0, 0);
if (numColour == 3) // 设置填充颜色为蓝色
col_in = RGB(0, 0, 255);
if (numLineWidth == 1)
lineWidth = 1; // 设置线宽为1像素
if (numLineWidth == 2)
lineWidth = 5; // 设置线宽为5像素
if (numLineWidth == 3)
lineWidth = 10; // 设置线宽为10像素
if (num == 1)
pShape = new Test3Shape(m_u, m_d, m_l, m_r, num, col_in, col_out, lineWidth);
else
pShape = new Test3Shape(min(m_u, m_d), max(m_u, m_d), min(m_l, m_r), max(m_l, m_r), num, col_in, col_out, lineWidth);
pDoc->ShapeArray.Add(pShape);
pDoc->ShapeArray[0]->ad();
InvalidateRect(NULL);
}
CView::OnLButtonUp(nFlags, point);
}
void CTest3View::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
m_x = point.x;
m_y = point.y;
CView::OnMouseMove(nFlags, point);
}
int CTest3View::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CView::OnCreate(lpCreateStruct) == -1)
return -1;
// TODO: 在此添加您专用的创建代码
Test3Shape* pShape = new Test3Shape(0, 0, 0, 0, 99999, col_in, col_out, lineWidth);
CTest3Doc* pDoc = GetDocument();
pDoc->ShapeArray.Add(pShape);
return 0;
}
4.保存文件带后缀名
比如保存的文件带后缀名line,假设项目名为XXX:
在资源视图中,XXX工程下的文件夹XXX.rc里面的String Table文件夹下的String Table,双击打开。找到IDR MAINFRAMETM,其值:
-
默认:Test3\n\nTest3\n\n\nTest3.Document\nTest3.Document
-
改为:Test3\n\nTest3\n.line\n.line\nTest3.Document\nTest3.Document
四、运行结果
对绘制文档进行保存,文件扩展名为.line,如下所示:
成功打开保存的.line绘图文档如下所示:
五、MFC项目文件的介绍
在Microsoft Foundation Class (MFC)库中,Doc.h
、View.h
、Doc.cpp
和View.cpp
是常见的文件名,通常与应用程序的文档(Document)和视图(View)部分相关。下面简要解释一下它们各自的作用:
- Doc.h:
- 这是文档类的头文件。通常,这个类定义了一个应用程序所处理的数据的结构。它也可能包含与数据持久性(例如,保存和加载到文件)相关的代码。
- 文档类通常与一个视图类关联,这样视图可以显示文档的内容并与之交互。
- View.h:
- 这是视图类的头文件。视图类定义了用户界面(UI),用户可以通过这个界面与文档交互。例如,如果文档是一个文本编辑器,视图可能就是一个文本编辑控件。
- 视图类通常包含用于处理用户输入(如按键和鼠标移动)的代码,以及用于更新显示内容的代码。
- Doc.cpp:
- 这是文档类的实现文件。它包含了在
Doc.h
中声明的类的实现代码。这包括数据结构的定义、数据持久性方法(如保存和加载到文件),以及其他与文档管理相关的逻辑。
- 这是文档类的实现文件。它包含了在
- View.cpp:
- 这是视图类的实现文件。它包含了在
View.h
中声明的类的实现代码,包括UI元素的更新、用户输入的处理等。
- 这是视图类的实现文件。它包含了在
在MFC应用程序中,通常会有多个视图类与一个文档类相关联,以支持多个UI线程或同时从多个窗口查看文档的不同部分。此外,MFC还提供了框架代码来处理文档和视图的生命周期,以及它们之间的通信,从而简化了Windows应用程序的开发。