欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

如何基于MFC制作简易绘图软件

程序员文章站 2022-06-24 17:45:53
本文将讲解如何通过MFC制作一个简易的绘图软件构造软件的边框画出图形图形的重绘与保存一、构造软件的边框二、画出图形三、图形的重绘与保存...
		             本文将讲解如何通过MFC制作一个简易的绘图软件

一、构造软件的界面

首先新建一个MFC的工程,进入之后按F5调试,可以看到现在的界面
如何基于MFC制作简易绘图软件

停止调试,开始看我们的程序,进入资源视图–>myxxxxxx.rc–>Menu->IDR_MAINFRAME
新键入按钮“画图”及其子按钮“线段”,并修改其ID为ID_Line
如何基于MFC制作简易绘图软件
进入资源视图–>myxxxxxx.rc–>toolbar->IDR_MAINFRAME_256
新键入图形按钮“线段”,并修改其ID为ID_Line
如何基于MFC制作简易绘图软件
此时调试会发现“线段”这个按钮是灰的,所以我们需要对他添加命令
点击项目–>类向导–>选择ID_Line,记得类名那儿选择xxxxxxxview
如何基于MFC制作简易绘图软件

然后点进xxxxxxxview会找到一个对应的函数,但内容为空,所以我们可以定义int型的标志type,在函数中写入type=1,表示点击“线段”按钮时type=1,可以作为之后导向画线段函数的标志。

void CMy1900402213View::OnLine()
{
	type = 1;
}
void CMy1900402213View::OnSquare()
{
	type = 2;
}
void CMy1900402213View::OnCircle()
{
	type = 3;
}

要制作绘出圆形矩形甚至画笔颜色以及形状都可以如法炮制
最终调试时的软件界面如下
如何基于MFC制作简易绘图软件

二、画出图形

                                      以画直线为例

首先我们建立一个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文件中添加鼠标点击,鼠标移动,鼠标抬起,三个消息处理函数
如何基于MFC制作简易绘图软件

然后可以在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这一条线。
如何基于MFC制作简易绘图软件

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线起始点的黑线。所以我们要做一个标志,判断下我们现在画的是第几条线,如果是第一条线,那我们就需要绕开反色笔涂抹那一段程序,如果不是第一条线,那么就不用绕开。
如何基于MFC制作简易绘图软件

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);
	}
}

这样之后就可以正常的画出直线了
如何基于MFC制作简易绘图软件
圆与矩形也可以用类似的流程来绘出,不过圆需要设置圆心点和计算半径,矩形则需要设置起始点和对角点。
当然如果想改变线的颜色和形状的话

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制作简易绘图软件

四、程序下载

有很多细小的地方很难讲述完毕,大家可以直接下载程序看看
基于MFC制作的简易绘图软件

本文地址:https://blog.csdn.net/weixin_51599044/article/details/111741111

相关标签: mfc