Essential MFC(一)——View和GDI对象

MFC中除了应用程序框架以外,另一个非常重要的显示元素就是视图。其中,在本篇中涉及的视图是CViewCScrollView


(一)关于CView和绘图

通俗的说,View就是我们所看到的用户显示区域。默认的CView展现给用户的是一个空白的显示区域。CView类中比较重要的成员函数有:

OnInitialUpdate

OnInitialUpdate函数是窗口建立后调用的第一个函数,框架在第一次调用OnDraw前会调用OnInitialUpdate。因此它是设置滚动视图逻辑尺寸和映射模式最适合的地方。此外可以在此做一些初始化工作,链接数据库、初始化OpenGL等。

MSDN这样描述OnInitialUpdateOverride this function to perform any one-time initialization that requires information about the document. For example, if your application has fixed-sized documents, you can use this function to initialize a view's scrolling limits based on the document size. If your application supports variable-sized documents, use OnUpdate to update the scrolling limits every time the document changes.

一个简单的用法

void  CEx5cView::OnInitialUpdate()
{
    CScrollView::OnInitialUpdate();        
// 应该先调用base class的虚拟函数

    CSize sizeTotal(
2000030000);
    CSize sizePage(sizeTotal.cx 
/ 2, sizeTotal.cy / 2);
    CSize sizeLine(sizeTotal.cx 
/ 50, sizeTotal.cy / 50);
    
    SetScrollSizes(MM_HIMETRIC
, sizeTotal, sizePage, sizeLine);        
    TRACE(
"invoke OnInitialUpdate here ");
}

 

OnDraw:属于一个callback函数,用户不需自己调用。当windows认为此窗口有重绘的必要时,会自动调用OnDraw,比如窗口大小改变。用户也可以通过调用调用CView中的成员函数InvalidateRectInvalidate划出一个无效的区域通知OnDraw函数重绘该区域。

MSDN这样描述OnDrawCalled to render an image of the document for screen display, printing, or print preview. Implementation required.

OnPrepareDC在每次OnDraw函数被调用前,OnPrepareDC都会被调用,你可以在这个函数中添加自己的映射行为。

OnDraw函数会自动调用OnPrepareDC,但是,如果在你自己的消息处理函数或功能函数中需要操作CDC对象,那么你应该手动调用OnPrepareDC函数。

对于显示环境来说,在每一个消息控制函数或用户定义的功能函数的入口处,设备环境都是未初始化的(用户一般会调用CClientDC函数根据CViewthis指针创建一个CDC对象副本),此时你需要调用OnPrepareDC来初始化这个CDC对象。当函数退出之后,在该函数内部所进行的任何GDI选择(或者映射模式,或者是其他的设备环境设置)都不再有效。因此,你每次都必须从头开始设置设备环境。

此外。在OnPrepareDC中设置的映射模式在OnDraw后也会无效,所以每次OnDraw前系统都会调用OnPrepaerDC函数。

默认的OnPrepaerDC什么也不做,但是这个虚函数会被一些子类覆写。比如在CScorllView中,此函数被重新实现,根据SetScrollSizes的参数来设置坐标映射模式。

最后,尽管CView的虚成员函数OnPrepareDC(pDCCClientCD都可以正确设置),对于设置映射模式非常有用,但你仍然需要小心谨慎地管理好自己的GDI对象。

MSDN这样描述OnPrepareDCCalled before the OnDraw member function is called for screen display or the OnPrint member function is called for printing or print preview.

The default implementation of this function does nothing if the function is called for screen display. However, this function is overridden in derived classes, such as CScrollView, to adjust attributes of the device context; consequently, you should always call the base class implementation at the beginning of your override.  

OnPrepareDC用法的简单例子:

void  CEx5cView::OnLButtonDown(UINT nFlags, CPoint point)
{
    
// TODO: 在此添加消息处理程序代码和/或调用默认值
    CClientDC dc(this);
    OnPrepareDC(
&dc);    // 坐标模式映射则应该调用此函数,否则坐标是无效的
    CRect rectDevice = m_rectEllipse;
    dc.LPtoDP(rectDevice);    

    
if (rectDevice.PtInRect(point)) {
        
if (m_nColor == GRAY_BRUSH) {
            m_nColor 
= WHITE_BRUSH;
        }

        
else {
            m_nColor 
= GRAY_BRUSH;
        }

        InvalidateRect(rectDevice);
    }
    
}

注意:InvalidateRect使用的是设备坐标。设备坐标和逻辑坐标将在下面论述

void  CEx5aView::OnPrepareDC(CDC *  pDC, CPrintInfo *  pInfo)
{
    
// TODO: 在此添加专用代码和/或调用基类
    TRACE("%d ", pDC->GetMapMode() == MM_TEXT);
    pDC
->SetMapMode(MM_HIMETRIC);
    TRACE(
"%d ", pDC->GetMapMode() == MM_HIMETRIC);
    CView::OnPrepareDC(pDC, pInfo);
    TRACE(
"%d ", pDC->GetMapMode() == MM_HIMETRIC);
}

输出全为: 
1

这个小程序证明了三件事

1、在OnPrepareDC中设置的映射模式,在OnDraw后无效了,所以每次OnDraw都要重新调用一次OnPrepareDC

2、缺省的OnPrepareDC什么也没做。MSDNThe default implementation of this function does nothing if the function is called for screen display.

3、我们应该在OnPrepareDC中设定映射模式等和CDC有关的属性,而且在我们的事件处理函数中手动调用,在OnDraw函数调用前由系统自己调用。

 

(二)关于坐标映射模式

在默认情况下,我们在MM_TEXT模式下工作。也就是基于像素点的坐标模式。在此模式下一个逻辑坐标单位相当于一个像素点,x值向右为增加,y值向下为增加。这就意味着像素大小相同的图像,相对于大分辨显示器可能会显得小一些。

为了改善这个问题,windows提供了一组非常重要的固定比例显示模式,它们是:

Mapping Mode

Logical Unit

Positive y-axis Extends...

MM_TEXT

1 pixel

Downward

MM_HIMETRIC

0.01 mm

Upward

MM_TWIPS

1/ 1440 in

Upward

MM_HIENGLISH

0.001 in

Upward

MM_LOMETRIC

0.1 mm

Upward

MM_LOENGLISH

0.01 in

Upward

        以上表中的都是固定映射模式,也就是说,当窗口缩小,你的可视范围就变小了。而且,非常重要的一点,它们的坐标模式和MM_TEXT不同(x,y的正方向)。

很多MFC库函数都是用MM_TEXT模式的坐标,比如鼠标消息函数获得的鼠标坐标、类CRect的成员函数。所以有时你需要进行坐标变换。

CDC对象中的LPtoDPDPtoLP函数可以很好的完成这个工作。

  此外Windows还提供另外两种可变比例映射模式:MM_ISOTROPIC和MM_ANISOTROPIC。在这两种映射模式下,当窗口大小变化,绘制的图形大小也会变化。

例如:

void  CEx6aView::OnPrepareDC(CDC *  pDC, CPrintInfo *  pInfo)
{
    
// TODO: 在此添加专用代码和/或调用基类
    CRect clientRect;
    GetClientRect(clientRect);

    pDC
->SetMapMode(MM_ISOTROPIC);       // 比例因子不变化,画出的是圆
    
// pDC->SetMapMode(MM_ANISOTROPIC);    // 比例因子可变化,画出的是椭圆

    pDC
->SetWindowExt(10001000);        // 将窗口大小设为1000个逻辑单位高和宽
    pDC->SetViewportExt(clientRect.right, -clientRect.bottom);

    
// pDC->SetViewportOrg(clientRect.right/2, clientRect.bottom/2);

    pDC
->Ellipse(CRect(001000-1000));
}

 


(三)关于CScrollView

CScrollView继承自CView,一般在其OnInitialUpdate函数中调用SetScrollSizes函数设置卷轴的滚动距离。

void SetScrollSizes(

   int nMapMode,

   SIZE sizeTotal,

   const SIZE& sizePage = sizeDefault,

   const SIZE& sizeLine = sizeDefault

);

OnHScrollOnVScroll都是CWnd类定义的成员函数。可以控制滚动窗口进行滚动。

一个典型的应用:通过键盘控制滚动,滚动单位值根据SetScrollSizes的设置
void  CMyScrollAppView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
    
// TODO: 在此添加消息处理程序代码和/或调用默认值
    switch (nChar) {
    
case VK_HOME:
        OnVScroll(SB_TOP, 
0, NULL);
        OnHScroll(SB_LEFT, 
0, NULL);
        
break;
    
case VK_END:
        OnVScroll(SB_BOTTOM, 
0, NULL);
        OnHScroll(SB_RIGHT, 
0, NULL);
        
break;
    
case VK_UP:
        OnVScroll(SB_LINEUP, 
0, NULL);
        
break;
    
case VK_DOWN:
        OnVScroll(SB_LINEDOWN, 
0, NULL);
        
break;
    
case VK_PRIOR:
        OnVScroll(SB_PAGEUP, 
0, NULL);
        
break;
    
case VK_NEXT:
        OnVScroll(SB_PAGEDOWN, 
0, NULL);
        
break;
    
case VK_LEFT:
        OnHScroll(SB_LINELEFT, 
0, NULL);
        
break;
    
case VK_RIGHT:
        OnHScroll(SB_LINERIGHT, 
0, NULL);
        
break;
    
default:
        
break;
    }

    CScrollView::OnKeyDown(nChar, nRepCnt, nFlags);
}


(四)常用GDI对象

在讨论设备描述表时,已经多次涉及到GDI对象。这里,需强调一下:GDI对象要选入Windows 设备描述表后才能使用;用毕,要恢复设备描述表的原GDI对象,并删除该GDI对象。

常用的GDI对象有:CBitmap,这是一种和设备有关的位图,现在常用的是DIB位图对象。

CBush:刷子定义了一种位图形式的像素组合,可用于填充

CFont:字体对象

CPlaltte:调色板对象

CPen:笔用来描绘边框

CRgn:区域是由多边形、椭圆或二者组合形成的一种范围,可利用它进行填充、裁剪以及鼠标点中测试。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Essential use cases(基本用例)是指在软件开发过程中,描述系统如何满足各种功能需求的重要场景和流程。它们涵盖了系统的核心功能,对于系统的正确运行和提供基本价值至关重要。 在开发软件之前,分析师和开发人员会与客户或利益相关者进行讨论,以确定哪些功能是最为关键和必需的。这些关键功能就是 essential use cases。在这些用例中,重点关注系统和用户之间的交互,以及系统如何实现特定的功能。通过详细描述 essential use cases,开发团队能够确保系统能够满足用户的基本需求。 而 real use cases(实际用例)则是指实际环境中的使用场景和具体情况,在软件开发过程中常用来验证系统是否能够满足实际需求和用户的期望。与 essential use cases 不同的是,real use cases 更加关注系统在实际操作中的表现,并考虑用户的行为和反馈。 在 real use cases 中,开发团队会考虑到不同用户的实际使用习惯、环境限制、潜在问题以及系统的稳定性。通过分析和测试 real use cases,开发团队能够发现潜在的问题和改进的机会,以提高系统的可用性和用户满意度。 综上所述,essential use cases 和 real use cases 在软件开发过程中起着不同的作用。前者关注系统的基本功能需求,后者则关注系统在实际应用中的表现和满足实际需求的能力。两者相互补充,帮助开发团队全面了解用户需求,从而构建出更好的系统。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值