zoukankan      html  css  js  c++  java
  • 【VS开发】CFormView

    原文地址: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实现对话框的处理分析完毕。
  • 相关阅读:
    自然语言交流系统 phxnet团队 创新实训 项目博客 (十一)
    install ubuntu on Android mobile phone
    Mac OS, Mac OSX 与Darwin
    About darwin OS
    自然语言交流系统 phxnet团队 创新实训 项目博客 (十)
    Linux下编译安装qemu和libvirt
    libvirt(virsh命令总结)
    深入浅出 kvm qemu libvirt
    自然语言交流系统 phxnet团队 创新实训 项目博客 (九)
    自然语言交流系统 phxnet团队 创新实训 项目博客 (八)
  • 原文地址:https://www.cnblogs.com/huty/p/8518702.html
Copyright © 2011-2022 走看看