本文将讲解如何通过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);}elsego=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制作的简易绘图软件

如何基于MFC制作简易绘图软件相关推荐

  1. MFC实现简易绘图软件

    本篇文章实现了使用MFC实现简易的绘图软件.本来想说一下具体的实现方法与思路的,因为这时之前做的,现在看起来写的真的太烂了,简直是不忍卒读,所以有兴趣的自己下载源码回去看吧.基本上是使用鼠标拖动来绘制 ...

  2. 基于C#实现简易绘图工具【100010177】

    C#实现简易绘图工具 一. 引言 实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有 ...

  3. 基于MFC平台的对话框软件模型

    一.MFC的架构根据主窗口类型的不同可分一下几种 (1)SDI,单文档界面,一个主框架窗口下只能编辑一份文档.如记事本和画笔等 (2)MDI,多文档界面,一个主框架窗口下可以编辑多份文档.如VC,PS ...

  4. MFC制作简易音乐播放器

    // MFC音乐播放器Dlg.cpp : implementation file // #include "stdafx.h" #include "MFC音乐播放器.h& ...

  5. 基于MFC的简易登录器

    一.项目创建 1.打开vs,并且创建一个新的项目(这里编者选择了使用vs2019) 2.这里选择MFC应用(如果这里没有MFC应用的话,请继续往下看,有解决方法) 3.配置完新项目后,会跳出配置&qu ...

  6. 基于java的简易邮件收发软件

    大二下半学期的作品,因为大二下课程多(有七八门),做的比较赶,还请见谅. 发送邮件主要使用的是 java.net.Socket 包, 而接收邮件则是用javax.mail 包,界面使用的是Window ...

  7. Android实战制作简易天气预报软件

    前言:  真正的梦想,永远在实现之中,更在坚持之中.累了,就停一停,让手贴着手,温暖冷漠的岁月:苦了,就笑一笑,让心贴着心,体味至爱的抚摸:哭了,就让泪水尽情流淌,痛彻心菲也是精彩.选择一条道路,就选 ...

  8. [原创][连载].基于SOPC的简易数码相框 - Nios II SBTE部分(软件部分) - 从SD卡内读取图片文件,然后显示在TFT-LCD上...

    实在很抱歉,时间紧张,我只讲怎样从SD卡内读取bin文件(二进制文件),然后现在TFT-LCD上. 准备工具 1. Image2Lcd.zip 操作步骤 步骤1 寻找或制作240x320的图片 简单起 ...

  9. 计算机地理绘图软件叫什么,地理教师如何选择理想的绘图软件 ──基于对常用绘图软件的比较与分析...

    一位前苏联地理教育家说过:"没有地图的地理课,也就没有地理了."这说明地理教学与地图有着非常密切的关系.在日常的授课.编写试题.教学研究中,地理教师都需要用到各种地图,如政区图.统 ...

最新文章

  1. 如何安装python3.7.4_银河麒麟安装Python3.7.4以及升级自带OpenSSL
  2. Android API Guides 安卓API指导----第一部分:Introduction(介绍)
  3. Python脚本文件和函数的基本运用
  4. Sklearn参数详解—GBDT
  5. concat合并的数组会有顺序么_超全的JS常用数组方法整理
  6. 精确光源(Punctual Light Sources)
  7. django之视图view小知识
  8. MTV和MVC的区别
  9. 桌面快递查询物流信息查看神器
  10. 如何解决图片在移动端清晰显示的问题
  11. linux 出现running guests on default URT情况解决
  12. 网络安全面试、实习、校招经验打包分享
  13. android gradle 版本部队,gradle中统一配置版本的小技巧。
  14. 某型无人机群的监视覆盖任务航路规划
  15. android蓝牙开发 蓝牙设备的查找和连接
  16. SQL Server 查询分析器使用(性能分析)
  17. Red Flag linux硬盘安装
  18. 【卸载CUDA-10.0】
  19. 蓝桥杯真题及答案JavaB组(第七届~第十一届)
  20. 微信支付宝手机网站支付(WAP)

热门文章

  1. 航天器/控制器飞行能够实现自主控制么?
  2. C语言_自定义数据类型
  3. MySQL创建组合索引
  4. 阿轩的复习笔记-主从库数据一致性
  5. Linux 查看tomcat占用的端口号
  6. Kitti数据集简介
  7. 2、java的应用领域
  8. byte[]与String互转对象和JSON相互转
  9. oracle12c ora 12560,12C安装历险记----ORA-12560和ORA-12537的解决方案
  10. 中国基金业协会网站资产管理业务综合报送平台-数据爬取