栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

基于C++的简单的MFC绘图程序

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

基于C++的简单的MFC绘图程序

一.准备工作
1.根据老师的运行实例,在创建MFC程序时使用的高级视图功能
1)由于老师平时上课时,使用的都是经典的基础视图,而课程设计文档中的视图是高级视图,对比运行的视图,我尝试着把程序的视图设为和课程设计文档里面一样的

2.参考老师发的代码,分析程序所需要的类和各个文件之间的关系
    1)设计类
 自己创建一个WShape(以W开头便于和MFC的类区分)图元类,派生图元类单独创建.h和.cpp文件,便于管理,虽然切换有些麻烦,但后期修改很方面,我没有像示例程序里面那样把所有的类都放在shape文件里面

 基类是WShape
     数据成员:
  1)原点坐标(鼠标点击的点)
  *2)旋转角度(单独为文本类设计,其他图形不使用)
  3)和线以及填充有关的属性字段

     成员函数
  1)有参数的构造函数
  2)绘制图元的函数
  3)判断鼠标点击的位置是否在图形内部的函数,是否打开属性设计窗口
  4)序列化数据的函数
  5)重新设置图元的属性的函数

     注意:要在每个派生类的.h头文件里面声明该类型支持序列化,并在.cpp源文件里面指定序列化的版本
     例如:矩形类
     DECLARE_SERIAL(WRectangle)//声明类WRectangle是支持序列化

     IMPLEMENT_SERIAL(WRectangle, CObject, 1)//实现类WSquare的序列化,指定版本为1

 派生类为(http://www.jizhuomi.com/software/244.html)CDC博客
     1)正方形类Square,矩形类Rectangle,对于正方形类,除了基类WShape之外的数据成员外,有一个独立的width数据,而矩形类只是在此基础上加上一个height数据,两者的绘图原理是一样的,CDC类的pDC对象中有绘制矩形的函数

     2)圆类Circle,椭圆类Ellipse,椭圆类既有长半轴,也有短半轴,两者的绘图原理是一样的,使用CDC类的pDC对象中绘制椭圆的函数

     3)三角类Triangle,使用CDC类的pDC对象中绘制多边形的Polygon函数(图片),(https://msdn.microsoft.com/en-us/library/fxhhde73.aspx#cdc__polyline)参看微软官网上的函数用法,要把多边形的顶点放在一个坐标点数组里面,并指定多边形的顶点的个数

     4)文本Text类,采取自定义的字体来实现,旋转角度是自定义字体的一个参数
  解决方法
      使用CreateFontIndirect(const LOGFONT* lpLogFont)函数创建斜率字体,参数lpLogFont->lfEscapement是字体的角度
  网址(http://bbs.csdn.net/topics/20746)
      LOGFONT logfont;//创建自己的字体
      lstrcpy((LPSTR)logfont.lfFaceName,(LPSTR)"楷体_GB2312");
      logfont.lfWeight=700;
      logfont.lfWidth=40;
      logfont.lfHeight=70;
      logfont.lfEscapement=angle;
      //这个参数就是用来控制角度,这里是正常显示
      logfont.lfUnderline=FALSE;
      logfont.lfItalic=FALSE;
      logfont.lfStrikeOut=FALSE;
      logfont.lfCharSet=GB2312_CHARSET;

      hFont=CreateFontIndirect(&logfont);
      ……
      下面就是使用该字体了,
      hOldFont=(HFONT*)dc.SelectObject(hFont);

    2)设计对话框
 使用MFC的控件,来搭建对话框的界面,适当修改控件的ID值
 1)Combobox下拉框控件
     所用的函数
  GetCurSel()来获取鼠标焦点的值的序号,注意下拉框的sort属性,默认是true,会按照自然排序,这样和你添加内容的顺序就会不一样,可以改为false
  AddString()来向下拉框控件里面添加内容,也可以到VS2015的属性框里面的data属性里面添加,Ctrl+Enter换行
 2)Listbox列表框控件
     所用的函数
  GetCurSel()来获取鼠标焦点的值的序号
  AddString()来向列表框控件里面添加内容
 3)MFC ColorButton颜色选取控件
     所用的函数
  Getcolor()获取当前选择的颜色,类型是COLORREF,以RGB的形式储存
 4)Static Text静态文本控件
     用来制作前台界面
 5)Edit Control编辑文本控件
     用来让用户输入参数值

二.开始MFC编程
根据课程设计文档里要实现的功能来一步步编程
1.和对话框相关的功能
使用控件的消息映射以及相关的成员函数来实现
1)初始化对话框(初始化函数)
向下拉框以及列表框里面添加内容
BOOL WAttribute::onInitDialog()
{
CDialogEx::onInitDialog();

     // TODO:  在此添加额外的初始化
     CComboBox* pComboBox = (CComboBox*)GetDlgItem(IDC_COMBO);
     pComboBox->AddString(L"正方形");
     pComboBox->AddString(L"矩形");
     pComboBox->AddString(L"圆形");
     pComboBox->AddString(L"椭圆形");
     pComboBox->AddString(L"正三角形");
     pComboBox->AddString(L"文本");

     CListBox* pListBox = (CListBox*)GetDlgItem(IDC_LINETYPE);
     pListBox->AddString(L"SOLID");
     pListBox->AddString(L"DASH");
     pListBox->AddString(L"DASHDOT");

     pListBox = (CListBox*)GetDlgItem(IDC_FILLTYPE);
     pListBox->AddString(L"SOLID");
     pListBox->AddString(L"BDIALOGAL");
     pListBox->AddString(L"CROSS");

     m_LineColor.EnableAutomaticButton(_T("默认值"), RGB(0, 0, 0));
     m_LineColor.EnableOtherButton(_T("其余颜色"));
     m_LineColor.SetColor((COLORREF)-1);
     m_LineColor.SetColumnsNumber(10);

     m_FillColor.EnableAutomaticButton(_T("默认值"), RGB(0, 0, 0));
     m_FillColor.EnableOtherButton(_T("其余颜色"));
     m_FillColor.SetColor((COLORREF)-1);
     m_FillColor.SetColumnsNumber(10);
     return TRUE;  // return TRUE unless you set the focus to a control
     // 异常: OCX 属性页应返回 FALSE
 }

    2)选择图形类型(控件消息)
 当用户选择的不是文本类型的对话框,就把和文本框有关的控件隐藏,得到下拉框所选序号,来判断是否要隐藏
 void WAttribute::onCbnSelchangeCombo()
 {
     // TODO: 在此添加控件通知处理程序代码
     int nIndex = m_ComboxType.GetCurSel();
     if (nIndex != 2)
     {
  CStatic* pStatic1 = (CStatic*)GetDlgItem(IDC_TEXT);
  pStatic1->ShowWindow(SW_HIDE);
  CEdit* pEdit1 = (CEdit*)GetDlgItem(IDC_STEXTEDIT);
  pEdit1->ShowWindow(SW_HIDE);

  pStatic1 = (CStatic*)GetDlgItem(IDC_TEXTANGLE);
  pStatic1->ShowWindow(SW_HIDE);
  pEdit1 = (CEdit*)GetDlgItem(IDC_EDITANGLE);
  pEdit1->ShowWindow(SW_HIDE);
     }
     else
     {
  CStatic* pStatic1 = (CStatic*)GetDlgItem(IDC_TEXT);
  pStatic1->ShowWindow(SW_SHOW);
  CEdit* pEdit1 = (CEdit*)GetDlgItem(IDC_STEXTEDIT);
  pEdit1->ShowWindow(SW_SHOW);

  CStatic* pStatic2 = (CStatic*)GetDlgItem(IDC_TEXTANGLE);
  pStatic2->ShowWindow(SW_SHOW);
  CEdit* pEdit2 = (CEdit*)GetDlgItem(IDC_EDITANGLE);
  pEdit2->ShowWindow(SW_SHOW);
     }
 }
    3)选择颜色(控件消息)
 获取边框线型的颜色以及填充颜色,因为我这里定义的是颜色控件变量是control的变量类型,所以还要使用该类型的函数才能获取颜色的color值,其实如果仅是为了获取颜色值,可以直接给该控件绑定一个value的变量类型,我在这里还对颜色控件做了一些定制(这个是从网上学到的,博客地址http://blog.csdn.net/akof1314/article/details/5947518)详细代码见初始化对话框函数

 void WAttribute::onBnClickedLinecolor()
 {
     // TODO: 在此添加控件通知处理程序代码
     m_nLineColor = m_LineColor.GetColor();
     if (m_nLineColor == -1)
     {
  m_nLineColor = m_LineColor.GetAutomaticColor();
     }
 }

 void WAttribute::onBnClickedFillcolor()
 {
     // TODO: 在此添加控件通知处理程序代码
     m_nFillColor = m_FillColor.GetColor();
     if (m_nFillColor == -1)
     {
  m_nFillColor = m_FillColor.GetAutomaticColor();
     }
 }
    4)ok控件(控件消息)
    把一些想在对话框点击确定后想保存的值:下拉框的选项,列表框的选项保存下来,供view文件使用,采取在对话框类里面自定义变量来实现
    void WAttribute::onBnClickedOk()
    {
 // TODO: 在此添加控件通知处理程序代码
 m_nComboxIndex = m_ComboxType.GetCurSel();//获取下拉框的索引
 m_nLineTypeIndex = m_LineType.GetCurSel();//获取线型列表框选择内容
 m_nFilltypeIndex = m_FillType.GetCurSel();//获取填充类型列表框选择内容
 CDialogEx::onOK();
    }

2.Doc数据文件
    1)使用一个动态数组来存放视图中绘制的图形的数组,则每个绘制图形就是数组中的一个元素
    (动态数组的实现原理:添加新的元素后,就把当前所有的元素copy以及新增的元素,再开辟一块新的内存空间来存放新的数组,实际上也是通过静态的普通数组来实现的)
 CObArray m_Elements;

    2)析构函数
 释放每个图形的所占用的资源,以及这个动态数组
 CWDemoDoc::~CWDemoDoc()
 {
     //析构 父类指针指向的图形资源 函数
     int i;
     WShape* p;
     if (m_Elements.GetCount() > 0)
     {
  for (i = 0; i < m_Elements.GetCount(); i++)
  {
      p = (WShape*)m_Elements[i];
      delete p;
  }
     }
     m_Elements.RemoveAll();//释放数组
 }

    3)序列化函数
 每个数组元素调用自己的序列化函数,面向对象的封装思想
 void CWDemoDoc::Serialize(CArchive& ar)
 {
     m_Elements.Serialize(ar);
 }

3.View文件
    1)鼠标左键加上Ctrl键弹出对话框功能
 1)设备坐标转化为逻辑坐标(参考博客:http://blog.csdn.net/xuxiaofei77/article/details/5734257)
 2)判断Ctrl键是否按下,来决定是否创建一个对话框
 3)获取鼠标点击的位置,要放在对话框对象之后,对话框弹出之前
 4)弹出对话框后根据用户选择的图形类型来创建相对应的图形及文本,我这里是通过比较用户选择的序号,用switch语句实现的
 5)每次在创建图形后要把它加入到动态数组中,最后刷新界面窗口,调用View文件的OnDraw函数绘制对应的图形
    2)只按下鼠标左键,而没有按下Ctrl键,则需要判断鼠标当前的落点又没在某个图形内
 1)把当前图形的相关参数传入到对话框中,供用户修改图形的相关的参数
 2)当用户按下OK键后,再把对话框界面的值传入到当前图形中,重新设置图元的属性的函数,这样刷新窗口后就改变当前图形
 void CWDemoView::onLButtonDown(UINT nFlags, CPoint point)
 {
     // TODO: 在此添加消息处理程序代码和/或调用默认值
     CWDemoDoc* pDoc = Getdocument();
     CClientDC   dc(this);
     CPoint pntLogical = point;
     onPrepareDC(&dc);
     dc.DPtoLP(&pntLogical);//DP->LP进行转换 

     if ((nFlags&MK_CONTROL) == MK_CONTROL)//Ctrl键按下
     {
  WAttribute dlg;
  //记录鼠标按下的位置
  dlg.m_ShapeX = pntLogical.x;
  dlg.m_ShapeY = pntLogical.y;

  //焦点是否在确定键上,弹出对话框
  if (dlg.DoModal() == IDOK)
  {
      //获取用户修改位置的坐标
      pntLogical.x = dlg.m_ShapeX;
      pntLogical.y = dlg.m_ShapeY;

      //获取用户输入的宽度和高度
      int width = dlg.m_ShapeWidth;
      int height = dlg.m_ShapeHeight;

      //根据对话框用户选择的下拉框的选项索引绘制对应的图形
      switch (dlg.m_nComboxIndex)
      {
      case 0:
      {
   WRectangle* p = NULL;
   p = new WRectangle(pntLogical.x, pntLogical.y, width, height);

   p->BoderWidth = dlg.m_LineWidth;
   p->BoderType = dlg.m_nLineTypeIndex;
   p->BoderColor = dlg.m_nLineColor;

   p->FillType = dlg.m_nFilltypeIndex;
   p->FillColor = dlg.m_nFillColor;

   pDoc->m_Elements.Add(p);
   break;
      }
      case 1:
      {
   WEllipse * p;
   p = new WEllipse(pntLogical.x, pntLogical.y, width, height);

   p->BoderWidth = dlg.m_LineWidth;
   p->BoderType = dlg.m_nLineTypeIndex;
   p->BoderColor = dlg.m_nLineColor;

   p->FillType = dlg.m_nFilltypeIndex;
   p->FillColor = dlg.m_nFillColor;

   pDoc->m_Elements.Add(p);
   break;
      }
      case 2:
      {
   WText *p = NULL;
   CString content = dlg.m_TextContent;
   p = new WText(pntLogical.x, pntLogical.y, content);

   p->Angle = dlg.m_Angle;
   p->BoderWidth = dlg.m_LineWidth;
   p->BoderType = dlg.m_nLineTypeIndex;
   p->BoderColor = dlg.m_nLineColor;

   p->FillType = dlg.m_nFilltypeIndex;
   p->FillColor = dlg.m_nFillColor;

   pDoc->m_Elements.Add(p);
   break;
      }
      case 3:
      {
   WCircle *p = NULL;
   p = new WCircle(pntLogical.x, pntLogical.y, width);

   p->BoderWidth = dlg.m_LineWidth;
   p->BoderType = dlg.m_nLineTypeIndex;
   p->BoderColor = dlg.m_nLineColor;

   p->FillType = dlg.m_nFilltypeIndex;
   p->FillColor = dlg.m_nFillColor;

   pDoc->m_Elements.Add(p);
   break;
      }
      case 4:
      {
   WSquare* p = NULL;
   p = new WSquare(pntLogical.x, pntLogical.y, width);

   p->BoderWidth = dlg.m_LineWidth;
   p->BoderType = dlg.m_nLineTypeIndex;
   p->BoderColor = dlg.m_nLineColor;

   p->FillType = dlg.m_nFilltypeIndex;
   p->FillColor = dlg.m_nFillColor;

   pDoc->m_Elements.Add(p);
   break;
      }
      case 5:
      {
   WTriangle* p = NULL;
   p = new WTriangle(pntLogical.x, pntLogical.y, width);

   p->BoderWidth = dlg.m_LineWidth;
   p->BoderType = dlg.m_nLineTypeIndex;
   p->BoderColor = dlg.m_nLineColor;

   p->FillType = dlg.m_nFilltypeIndex;
   p->FillColor = dlg.m_nFillColor;

   pDoc->m_Elements.Add(p);
   break;
      }
      default:
   MessageBox(L"请您选择图形类型!");
   break;
      }
      Invalidate();//刷新窗口
  }

     }
     else
     {
  //未按下Ctrl键时左击,则逐个比较,看是否命中图元
  int i;
  WShape* p;
  for (i = 0; i < pDoc->m_Elements.GetCount(); i++)
  {
      p = (WShape*)pDoc->m_Elements[i];
      if (p->IsMatched(pntLogical))
      {
   //修改图元属性,从图元属性值里面取值赋值给对话框的变量
   WAttribute dlg;

   dlg.m_ShapeX = p->OrgX;
   dlg.m_ShapeY = p->OrgY;

   if (dlg.DoModal() == IDOK)
   {
//利用改了以后对话框中图元的属性更新到文档图元数组的对象中
p->OrgX = dlg.m_ShapeX;
p->OrgY = dlg.m_ShapeY;

p->BoderColor = dlg.m_LineColor.GetColor();
p->BoderWidth = dlg.m_LineWidth;
p->BoderType = dlg.m_nLineTypeIndex;

p->FillColor = dlg.m_FillColor.GetColor();
p->FillType = dlg.m_nFilltypeIndex;

p->SetAttribute(p->OrgX, p->OrgY, p->BoderColor, p->BoderType, p->BoderWidth, p->FillColor, p->FillType);

   }
   Invalidate();//刷新窗口
      }
  }
     }
     CScrollView::onLButtonDown(nFlags, point);
 }
    3)双击鼠标右键删除当前图元
    这个消息映射需要在使用时,需要注意把MFC框架自带的鼠标右键消息禁用,不然会有冲突
 1)还是先把设备坐标转化为逻辑坐标
 2)用父类指针指向子类对象,调用子类对象的IsMatched函数,循环查找用户选中的是动态数组中的哪个图形,找到后使用动态数组对象自带的删除元素函数删除
 3)刷新窗口,图形消失
 void CWDemoView::onRButtonDblClk(UINT nFlags, CPoint point)
 {
     // TODO: 在此添加消息处理程序代码和/或调用默认值
     CWDemoDoc* pDoc = Getdocument();
     int i;
     WShape* p;
     CClientDC   dc(this);
     CPoint pntLogical = point;
     onPrepareDC(&dc);
     dc.DPtoLP(&pntLogical);//DP->LP进行转换 

     for (i = 0; i < pDoc->m_Elements.GetCount(); i++)
     {
  p = (WShape*)pDoc->m_Elements[i];
  if (p->IsMatched(pntLogical))
  {
      AfxMessageBox(L"你是否要删除?");
      pDoc->m_Elements.RemoveAt(i);
  }
     }
     Invalidate();//刷新窗口
     CScrollView::onRButtonDblClk(nFlags, point);
 }
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/232988.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号