MFC之Scribble例子

       在MSDN中看着英文版的document中的scribble的例子,发现:自己的英语不够用,而且MSDN里面的教程没有目录,看着太乱了。想了想还是自己梳理一下,看着比较有条理。(MFC绘图常用函数:http://blog.sina.com.cn/s/blog_1362f005c0102vyem.html

零、知识了解

       Scribble演示了MFC编程模型,它将程序数据(文档)的存储与该数据的显示(视图)以及大多数用户与数据的交互分开。此模型支持多个视图,多个文档类型,拆分器窗口和其他有价值的用户界面功能。文档/视图的核心是四个关键类:

       要更好地了解文档/视图模型如何适用于您的应用程序,请研究下表:

        此表显示如何在框架应用程序中创建和管理文档和其他对象。

此表显示了您在实施文档时的职责和框架的职责。

step1:https://mindmap.airmore.cn/doc/d3a15252fbca1e73026d7492600bc41a

 步骤:

一、先创建一个多文档(MDI)的MFC应用程序。

二、创建文档(doc)

           Scribble中的文档存储构成绘图的线条或“笔划”。由于绘图通常由许多笔划组成,因此文档存储用户绘制的所有笔划的列表。下图显示了在Scribble文档视图中绘制的单个笔划。

         成员变量  m_strokeList用于存储笔划列表和成员函数以管理笔划列表。

1.首先,自定义CSribbleDoc(在原有的基础上自己添加代码)

    添加1:

https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-6.0/aa733759(v=vs.60) )

成员变量:m_nPenWidthm_penCur

成员函数:InitDocument()和NewStroke()

另外,还添加了成员:m_strokeList(变量)、GetCurrentPen( )函数。   

          先声明上面的这几个成员,(但是m_strokeList和 GetCurrentPen( )由于其特殊性,就直接在.h文件中定义了)

CTypedPtrList<CObList, CStroke*> m_strokeList;  

/*CTypedPtrList类是个模板类,一般在软件开发中用到的时候比较多,它的作用就是类似一个链表,下面是这个类的原型:
template < class BASE_CLASS, class TYPE>
  class CTypedPtrList : public BASE_CLASS
    第一个参数说明是列表的基类,这里必须是个指针列表类(Coblist或者CPtrlist,自己觉得用其他的列表类也可以,只要是指针列表类);
    第二个参数指在列表类中所存放的类型。

    为什么不直接用Coblist或者CPtrlist呢?这是有原因的,因为CTypedPtrList类又进一步的封装了
CPtrlist,并且为消除了一些错误,还提供了一些类型的强制转化,所以说一般都是用CTypePtrList这个模板
类,你也不用担心这个类封装以后会变的速度慢,因为CTypedPtrList类封装以后里面的函数是内联的,
如果你想真正想掌握这个模板类还是需要看看他提供的成员函数,下面是它的成员函数:


CPen*   GetCurrentPen( ) { return &m_penCur; };

              然后,在定义这些成员:

/*1、对InitDocument()*/
m_nPenWidth = 2;  // Default 2-pixel pen width
// Solid black pen
m_penCur.CreatePen( PS_SOLID, m_nPenWidth, RGB(0,0,0) );

         【注】①CTypedPtrList的详解:https://www.cnblogs.com/VCdog/archive/2010/04/12/1709918.html;又因为CTypedPtrList类封装后里面的函数都是内联函数所以它最好在.h(头文件)中完成定义

inline函数(即内联函数)对编译器而言必须是可见的,以便能够在调用点展开该函数,与非inline函数不同的是,inline函数必须在调用该函数的每个文件中定义。当然,对于同一程序的不同文件,如果inline函数出现的话,其定义必须相同。

正因为如此,建议把inline函数的定义放到头文件中,在每个调用该inline函数的文件中包含该头文件。这种方法保证了每个inline函数只有一个定义,且程序员无需复制代码,并且不可能在程序的生命周期中引起无意的不匹配的事情。

——摘自《C++ Primer》(第三版)

https://blog.csdn.net/Ronnie_Hu/article/details/62238311

                    ②对于函数GetCurrentPen( ),是内联函数的解释:设备描述表(DC, Device Context)。DC是一个数据结构,当程序向GDI设备中绘图时,需要访问该设备的DC。MFC将GDI的DC封装在C++类中,包括CDC类和CDC派生类,这些类中的许多成员都是对本地GDI绘图函数进行简单封装而形成的内联函数。【CDC::GetCurrentPen,即GetCurrentPen( )是内联函数

添加2(准确说是:覆盖基类函数):

https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-6.0/aa748590(v=vs.60)

成员函数:OnOpenDocument()和DeleteContents()

 

        此次,使用“类向导”在CScribbleDoc.h中声明两个成员函数:OnOpenDocumentDeleteContents。(https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-6.0/aa748590(v=vs.60)

        然后,要在CScribbleDoc.cpp中为这两个函数定义初始化:(https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-6.0/aa232160%28v%3dvs.60%29

2. 将AfxTempl.h添加在StdAfx.h

https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-6.0/aa716362(v=vs.60)

在上一步当中,用到C++集合模板类中的:CTypedPtrList和。在接下来声明CStroke类时,使用的CArray。

        其中,所有模板集合类都在头文件AfxTempl.h中定义。这个MFC提供的头文件在开发Scribble应用程序的过程中不会改变,因此将其添加到Scribble的预编译头StdAfx.h中。(StdAfx.h是由AppWizard创建的,用于保存要预编译的头文件列表。它由#include语句列表组成,后跟头文件的名称。)

3.创建CStroke类

        在Scribble中,笔划由一系列点组成。当用户拖动鼠标进行绘制时,Scribble会收集点并将它们存储为当前笔划的一部分。从鼠标左键按下的时间点到从Scribble绘图的一个笔划中释放的时间点收集的点。下图显示了Scribble的数据结构。Scribble使用CPen类的对象进行绘制。

     添加一个CStroke类

(在CScribbleDoc类中的 ScribbleDoc.h 添加),还要声明成员。

          (1)声明CStroke类 https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-6.0/aa716352(v=vs.60)

      首先在ScribbleDoc.h中声明。

       然后,在该类中声明其成员:                 (2)为CStroke类的成员进行定义初始化:

https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-6.0/aa716400(v=vs.60)

https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-6.0/aa232213%28v%3dvs.60%29

/*1、为该类的两个构造函数惊醒定义*/

//空构造函数
CStroke::CStroke()
{
// This empty constructor should be used by
// the application framework for serialization only
}
在ScribbleDoc.h中声明受保护的第一个构造函数在CStroke对象序列化期间仅由应用程序框架使用。
它的参数列表和函数体是空的。

//另一个
CStroke::CStroke(UINT nPenWidth)
{
m_nPenWidth = nPenWidth;
}
第二个构造函数供公众使用,直接构造新的笔画对象。当它构造一个新的笔触对象时,
公共构造函数初始化笔的宽度。CStroke不声明它自己的析构函数 - 它依赖于CObject默认提供一个。

 

/*2、对DrawStroke()函数进行定义初始化*/
BOOL CStroke::DrawStroke( CDC* pDC )
{
CPen penStroke;
if( !penStroke.CreatePen(PS_SOLID, m_nPenWidth, RGB(0,0,0)))
return FALSE;
CPen* pOldPen = pDC->SelectObject( &penStroke );
pDC->MoveTo( m_pointArray[0] );
for( int i=1; i < m_pointArray.GetSize(); i++ )
{
pDC->LineTo( m_pointArray[i] );
}
pDC->SelectObject( pOldPen );
return TRUE;
}
此代码传递DrawStroke指向CDC类对象的指针,该对象封装了Windows设备上下文(DC)。

  【注意】在使用MFC编写的程序中,所有图形调用都是通过类 CDC的设备上下文对象或其派生类之一进行的。

    DrawStroke:

           调用CDC成员函数 - SelectObjectMoveToLineTo - 通过指针选择图形设备接口(GDI)笔到设备上下文中并移动笔      和绘图。

           构造一个新的CPen对象,并通过调用笔的CreatePen成员函数使用当前属性对其进行初始化。

                                        注意    这种两阶段结构是典型的框架对象。

          调用SelectObject以将笔选择到设备上下文中(将现有笔保存为pOldPen)。

          调用MoveTo将笔定位到第一个点。

          迭代点数组。

          调用设备上下文的LineTo成员函数将前一个点与下一个点连接起来。

          通过重新安装旧笔,将设备上下文恢复为先前的状态。

????不应该在这里吧????????????????????????????????
为视图的OnDraw成员函数添加实现代码

  使用WizardBar跳转到类的启动器OnDraw成员函数CScribbleView。

  使用ASSERT_VALID(pDoc)以下代码替换行后的// TODO注释:
// The view delegates the drawing of individual strokes to
// CStroke::DrawStroke( ).
CTypedPtrList<CObList, CStroke*>& strokeList =
pDoc->m_strokeList;
POSITION pos = strokeList.GetHeadPosition( );
while (pos != NULL)
{
CStroke* pStroke = strokeList.GetNext(pos);
pStroke->DrawStroke( pDC );
}
视图使用指针迭代笔划列表,告诉每个笔划绘制自己。当OnDraw调用DrawStroke给定的笔画对象时,
它会传递它作为参数接收的设备上下文对象。(数据绘制本身只是一种可能的策略。)

要完成Scribble的绘图,还必须将DrawStroke成员函数定义添加到类中CStroke。

 

  3. 利用 类向导 在CSribbledoc类中,添加虚函数: OnOpenDocument()   和   DeleteContents()https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-6.0/aa748590(v=vs.60)          

                                      

 

step2:https://share.mindmanager.com/#publish/9M6FiNbEqNKD4k_4878bUAffJ9n1y2SIF3m8_B7n

 

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值