原文地址:CFormView作者:罗纳尔多
CFormView是MFC使用无模式对话框的一个典型例子。CFormView是基于对话框模板创建的视,它的直接基类是CSrcollView,CSrcollView的直接基类才是CView。所以,这里先对CScorllView作一个简要的介绍。

CScrollView

CScrollView继承了CView的特性,并且增加了如下的功能:

(1)管理映射模式、窗口尺寸、视口尺寸(Map mode、Window and Viewport size)。Window and Viewport size用来完成页面空间到设备空间的转换。

(2)自动管理滚动条,响应滚动条消息。

为了实现这些功能,CScrollView覆盖CView或者CWnd的一些虚拟函数和消息处理函数,添加了一些新的函数,当然也设计了新的成员变量。

CscrollView新的成员变量

protected:

int m_nMapMode;

CSize m_totalLog; // total size in logical units (no rounding)

CSize m_totalDev; // total size in device units

CSize m_pageDev; // per page scroll size in device units

CSize m_lineDev; // per line scroll size in device units

BOOL m_bCenter; // Center output if larger than total size

BOOL m_bInsideUpdate; // internal state for OnSize callback

CScrollView新的成员函数,用来完成和滚动操作、滚动条等有关的功能

void SetScaleToFitSize(SIZE sizeTotal);

void SetScrollSizes(int nMapMode, SIZE sizeTotal,

const SIZE& sizePage = sizeDefault,

const SIZE& sizeLine = sizeDefault);

这两个函数中的尺寸大小按逻辑单位计算。

SetScaleToFitSize设置视口尺寸为当前的窗口尺寸,这样,在没有滚动条时,逻辑视的内容被放大或者缩小到正好窗口大小。

SetScrollSizes设置窗口的映射模式,窗口尺寸,页和行尺寸。sizeDefualt被定义为(0,0)。

下面几个函数用来实现滚动或者得到滚动条相关的信息

void ScrollToPosition(POINT pt); // set upper left position

void FillOutsideRect(CDC* pDC, CBrush* pBrush);

void ResizeParentToFit(BOOL bShrinkOnly = TRUE);

CPoint GetScrollPosition() const; // upper corner of scrolling

CSize GetTotalSize() const; // logical size

下面两个函数使用了设备坐标单位

CPoint GetDeviceScrollPosition() const;

void GetDeviceScrollSizes(int& nMapMode, SIZE& sizeTotal,

SIZE& sizePage, SIZE& sizeLine) const;

覆盖的消息处理函数

处理WM_SIZE的OnSize;

处理WM_HSCROLL的OnHScroll;

处理WM_VSCROLL的OnVScroll;

覆盖的虚拟函数

CWnd的CalcWindowRect

CView的OnPrepareDC、OnScroll、OnScrollBy

用于DEBUG的Dump和AssertValid

这里,覆盖的消息处理函数和虚拟函数共同完成对滚动条、滚动消息的处理。

在CSrcollView的实现涉及到许多和Windows映射模式、坐标转换等相关的函数的使用。这里,不作具体讨论。

CFormView

CFormView派生于CSrcollView,本身没有增加新的函数,但覆盖了一些基类的虚拟函数,增加了几个成员变量(以下列出的不包含OLE处理)。

增加的成员变量

LPCTSTR m_lpszTemplateName;

CCreateContext* m_pCreateContext;

HWND m_hWndFocus; // last window to have focus

m_lpszTemplateName用来保存创建视图的对话框模板的名称,_pCreateContext用来保存创建上下文,m_hWndFocus用来保存最近一次拥有焦点的控制窗口。在构造CFormView对象时,构造函数把有关信息保存到成员变量中,如下所示:

CFormView::CFormView(LPCTSTR lpszTemplateName)

{

m_lpszTemplateName = lpszTemplateName;

m_pCreateContext = NULL;

m_hWndFocus = NULL; // focus window is font

}

覆盖的虚拟函数

virtual void OnDraw(CDC* pDC); // MFC缺省处理空

virtual BOOL Create(LPCTSTR, LPCTSTR, DWORD,

const RECT&, CWnd*, UINT, CCreateContext*);

virtual BOOL PreTranslateMessage(MSG* pMsg);

virtual void OnActivateView(BOOL, CView*, CView*);

virtual void OnActivateFrame(UINT, CFrameWnd*);

创建基于对话框的视窗口,不同于创建普通视窗口(前者调用CWnd::CreateEx,后者调用CWnd::CreateDlg),故需要覆盖Create虚拟函数。

覆盖PreTranslateMessage是为了过滤对话框消息,把一些消息让CFormView对象来处理。

覆盖了两个消息处理函数:

afx_msg int OnCreate(LPCREATESTRUCT lpcs);

afx_msg void OnSetFocus(CWnd* pOldWnd);

下面,分析几个函数作。Create函数解释了MFC如何使用一个对话框作为视的方法,PreTranslateMessage显示了CFormView不同于CDialog的实现。

CFormView的创建

设计CFormView的创建函数,必须考虑两个问题:

首先,CFormView是一个视,其创建函数必须是一个虚拟函数,原型必须和CWnd::Create(LPSTR…pContext)函数一致,见图5-13视的创建。其次,CFormView使用了对话框创建函数和对话框“窗口类”来创建视,但必须作一些处理使得该窗口具备视的特征。

Create的实现如下:

BOOL CFormView::Create(LPCTSTR ,

LPCTSTR ,

DWORD dwRequestedStyle, const RECT& rect, CWnd* pParentWnd, UINT nID,

CCreateContext* pContext)

{

ASSERT(pParentWnd != NULL);

ASSERT(m_lpszTemplateName != NULL);

m_pCreateContext = pContext; // save state for later OnCreate

#ifdef _DEBUG

// dialog template must exist and be invisible with WS_CHILD set

if (!_AfxCheckDialogTemplate(m_lpszTemplateName, TRUE))

{

ASSERT(FALSE); // invalid dialog template name

PostNcDestroy(); // cleanup if Create fails too soon

return FALSE;

}

#endif //_DEBUG

//若common control window类还没有注册,则注册

VERIFY(AfxDeferRegisterClass(AFX_WNDCOMMCTLS_REG));

// call PreCreateWindow to get prefered extended style

CREATESTRUCT cs; memset(&cs, 0, sizeof(CREATESTRUCT));

if (dwRequestedStyle == 0)

dwRequestedStyle = AFX_WS_DEFAULT_VIEW;

cs.style = dwRequestedStyle;

if (!PreCreateWindow(cs))

return FALSE;

//::CreateDialogIndirect间接被调用来创建一个无模式对话框

if (!CreateDlg(m_lpszTemplateName, pParentWnd))

return FALSE;

//创建对话框时,OnCreate被调用,m_pCreateContext的作用结束了

m_pCreateContext = NULL;

// we use the style from the template - but make sure that

// the WS_BORDER bit is correct

// the WS_BORDER bit will be whatever is in dwRequestedStyle

ModifyStyle(WS_BORDER|WS_CAPTION, cs.style & (WS_BORDER|WS_CAPTION));

ModifyStyleEx(WS_EX_CLIENTEDGE, cs.dwExStyle & WS_EX_CLIENTEDGE);

SetDlgCtrlID(nID);

CRect rectTemplate;

GetWindowRect(rectTemplate);

SetScrollSizes(MM_TEXT, rectTemplate.Size());

// initialize controls etc

if (!ExecuteDlgInit(m_lpszTemplateName))

return FALSE;

// force the size requested

SetWindowPos(NULL, rect.left, rect.top,

rect.right - rect.left, rect.bottom - rect.top,

SWP_NOZORDER|SWP_NOACTIVATE);

// make visible if requested

if (dwRequestedStyle & WS_VISIBLE)

ShowWindow(SW_NORMAL);

return TRUE;

}

从Create的实现过程可以看出,CreateDialog在创建对话框时使用了Windows预定义的对话框“窗口类”,PreCreateWindow返回的cs在创建对话框窗口时并没有得到体现,所以在CFormView::Create调用PreCreateWindow让程序员修改“窗口类”的风格之后,还要调用ModifyStyle和ModifyStyleEx来按PreCreateWindow返回的cs的值修改窗口风格。

回顾视窗口的创建过程,Create函数被CFrameWnd::CreateView所调用,参数nID取值AFX_IDW_PANE_FIRST。由于CreateDlg设置对话框窗口的ID为对话框模板的ID,所以需要调用函数SetDlgCtrlID(nID)设置视窗口ID为nID(即AFX_IDW_PANE_FIRST)。

由于CFormView是从CScrollView继承,所以调用SetScrollSize设置映射模式,窗口尺寸等。

完成上述动作之后,初始化对话框的控制子窗口。

最后,必要的话,显示视窗口。

这样,一个无模式对话框被创建,它被用作当前MDI窗口或者MDI子窗口的视。如同CDialog的消息处理一样,必要时,消息或者事件将传递给视原来的窗口过程(无模式对话框的原窗口过程)处理,其他的消息处理和通常视一样。

由于是调用对话框创建函数创建视窗口,所以不能向::CreateWindowEX传递创建上下文指针,于是把它保存到成员变量m_pCreateContext中,在OnCreate时使用。OnCreate的实现如下:

int CFormView::OnCreate(LPCREATESTRUCT lpcs)

{

//既然不能通过CreateDialog使用参数传递的方法得到创建上下文

//参数,则使用一个成员变量来传递

return CScrollView::OnCreate(lpcs);

}

CFormView的消息预处理

现在,讨论CFormView 的PreTranslateMessage函数。CDialog覆盖函数PreTranslateMessage的主要目的是处理Tooltip消息、Escape键盘消息和Dialog消息。CFormView覆盖该函数的目的是处理Tooltip消息和Dialog消息。CFormView和CDialog不同之处在于CFormView是一个视,故在把键盘消息当Dialog消息处理之前,必须优先让其父窗口检查按下的键是否是快捷键。PreTranslateMessage函数实现如下:

BOOL CFormView::PreTranslateMessage(MSG* pMsg)

{

ASSERT(pMsg != NULL);

ASSERT_VALID(this);

ASSERT(m_hWnd != NULL);

//过滤Tooltip消息

if (CView::PreTranslateMessage(pMsg))

return TRUE;

//SHIFT+F1上下文帮助模式下,不处理Dialog消息

CFrameWnd* pFrameWnd = GetTopLevelFrame();

if (pFrameWnd != NULL && pFrameWnd->m_bHelpMode)

return FALSE;

//既然IsDialogMessage将把窗口快捷键解释成Dialog消息

//所以在此先调用所有父边框窗口的消息预处理函数

pFrameWnd = GetParentFrame(); // start with first parent frame

while (pFrameWnd != NULL)

{

// allow owner & frames to translate before IsDialogMessage does

if (pFrameWnd->PreTranslateMessage(pMsg))

return TRUE;

// try parent frames until there are no parent frames

pFrameWnd = pFrameWnd->GetParentFrame();

}

// 过滤来自子窗口的消息或者给对话框的消息

return PreTranslateInput(pMsg);

}

由于CFormView是一个视,不是模式对话框,所以它首先要把消息给父窗口(MDI子窗口或者MDI窗口)预处理,如果它们不能处理,则调用PreTranslateInput来过滤Dialog消息。

CFormView的输入焦点

CFormView另一个特性是:在和用户交互中,如果用户离开视窗口,则必须保存CFormView视的哪个控制子窗口拥有输入焦点,以便在重新激活视窗口时,原来的那个窗口重新获得输入焦点。所以,CFormView覆盖了虚拟函数OnActivateView和OnActiveFrame,以便在视窗口失去激活时把它的当前输入焦点保存到成员变量m_hWndFocus中。

为了在适当时候恢复输入焦点,CFormView覆盖了消息处理函数OnSetFocus,以便在视获得输入焦点时把输入焦点传递给m_hWndFocus(如果非空)。

至此,MFC实现对话框的处理分析完毕。

【VS开发】CFormView的更多相关文章

  1. 【VS开发】关于在CFormView中实现CListCtrl控件的注意事项

    [VS开发]关于在CFormView中实现CListCtrl控件的注意事项 标签(空格分隔): [VS开发] 今天调试中发现了一项非常令人恼怒的事情,本来早都知道在CFormView中没有了像在对话框 ...

  2. 【VS开发】EasySize使用设置CFormView空间自适应view窗口大小

    1.在stdafx.h中引用EasySize.h头文件(同时将EasySize.h放到你的程序目录中) 2.在类定义中添加DECLARE_EASYSIZE [cpp] view plain copy ...

  3. 如何在其他类中实现继承自CFormView类的对象

    今天项目开发中,我们创建了一个对话框资源,并创建了一个派生自CFormView的类(假设为CMyClassDlg)来管理它. CMyClassDlg.h #pragma once // CMyClas ...

  4. CFormView动态调整对话框的尺寸和调整比例控制的部署

    基于单个文件CFormView动态调整对话框的尺寸和调整比例控制的部署 假设你正在开发一个程序基于单个文件,使用CFormView基类来实现多种形式展示,那么,这个文件可能会给你一点帮助. 一.实现对 ...

  5. VC、OpenGL、ArcGIS Engine开发的二维三维结合的GIS系统

    一.前言 众所周知,二维GIS技术发展了近四十年,伴随着计算机软硬件以及关系型数据库的飞速发展,二维GIS技术已日臻完善.在对地理信息的分析功能上有着无可比拟的优势.一些宏观的地理信息,一维的地理信息 ...

  6. 【VS开发】关于各种View的实现总结

    [VS开发]关于各种View的实现总结 标签(空格分隔): [VS开发] 最近两天整理了一下各种View的实现,实际上各种View也只是实现了对应Dialog对话框中的一些控件而已,比如CListCt ...

  7. 【VS开发】单文档中往视图中加入控件

    [VS开发]单文档中往视图中加入控件 标签(空格分隔): [VS开发] 分隔视图的但文档窗口,要显示控件,推荐使用CFormView或者CCtrlView,前者和对话框的做法一致. 在MainFram ...

  8. 避免重复造轮子的UI自动化测试框架开发

    一懒起来就好久没更新文章了,其实懒也还是因为忙,今年上半年的加班赶上了去年一年的加班,加班不息啊,好了吐槽完就写写一直打算继续的自动化开发 目前各种UI测试框架层出不穷,但是万变不离其宗,驱动PC浏览 ...

  9. App开发:模拟服务器数据接口 - MockApi

    为了方便app开发过程中,不受服务器接口的限制,便于客户端功能的快速测试,可以在客户端实现一个模拟服务器数据接口的MockApi模块.本篇文章就尝试为使用gradle的android项目设计实现Moc ...

随机推荐

  1. javascript内置对象一:String

    <script>    //string.split("-"),以字符"-"把string拆分成一个数组.返回一个数组    //array.joi ...

  2. .net core 反编译一小段

    public static class A { private static readonly MethodInfo GetServiceInfo; public static IApplicatio ...

  3. ZurmoCRM 可执行代码高危风险报告及修复

    鉴于目前ZumroCRM已经云端化,其开源版本的代码基本没有人维护,也没有地方提交bug清单.但相信国内有一些用户在使用这个开源的版本,下面报告一个ZumoCRM的重大风险点. 风险描述: 当用户登录 ...

  4. HGOI 20191106 题解

    Problem A  旅行者 有$n$种转移装置,每种转移装置本质相同,每种装置可以前进$a_i$单位,但只有$b_i$个. 从初始坐标为$0$出发,途中不能经过$c_1,c2,...,c_m$中的任 ...

  5. PAT L2-001 紧急救援 —— (多参数最短路)

    和天梯中的直捣黄龙差不多.但是,通过这个问题,我对多参数最短路又有了更深一层的了解. 这题因为点数比较多,所以如果直接用大力学长的在G上dfs找最短路径的条数的话,会TLE,所以需要剪枝.剪枝方法是, ...

  6. 【Redis 向Redis中批量导入mysql中的数据(亲自测试)】

    转自:https://blog.csdn.net/kenianni/article/details/84910638 有改动,仅供个人学习 问题提出:缓存的冷启动问题 应用系统新版本上线,这时候 re ...

  7. RabbitMQ 简使用案例

    第一步导入依赖 : <dependency> <groupId>com.rabbitmq</groupId> <artifactId>amqp-clie ...

  8. FFmpeg之avformat_alloc_context()

    1. avformat_alloc_context /** * Allocate an AVFormatContext. * avformat_free_context() can be used t ...

  9. java代理浅述

    代理 代理主要可以分为: 静态代理 JDK自带的动态代理 Cglib 静态代理 静态代理比较简单,简单来说就是不想直接调用被代理类,通过代理类来实现功能.如下就是使用了静态代理 定义接口 public ...

  10. Flutter移动电商实战 --(12)首页导航区域编写

    1.导航单元素的编写 从外部看,导航是一个GridView部件,但是每一个导航又是一个上下关系的Column.小伙伴们都知道Flutter有多层嵌套的问题,如果我们都写在一个组件里,那势必造成嵌套严重 ...