vs2019 MFC實(shí)現(xiàn)office界面的畫圖小項(xiàng)目
vs2019安裝MFC
有許多新手不知道MFC在vs2019里的安裝選項(xiàng),其實(shí)它不會在勾選工作負(fù)載時自動默認(rèn)勾選,而通常需要人為勾選,具體安裝步驟如下:(安裝時間不是很長,網(wǎng)速夠快幾分鐘就下載完畢)
1.打開vs安裝程序

2.選擇Visual Studio擴(kuò)展開發(fā)

3.選擇C++ MFC 生成工具(x86 和 x64)進(jìn)行安裝

4.選擇修改,等待安裝

5.等待安裝完成

6.安裝完成,我們打開vs2019

7.我們創(chuàng)建新項(xiàng)目,可以看到已經(jīng)有MFC應(yīng)用這個選項(xiàng)

說明我們的MFC安裝成功~
vs2019 MFC實(shí)現(xiàn)office界面的畫圖小項(xiàng)目
一、創(chuàng)建項(xiàng)目
1.點(diǎn)擊文件-->新建-->項(xiàng)目,選擇MFC應(yīng)用,點(diǎn)擊下一步

2.項(xiàng)目名稱為Draw,點(diǎn)擊創(chuàng)建

3.可以看到有很多內(nèi)容

4.應(yīng)用程序類型有:
- 單個文檔
- 多個文檔
- 基于對話框
- 多個頂層文檔

5.項(xiàng)目樣式有:
- MFC standard
- Windows Explorer
- Visual Studio
- Office

6.這里應(yīng)用程序類型選擇多個文檔,項(xiàng)目樣式選擇Office。
視覺樣式和顏色選擇默認(rèn)的Office 2007(Blue theme)即可。
值得注意的是:Office會比默認(rèn)選擇的項(xiàng)目樣式多一個Ribbon框,后面會說到~

7.點(diǎn)擊下一步,我們來到文檔模板屬性,可以看到主框架描述和文檔類型名稱等內(nèi)容,這里可以默認(rèn)不用修改

8.下一步,來到用戶界面功能,可以看到Command bar里有三個選項(xiàng),這里我們選擇默認(rèn)的使用功能區(qū)(ribbon)

9.下一步,來到高級功能,可以直接默認(rèn)跳過

10.點(diǎn)擊下一步來到最后一步——生成的類,可以看到生成的類和類名,我們選擇默認(rèn)的App即可,這樣我們的頭文件和.cpp文件就是以項(xiàng)目名稱命名

11.點(diǎn)擊完成,之后看到左側(cè)的解決方案資源管理器,這里包含了5個內(nèi)容:
- 引用
- 外部依賴項(xiàng)
- 頭文件
- 源文件
- 資源文件
其中,我們可以在頭文件和源文件里看到生成的.h和.cpp文件

二、進(jìn)入多個文檔的控件界面
1.因?yàn)槭腔诙鄠€文檔,所以我們需要了解如何在對話框上如何添加控件。因此我們可以雙擊 項(xiàng)目名稱.rc2,進(jìn)入資源視圖;

當(dāng)然,我們也可以直接點(diǎn)擊系統(tǒng)默認(rèn)打開的底下的資源視圖選項(xiàng)(注意:不能resource.h文件同時打開?。?!)

在這里喲~
如果不小心關(guān)閉了,也可以在菜單欄里 視圖->資源視圖里重新打開該視圖
2.這里比較重要的是Menu部分,因?yàn)橛泻芏郙FC已經(jīng)內(nèi)置好的功能,例如主框架IDR_MAINFRAME里就有文件下拉菜單等選項(xiàng),我們想要添加一個新的下拉菜單,只需要在右邊的“請?jiān)诖随I入”輸入內(nèi)容,并且編寫對應(yīng)的代碼即可。但這里博主并不在這里添加畫圖的功能。

3.博主這里要在Ribbon里添加畫圖的功能

4.點(diǎn)擊右邊的工具箱(豎著的,治療頸椎~)

然后點(diǎn)擊Ribbon編輯器,可以看到有很多種類的控件

三、編寫畫圖小程序(先從畫矩形開始)
1.我們點(diǎn)擊面板,然后拖動到窗口面板的右邊,可以看到多了一個面板1

2.我們點(diǎn)擊這個面板,在屬性窗口中的外觀下面,修改Caption,把面板1改為圖形,這就算給這個面板改了名字

3.我們再選擇Ribbon工具箱里的按鈕,同樣拖動,至圖形面板,如下,名字為button1

4.修改這個按鈕的屬性中雜項(xiàng)的ID,改為ID_RECTANGLE
這里指的注意的就是這個ID,這個在MFC編程中十分重要,因?yàn)槲覀兺枰@取控件的ID號來對該控件進(jìn)行函數(shù)編寫、消息處理等操作!?。?/p>

5.既然是按鈕,那就得有觸發(fā)這個按鈕所要執(zhí)行的操作,右鍵這個按鈕,我們選擇添加事件處理程序

6.彈出以下框

7.類列表我們選擇CDrawView,點(diǎn)擊確定

8.我們可以看到在DrawView.cpp中生成的命令函數(shù)

我們也可以看到在前面的代碼中多了一個ON_COMMAND函數(shù),說明我們確實(shí)添加了這樣的一個按鈕命令(注意:ID_RECTANGLE這個ID號就是對應(yīng)的按鈕的編號,&CDrawView::OnRectangle表示這個ID的按鈕實(shí)現(xiàn)的命令函數(shù))

這里的紅波浪線提示沒有這個ID,其實(shí)我們是添加了這個按鈕的,可以在Resource.h中看到(注意打開Resource.h再去看資源試圖是打不開的,會報(bào)錯,因此需要先關(guān)掉Resource.h再去訪問資源試圖?。。。。。?/p>
#define ID_RECTANGLE 32771
這個IDE編輯器的小問題,我們可以選擇重啟vs2019打開就不會顯示未定義ID了,如下,沒有紅色波浪線了

9.現(xiàn)在的問題就是如何畫圖的問題:畫矩形,則定義一個矩形類,寫方法,在消息函數(shù)里新建對象即可;但是我們?nèi)绻粷M足只畫矩形呢?畫箭頭、三角可不可以?這個時候我們就應(yīng)該想到繼承,即建一個抽象類graph,然后派生幾個子類去完成這個功能。因此我們應(yīng)該先新建一個graph抽象類:
我們右鍵頭文件,選擇添加-->新建項(xiàng),彈出如下界面;
選擇頭文件(.h),名稱為graph.h,點(diǎn)擊確定

10.如下

11.添加如下代碼:
graph.h
class graph :
public CObject
{
protected:
//邊框
DECLARE_SERIAL(graph)
int left, up, right, down;
//選中狀態(tài)
unsigned int state;
int sx, sy;
int f_width = 5;
int fcolor = 0xffffff, bcolor = 0;
public:
graph() :graph(50, 50, 100, 100) {
}
graph(int l, int u, int r, int d);
void Offset(int cx, int cy);
void onPress(int x, int y); // 鼠標(biāo)按下
int onMove(int cx, int cy); // 鼠標(biāo)移動
void onRelease(int x, int y); // 鼠標(biāo)釋放
virtual void onDraw(CDC* pDC);
virtual int getGraphID() { return 0; }
virtual void Serialize(CArchive& ar);
void SetFillColor(int color);
void SetBorderColor(int color);
~graph();
};
結(jié)果報(bào)了一堆錯誤,事實(shí)是因?yàn)槲覀冃陆ǖ膅raph并沒有和MFC本身的類關(guān)聯(lián)起來

我們可以這樣做:打開framework.h(vs2017里是stdafx.h),我們在這里include一下我們的graph抽象類

如下(注意自己寫的頭文件要使用引號“”)
#include "graph.h"

我們再回過頭看graph.h,發(fā)現(xiàn)已經(jīng)沒有錯誤了

12.寫了頭文件,還要寫對應(yīng)的源文件:
我們右鍵頭文件,選擇添加-->新建項(xiàng),彈出如下界面;
選擇C++文件(.cpp),名稱為graph.cpp,點(diǎn)擊確定

13.如下

14.填寫如下代碼
graph.cpp
#include "framework.h"
IMPLEMENT_SERIAL(graph, CObject, 1)
graph::graph(int l, int u, int r, int d)
{
left = l;
up = u;
right = r;
down = d;
state = 0;
fcolor = 0xffffff;
}
void graph::Offset(int cx, int cy)
{
left += cx;
right += cx;
up += cy;
down += cy;
}
void graph::onPress(int x, int y)
{
sx = x; sy = y;
state = 0;
//選中圖形
if (left < x && x < right &&
up < y && y < down) {
state = 1;
return;
}
if (left - f_width / 2 < x && x < left + f_width / 2) state |= 2; // 選中左邊
if (up - f_width / 2 < y && y < up + f_width / 2) state |= 4;//選中上邊
if (right - f_width / 2 < x && x < right + f_width / 2) state |= 8;//選中右邊
if (down - f_width / 2 < y && y < down + f_width / 2) state |= 16; // 選中下邊
}
void graph::onRelease(int x, int y)
{
state = 0;
}
void graph::SetBorderColor(int color)
{
fcolor = color;
}
void graph::SetFillColor(int color)
{
bcolor = color;
}
int graph::onMove(int x, int y)
{
int cx, cy;
cx = x - sx; cy = y - sy;
sx = x; sy = y;
if (state == 1) {
Offset(cx, cy); // 位移量cx,cy
}
if (2 == (state & 2)) {
left = x;
}
if (4 == (state & 4)) {
up = y;
}
if (8 == (state & 8)) {
right = x;
}
if (16 == (state & 16)) {
down = y;
}
return state == 0 ? 0 : 1;
}
void graph::Serialize(CArchive & ar)
{
CObject::Serialize(ar);
if (ar.IsLoading()) {
ar >> left >> right >> up >> down >> f_width >> fcolor >> bcolor;
}
else
{
ar << left << right << up << down << f_width << fcolor << bcolor;
}
}
graph::~graph()
{
}
void graph::onDraw(CDC * pDC) {
CBrush b(fcolor);
pDC->SelectObject(&b);
CRect r(left, up, right, down);
pDC->FillRect(&r, &b);
CPen p(PS_SOLID, 1, bcolor);
pDC->SelectObject(&p);
pDC->Rectangle(left, up, right, down);
pDC->MoveTo(left, up);
pDC->DrawText(_T("空圖形"), -1, new CRect(left, up, right, down), DT_CENTER | DT_VCENTER | DT_SINGLELINE);
}
15.根據(jù)上面的步驟,同樣地,我們再新建一個矩形Rectangle類。這里直接列出頭文件和源文件
rectangle.h
#pragma once
#include "graph.h"
class rectangle :
public graph
{
public:
//DECLARE_SERIAL(graph)
//void Serialize(CArchive& ar);
rectangle() :graph(50, 50, 100, 100) {}
rectangle(int l, int u, int r, int d);
void onDraw(CDC* pDC);
int getGraphID() { return 2; }
~rectangle();
};
rectangle.cpp
#include "framework.h"
rectangle::rectangle(int l, int u, int r, int d) :graph(l, u, r, d)
{
state = 0;
fcolor = 0xffffff;
}
void rectangle::onDraw(CDC* pDC)
{
CBrush b(fcolor);
pDC->SelectObject(&b);
CRect r(left, up, right, down);
pDC->FillRect(&r, &b);
CPen p(PS_SOLID, 1, bcolor);
pDC->SelectObject(&p);
pDC->Rectangle(left, up, right, down);
pDC->MoveTo(left, up);
}
rectangle::~rectangle()
{
}
注意framework.h不要忘了加上:
#include "rectangle.h"

16.由于我們需要可以添加多個圖形,因此一個以graph對象構(gòu)成的list是必不可少的。我們在DrawDoc.h文檔頭文件添加這個list,可以添加//操作下面

17.如下,可以看到又報(bào)錯了:
std::list<graph*> graphList;

還是一樣的,把list的頭文件加到framework.h中
#include <list>

再回去看DrawDoc.h,發(fā)現(xiàn)沒有錯誤

18.我們再回到DrawView.cpp,填寫矩形Rectangle的消息處理程序,如下。代碼解釋為每點(diǎn)擊一次矩形按鈕,添加一個矩形:
void CDrawView::OnRectangle()
{
// TODO: 在此添加命令處理程序代碼
CDrawDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
pDoc->graphList.push_front(new rectangle(50, 50, 100, 100));
Invalidate();
}

19.我們還需要修改DrawView.cpp里的繪圖代碼部分

添加如下代碼(CDC* 后面的pDC取消注釋)
// CDrawView 繪圖
void CDrawView::OnDraw(CDC* pDC)
{
CDrawDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
// TODO: 在此處為本機(jī)數(shù)據(jù)添加繪制代碼
std::list<graph*>::iterator v;
for (v = pDoc->graphList.begin(); v != pDoc->graphList.end(); ++v) {
(*v)->onDraw(pDC);
}
}

20.運(yùn)行程序,發(fā)現(xiàn)報(bào)錯,這是預(yù)編譯頭的問題
1>------ 已啟動生成: 項(xiàng)目: Draw, 配置: Debug Win32 ------
1>pch.cpp
1>calendarbar.cpp
1>ChildFrm.cpp
1>Draw.cpp
1>DrawDoc.cpp
1>DrawView.cpp
1>graph.cpp
1>D:\vs2019_project\Draw\graph.cpp(110): fatal error C1010: 在查找預(yù)編譯頭時遇到意外的文件結(jié)尾。是否忘記了向源中添加“#include "pch.h"”?
1>MainFrm.cpp
1>rectangle.cpp
1>D:\vs2019_project\Draw\rectangle.cpp(25): fatal error C1010: 在查找預(yù)編譯頭時遇到意外的文件結(jié)尾。是否忘記了向源中添加“#include "pch.h"”?
1>正在生成代碼...
1>已完成生成項(xiàng)目“Draw.vcxproj”的操作 - 失敗。
========== 生成: 成功 0 個,失敗 1 個,最新 0 個,跳過 0 個 ==========

21.點(diǎn)擊菜單欄的項(xiàng)目-->屬性,選擇C/C++-->預(yù)編譯頭,如下

改為不使用預(yù)編譯頭,點(diǎn)擊確定

22.我們再次運(yùn)行代碼,出現(xiàn)如下界面

23.我們點(diǎn)擊圖形面板的矩形,可以看到窗口生成了一個矩形

24.然而,僅僅是生成一個矩形還是不夠的;我們還需要添加鼠標(biāo)的相關(guān)消息響應(yīng)函數(shù),例如鼠標(biāo)移動,鼠標(biāo)按下和鼠標(biāo)抬起
右鍵源文件,點(diǎn)擊類向?qū)?/p>

25.如下

26.我們添加一個鼠標(biāo)左鍵按下的消息響應(yīng):選擇消息欄,選擇WM_LBUTTONUP,類名選擇CDrawView,點(diǎn)擊添加處理程序

如下


添加如下代碼:
void CDrawView::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息處理程序代碼和/或調(diào)用默認(rèn)值
CDrawDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
// TODO: 在此處為本機(jī)數(shù)據(jù)添加繪制代碼
std::list<graph*>::iterator v;
for (v = pDoc->graphList.begin(); v != pDoc->graphList.end(); ++v) {
(*v)->onPress(point.x, point.y);
}
Invalidate();
//CView::OnLButtonDown(nFlags, point);
}

27.同樣地,我們再添加鼠標(biāo)左鍵松開

填寫代碼:
void CDrawView::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息處理程序代碼和/或調(diào)用默認(rèn)值
CDrawDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
// TODO: 在此處為本機(jī)數(shù)據(jù)添加繪制代碼
std::list<graph*>::iterator v;
for (v = pDoc->graphList.begin(); v != pDoc->graphList.end(); ++v) {
(*v)->onRelease(point.x, point.y);
}
Invalidate();
//CView::OnLButtonUp(nFlags, point);
}

28.同樣地,我們再添加鼠標(biāo)移動

填寫代碼:
void CDrawView::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息處理程序代碼和/或調(diào)用默認(rèn)值
CDrawDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
// TODO: 在此處為本機(jī)數(shù)據(jù)添加繪制代碼
std::list<graph*>::iterator v;
for (v = pDoc->graphList.begin(); v != pDoc->graphList.end(); ++v) {
(*v)->onMove(point.x, point.y);
}
Invalidate();
//CView::OnMouseMove(nFlags, point);
}

29.運(yùn)行程序,我們點(diǎn)擊矩形,可以拖動這個矩形了?。?/p>


30.博主這里還做了放大和縮?。喊咽髽?biāo)移到矩形邊緣,點(diǎn)擊邊緣就可以放大縮小~

31.當(dāng)然,我們還可以再添加好幾個矩形,因?yàn)閯e忘了博主是用list來存儲每次生成的graph對象的~

四、我們還可以再多畫一些,例如箭頭、直線和三角
1.相信前面的步驟大家了解的話,我相信添加按鈕是很容易的,這里就直接給出博主的按鈕ID
三角形:ID_TRIANGLE
箭頭:ID_ARROW
直線:ID_LINE



2.添加的類和方法:
*framework.h
#include <afxwin.h> // MFC 核心組件和標(biāo)準(zhǔn)組件 #include <afxext.h> // MFC 擴(kuò)展 #include "graph.h" #include "rectangle.h" #include "triangle.h" #include "arrow.h" #include "line.h" #include <list>

(1)三角形:
triangle.h
#pragma once
#include "graph.h"
class triangle :
public graph
{
protected:
public:
triangle(int l, int u, int r, int d);
int getGraphID() { return 3; }
void onDraw(CDC* pDC);
~triangle();
};
triangle.cpp
#include "framework.h"
triangle::triangle(int l, int u, int r, int d) :graph(l, u, r, d)
{
state = 0;
fcolor = 0xffffff;
}
void triangle::onDraw(CDC* pDC)
{
CPoint pts[3];
pts[0].x = (left + right) / 2;
pts[0].y = up;
pts[1].x = left;
pts[1].y = down;
pts[2].x = right;
pts[2].y = down;
CBrush b(fcolor);
pDC->SelectObject(&b);
CPen p(PS_SOLID, 1, bcolor);
pDC->SelectObject(&p);
pDC->Polygon(pts, 3);
}
triangle::~triangle()
{
}
(2)箭頭:
arrow.h
#pragma once
#include "graph.h"
class arrow :
public graph
{
public:
arrow(int l, int u, int r, int d);
void onDraw(CDC* pDC);
int getGraphID() { return 4; }
~arrow();
};
arrow.cpp
#include "framework.h"
arrow::arrow(int l, int u, int r, int d) :graph(l, u, r, d)
{
}
void arrow::onDraw(CDC* pDC)
{
CPoint pts[2], pt[3];
pts[0].x = left;
pts[0].y = (up + down) / 2;
pts[1].x = (left + right) / 2;
pts[1].y = (up + down) / 2;
pt[0].x = (left + right) / 2;
pt[0].y = up;
pt[1].x = (left + right) / 2;
pt[1].y = down;
pt[2].x = right;
pt[2].y = (up + down) / 2;
CBrush b(fcolor);
pDC->SelectObject(&b);
CPen p(PS_SOLID, 1, bcolor);
pDC->SelectObject(&p);
pDC->Polygon(pts, 2);
pDC->Polygon(pt, 3);
}
arrow::~arrow()
{
}
(3)直線:
line.h
#pragma once
#include "graph.h"
class line :
public graph
{
public:
line() :line(50, 50, 100, 100) {}
line(int l, int u, int r, int d);
void onDraw(CDC* pDC);
int getGraphID() { return 1; }
~line();
};
line.cpp
#include "framework.h"
line::line(int l, int u, int r, int d) :graph(l, u, r, d)
{
state = 0;
fcolor = 0xffffff;
}
void line::onDraw(CDC* pDC)
{
CPoint pts[2];
pts[0].x = left;
pts[0].y = (up + down) / 2;
pts[1].x = right;
pts[1].y = (up + down) / 2;
CBrush b(fcolor);
pDC->SelectObject(&b);
CPen p(PS_SOLID, 1, bcolor);
pDC->SelectObject(&p);
pDC->Polygon(pts, 2);
}
line::~line()
{
}
3.添加的事件處理程序:
(1)三角形:
void CDrawView::OnTriangle()
{
// TODO: 在此添加命令處理程序代碼
CDrawDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
pDoc->graphList.push_front(new triangle(50, 50, 100, 100));
Invalidate();
}
(2)箭頭:
void CDrawView::OnArrow()
{
// TODO: 在此添加命令處理程序代碼
CDrawDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
pDoc->graphList.push_front(new arrow(50, 50, 100, 100));
Invalidate();
}
(3)直線:
void CDrawView::OnLine()
{
// TODO: 在此添加命令處理程序代碼
CDrawDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
pDoc->graphList.push_front(new line(50, 50, 100, 100));
Invalidate();
}
4.運(yùn)行程序,現(xiàn)在可以添加各種圖形并且可以改變大小啦

五、圖形的輪廓填充和內(nèi)部填充(為了方便,都是統(tǒng)一更改顏色,一個一個改代碼很麻煩)
1.添加一個面板,再添加兩個按鈕。按鈕的選擇是顏色按鈕

2.如下
輪廓:ID_FILLCOLOR_LINE
內(nèi)部:ID_FILLCOLOR_IN


3.添加的事件處理程序:
(1)需要在DrawView.cpp中添加MainFrm.h頭文件
#include "MainFrm.h"

(2)還需要在MainFrm.h中將m_wndRibbonBar從protected變?yōu)閜ublic,這樣才能訪問到這個變量,否則報(bào)錯!
protected: // 控件條嵌入成員 //CMFCRibbonBar m_wndRibbonBar; CMFCRibbonApplicationButton m_MainButton; CMFCToolBarImages m_PanelImages; CMFCRibbonStatusBar m_wndStatusBar; COutlookBar m_wndNavigationBar; CMFCShellTreeCtrl m_wndTree; CCalendarBar m_wndCalendar; CMFCCaptionBar m_wndCaptionBar; public: CMFCRibbonBar m_wndRibbonBar;

輪廓
void CDrawView::OnFillcolorLine()
{
// TODO: 在此添加命令處理程序代碼
CDrawDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
CMainFrame* pFrame = (CMainFrame*)AfxGetMainWnd();
CMFCRibbonColorButton* a = (CMFCRibbonColorButton*)((pFrame->m_wndRibbonBar).FindByID(ID_FILLCOLOR_LINE));
COLORREF c = a->GetColor();
std::list<graph*>::iterator v;
for (v = pDoc->graphList.begin(); v != pDoc->graphList.end(); ++v) {
(*v)->SetFillColor(c);
}
Invalidate();
}
內(nèi)部
void CDrawView::OnFillcolorIn()
{
// TODO: 在此添加命令處理程序代碼
CDrawDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
CMainFrame* pFrame = (CMainFrame*)AfxGetMainWnd();
CMFCRibbonColorButton* a = (CMFCRibbonColorButton*)((pFrame->m_wndRibbonBar).FindByID(ID_FILLCOLOR_IN));
COLORREF c = a->GetColor();
std::list<graph*>::iterator v;
for (v = pDoc->graphList.begin(); v != pDoc->graphList.end(); ++v) {
(*v)->SetBorderColor(c);
}
Invalidate();
}
4.運(yùn)行程序
(1)輪廓變紅,選擇紅色

如下

(2)內(nèi)部填充為綠色,選擇綠色

如下

六、序列化保存和讀取文件
1.修改DrawDoc.cpp的序列化部分,這是用于菜單欄里的保存和打開圖片所使用的


2.修改代碼如下:
// CDrawDoc 序列化
void CDrawDoc::Serialize(CArchive& ar)
{
if (ar.IsStoring())
{
// TODO: 在此添加存儲代碼
ar << graphList.size();
for (auto it = graphList.begin(); it != graphList.end(); it++) {
ar << (*it)->getGraphID();
(*it)->Serialize(ar);
}
}
else
{
// TODO: 在此添加加載代碼
for (auto it = graphList.begin(); it != graphList.end(); it++) {
delete* it;
}
graphList.clear();
int i, gid;
ar >> i;
graph* g;
while (i--) {
ar >> gid;
switch (gid)
{
case 1:
g = new line();
break;
case 2:
g = new rectangle();
break;
case 3:
g = new triangle(0, 0, 0, 0);
break;
case 4:
g = new arrow(0, 0, 0, 0);
break;
default:
g = new graph();
break;
}
g->Serialize(ar);
graphList.push_back(g);
}
}
}

3.運(yùn)行程序:博主隨便畫一個圖

選擇保存,默認(rèn)路徑和名字

可以看到有一個Draw1的文件

我們關(guān)掉程序,再運(yùn)行程序,打開這個文件


可以看到多一個窗口,說明我們序列化成功?。?nbsp;

七、自己寫一個導(dǎo)出文件的按鈕
1.再新建一個面板,拖入一個按鈕,名字到導(dǎo)出圖片,ID為ID_SAVE

2.添加的事件處理程序:
void CDrawView::OnSave()
{
// TODO: 在此添加命令處理程序代碼
CClientDC dc(this);
CRect rect;
CString saveFilePath;
BOOL showMsgTag;
BOOL saveTag = FALSE;
GetClientRect(&rect);
HBITMAP hbitmap = CreateCompatibleBitmap(dc, rect.right - rect.left, rect.bottom - rect.top);
HDC hdc = CreateCompatibleDC(dc);
HBITMAP hOldMap = (HBITMAP)SelectObject(hdc, hbitmap);
BitBlt(hdc, 0, 0, rect.right - rect.left, rect.bottom - rect.top, dc, 0, 0, SRCCOPY);
CImage image;
image.Attach(hbitmap);
if (!saveTag)
{
saveTag = TRUE;
showMsgTag = TRUE;
CString strFilter = _T("位圖文件(*.bmp)|*.bmp|JPEG 圖像文件|*.jpg| GIF圖像文件 | *.gif | PNG圖像文件 | *.png |其他格式(*.*) | *.* || ");
CFileDialog dlg(FALSE, _T("bmp"), _T("iPaint1.bmp"), NULL, strFilter);
if (dlg.DoModal() != IDOK) return;
CString strFileName;
CString strExtension;
strFileName = dlg.m_ofn.lpstrFile;
if (dlg.m_ofn.nFileExtension == 0)
{
switch (dlg.m_ofn.nFilterIndex)
{
case 1:
strExtension = "bmp";
break;
case 2:
strExtension = "jpg";
break;
case 3:
strExtension = "gif";
break;
case 4:
strExtension = "png";
break;
}
strFileName = strFileName + "." + strExtension;
}
saveFilePath = strFileName;
}
else
{
showMsgTag = FALSE;
}
HRESULT hResult = image.Save(saveFilePath);
if (FAILED(hResult)) {
MessageBox(_T("保存圖像文件失敗!"));
}
else
{
if (showMsgTag)
MessageBox(_T("文件保存成功!"));
}
image.Detach();
SelectObject(hdc, hOldMap);
}
3.運(yùn)行程序,導(dǎo)入Draw1

點(diǎn)擊導(dǎo)出圖片,選擇路徑和文件名稱,確定

4.提示文件保存成功!

5.雙擊打開這個文件iPaint1.bmp

6.可以看到成功打開(注意:這個文件不能用剛才自己寫的MFC程序自帶的打開來打開這個文件,因?yàn)榇蜷_的文件不是被序列化過的,因此打開會失?。。?/p>
因此不能用這里的打開按鈕喲![]()

八、總結(jié)
1.MFC的按鈕消息需要熟練掌握
2.鼠標(biāo)消息的使用也很重要
3.序列化保存和普通的導(dǎo)出圖片不是一碼事
4.對于抽象類的使用,尤其是畫各種圖是很重要的
5.MFC內(nèi)置的庫函數(shù)需要熟練掌握(畫筆、刷子等等)
到此這篇關(guān)于vs2019 MFC實(shí)現(xiàn)office界面的畫圖小項(xiàng)目的文章就介紹到這了,更多相關(guān)vs2019 MFC 畫圖內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語言中輸入函數(shù)(scanf()、fgets()和gets())的區(qū)別詳解
這篇文章主要給大家介紹了關(guān)于C語言中三種輸入函數(shù)(scanf()、fgets()和gets())區(qū)別的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2017-11-11
C/C++中不同數(shù)據(jù)類型之間的轉(zhuǎn)換詳解
這篇文章主要介紹了C/C++中不同數(shù)據(jù)類型之間的轉(zhuǎn)換詳解,數(shù)據(jù)類型轉(zhuǎn)換是計(jì)算機(jī)編程中常見的操作,用于將一個數(shù)據(jù)類型轉(zhuǎn)換為另一個數(shù)據(jù)類型,本文將對不同數(shù)據(jù)類型之間的轉(zhuǎn)換作出說明,需要的朋友可以參考下2023-10-10
C++實(shí)現(xiàn)職工信息管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了c++實(shí)現(xiàn)職工信息管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-01-01
vector,map,list,queue的區(qū)別詳細(xì)解析
如果我們需要隨機(jī)訪問一個容器則vector要比list好得多。如果我們已知要存儲元素的個數(shù)則vector 又是一個比list好的選擇。如果我們需要的不只是在容器兩端插入和刪除元素則list顯然要比vector好2013-09-09
M1 Macbook vscode C++ debug調(diào)試實(shí)現(xiàn)
本文主要介紹了M1 Macbook vscode C++ debug調(diào)試,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-08-08

