本文將講解如何通過MFC制作一個簡易的繪圖軟體
文章目錄
- 一、構造軟體的界面
- 二、畫出圖形
- 三、圖形的保存與重繪
- 四、程式下載
一、構造軟體的界面
首先新建一個MFC的工程,進入之后按F5除錯,可以看到現在的界面

停止除錯,開始看我們的程式,進入資源視圖–>myxxxxxx.rc–>Menu->IDR_MAINFRAME
新鍵入按鈕“畫圖”及其子按鈕“線段”,并修改其ID為ID_Line

進入資源視圖–>myxxxxxx.rc–>toolbar->IDR_MAINFRAME_256
新鍵入圖形按鈕“線段”,并修改其ID為ID_Line

此時除錯會發現“線段”這個按鈕是灰的,所以我們需要對他添加命令
點擊專案–>類向導–>選擇ID_Line,記得類名那兒選擇xxxxxxxview

然后點進xxxxxxxview會找到一個對應的函式,但內容為空,所以我們可以定義int型的標志type,在函式中寫入type=1,表示點擊“線段”按鈕時type=1,可以作為之后導向畫線段函式的標志,
void CMy1900402213View::OnLine()
{
type = 1;
}
void CMy1900402213View::OnSquare()
{
type = 2;
}
void CMy1900402213View::OnCircle()
{
type = 3;
}
要制作繪出圓形矩形甚至畫筆顏色以及形狀都可以如法炮制
最終除錯時的軟體界面如下

二、畫出圖形
以畫直線為例
首先我們建立一個CLine的類,里面要寫一個放置起點和終點的函式,和最后畫圖的函式
**CLine.h**
#pragma once
class CLine
{
public:
CLine();
void Set_start_point(CPoint p);
void Set_end_point(CPoint p);
private:
CPoint Line_start_point;
CPoint Line_end_point;
public:
void Draw(CDC* pDC);
};
**CLine.cpp**
#include "pch.h"
#include "CLine.h"
CLine::CLine()
{
}
void CLine::Set_start_point(CPoint p)
{
Line_start_point = p;
}
void CLine::Set_end_point(CPoint p)
{
Line_end_point = p;
}
void CLine::Draw(CDC* pDC)
{
pDC->MoveTo(Line_start_point);
pDC->LineTo(Line_end_point);
}
然后在xxxxxxview檔案中添加滑鼠點擊,滑鼠移動,滑鼠抬起,三個訊息處理函式

然后可以在xxxxxxxxview.cpp找到對應的三個空的訊息處理函式,我們需要填充他,以直線為例,我們可以在滑鼠點擊時設定起始點,滑鼠移動時一直設定終點,然后從起始點到終點畫出一條直線
void CMy1900402213View::OnLButtonDown(UINT nFlags, CPoint point)
{
switch (type)
{
case 1:
{
m_pline = new CLine;
m_pline->Set_start_point(point);
}break;
}
start = true;
CView::OnLButtonDown(nFlags, point);
}
void CMy1900402213View::OnMouseMove(UINT nFlags, CPoint point)
{
if (start == true)
{
CDC* pDC = GetDC();
if (type == 1)
{
m_pline->Set_end_point(point);
m_pline->Draw(pDC);
}
ReleaseDC(pDC);
}
}
void CMy1900402213View::OnLButtonUp(UINT nFlags, CPoint point)
{
start=false;
}
此時除錯,畫直線時會發現有重影,這是因為滑鼠每次移動都新設立了一個終點,也就是每次滑鼠的移動都會新畫一條線,但只有最后一條線才是我們想要的,可之前畫出的線也還留在畫布上,這個時候我們需要在畫線前用一支反色筆(使用其他顏色的時候,這個反色筆其實是背景色,但如果這條線如果是白色的話,反色筆的顏色將會是黑色)把上一條線覆寫掉,即畫出a線,接著滑鼠移影片出了b線的同時會用一支白色的筆把a線覆寫掉,最后畫布上只剩下b這一條線,

void CMy1900402213View::OnMouseMove(UINT nFlags, CPoint point)
{
if (start == true)
{
CDC* pDC = GetDC();
if (type == 1)
{
pDC->SetROP2(R2_NOTXORPEN);//呼叫反色筆
m_pline->Draw(pDC);
m_pline->Set_end_point(point);
m_pline->Draw(pDC);
}
ReleaseDC(pDC);
}
}
那么問題又來了,在vs2019的環境下,畫好a線時會出現一條從坐標原點到a線起始點的黑線,我推測在畫第一條a線的時候會有一條默認從坐標原點到起始點的白線,然后在反色筆的影響下變成了黑色,畫出a線時會有一條從原點到a線起始點的黑線,所以我們要做一個標志,判斷下我們現在畫的是第幾條線,如果是第一條線,那我們就需要繞開反色筆涂抹那一段程式,如果不是第一條線,那么就不用繞開,

void CMy1900402213View::OnMouseMove(UINT nFlags, CPoint point)
{
if (start == true)
{
CDC* pDC = GetDC();
if (type == 1)
{
if(go)
{
pDC->SetROP2(R2_NOTXORPEN);//呼叫反色筆
m_pline->Draw(pDC);
}
else
go=ture;
m_pline->Set_end_point(point);
m_pline->Draw(pDC);
}
ReleaseDC(pDC);
}
}
這樣之后就可以正常的畫出直線了

圓與矩形也可以用類似的流程來繪出,不過圓需要設定圓心點和計算半徑,矩形則需要設定起始點和對角點,
當然如果想改變線的顏色和形狀的話
CDC* pDC = GetDC();
CPen pen(PS_SOLID, 1, RGB(255, 0, 0));//PS_SOLID是實線,PS_DOT是虛線,RGB就是那個RGB
CPen* pOldPen = (CPen*)pDC->SelectObject(&pen);
ReleaseDC(pDC);//最后必須釋放掉
三、圖形的保存與重繪
大家可能已經發現通過第二部分畫出的直線是無法保存的,也就是說將視窗最小化后再打開時畫的的直線就不見了,所以我們需要畫完每一條直線后將其保存,并且一直重新繪制它,
至于如何保存直線的資料,并且提取它的資料來繪制,這里就要用到鏈表了
**Clist.h**
#pragma once
#ifndef Clist_h
#define Clist_h
#include<iostream>
#include<stdlib.h>
using namespace std;
struct Node
{
int now_RGB;//儲存畫筆顏色
int now_line;//儲存線型
int num;
void* data;
int now_type;//儲存形狀
Node* next;
Node()
{
next = NULL;
}
};
class CSlist
{
public:
CSlist();
Node* first;
void InputFront(Node* Pelem);//在前面插入最新的一個節點
int Length()const;//判斷鏈表長度
bool IsEmpty()const;//判斷鏈表是否為空
void MakeEmpty();//將鏈表清空
Node* Locate(int i);//將第i個資料取出
int Input_behind_pos(Node* Pelem, int pos);//在第幾個數值之后插入新節點
int Input_before_pos(Node* Pelem, int pos);
void Delete(int pos);//洗掉
};
#endif
**Clist.cpp**
#include"CSlist.h"
#include<iostream>
#include<stdlib.h>
using namespace std;
CSlist::CSlist()
{
first=NULL;
};
void CSlist::InputFront(struct Node* Pelem)
{
if (Pelem == NULL) return;
Pelem->next = first;
first= Pelem;
}
int CSlist::Length()const
{
if (first == NULL) return 0;
struct Node* n = first;
int length = 1;
while (n->next)
{
length++;
n = n->next;
}
return length;
}
bool CSlist::IsEmpty()const
{
if (first == NULL)
{
return true ;
}
else
{
return false;
}
}
void CSlist::MakeEmpty()
{
struct Node* d;
while (first)
{
d = first;
first = first -> next;
delete d;
}
return;
}
struct Node* CSlist::Locate(int i)
{
int j=0;
struct Node* L = first;
if (i <= 0) cout << "error"<<endl;
while (L->next)
{
j++;
if (j == i)
{
return L;
}
else L = L->next;
}
cout << "error" << endl;
}
int CSlist::Input_behind_pos(struct Node* Pelem, int pos)
{
struct Node* d = Locate(pos + 1);
Pelem->next = d;//新節點的next指向原來的下一個節點
struct Node* c = Locate(pos);
c->next = Pelem;//上一個節點的next指向新節點
return 1;
}
int CSlist::Input_before_pos(struct Node* Pelem, int pos)
{
struct Node* c = Locate(pos-1);
c->next = Pelem;//上一個節點的next指向新節點
struct Node* d = Locate(pos);
Pelem->next = d;//新節點的next指向原來的下一個節點
return 1;
}
void CSlist::Delete(int pos)
{
struct Node* a = Locate(pos-1);
struct Node* b = Locate(pos);
struct Node* c = Locate(pos+1);
a->next = c;
delete b;
}
了解了鏈表之后我們要選擇一個合適的時機儲存我們的圖形,所以我們可以在之前放置的滑鼠抬起訊息處理程式里進行儲存,分別在鏈表中存盤圖形是什么,即now_type是幾,以及圖形所需要的點和資料,也就是直線的起始點和終點,圓的圓心與半徑,矩形的起始點與對角點
void CMy1900402213View::OnLButtonUp(UINT nFlags, CPoint point)
{
if (start==true)
{
Node* repaint = new Node;
switch (type)
{
case 1:
{
repaint->now_type = 1;
repaint->data =m_pline;
m_line_list.InputFront(repaint);
}
break;
case 2:
{
repaint->now_type = 2;
repaint->data = m_psquare;
m_line_list.InputFront(repaint);
}
break;
case 3:
{
repaint->now_type = 3;
repaint->data = m_pcircle;
m_line_list.InputFront(repaint);
}break;
}
}
go = false;
start = false;
CView::OnLButtonUp(nFlags, point);
}
這樣之后你所畫的所有圖形都被存盤在一條鏈表中,就像一棟公寓,每一個房間里都是一個圖形,里面記錄著他的形狀和他的特殊點,
然后繼續在xxxxxxview.cpp檔案中尋找ondraw函式,在這個里面添加上重繪圖形的程式,
重繪的原理就是,利用回圈,把鏈表中每個圖形的資料拿出來,再根據圖形的不一樣,使用不同的繪圖程式繪制,
注意,因為矩形和圓心其實是個背景色的封閉圖形,重繪時可能會因為重繪的順序導致,把一些線條遮住,所以我重繪時采用了空心筆刷,這樣他們就是透明的封閉圖形了,
void CMy1900402213View::OnDraw(CDC* pDC)
{
CMy1900402213Doc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: 在此處為本機資料添加繪制代碼
int i,j;
j = m_line_list.Length();
if (m_line_list.IsEmpty() == false)
{
for(i=0;i<j+1;i++)
{
CDC* pDC = GetDC();
pDC->SelectStockObject(NULL_BRUSH);//空心筆刷
Node* paint;
paint=m_line_list.Locate(i);
if (paint->now_type == 1)
{
((CLine*)paint->data)->Draw(pDC);
}
if (paint->now_type == 2)
{
((CQuare*)paint->data)->Draw(pDC);
}
if (paint->now_type == 3)
{
((CCircle*)paint->data)->Draw(pDC);
}
ReleaseDC(pDC);
}
}
}
最后就可以做到像我這樣了

四、程式下載
有很多細小的地方很難講述完畢,大家可以直接下載程式看看
基于MFC制作的簡易繪圖軟體
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/242414.html
標籤:其他
