作者:pengkuny
下决心把MFC学好,
从暑假到现在,
连皮毛都没学到一点,
真他*丢脸.

发现写日记的方法效果不错,
该记住的终于记住了

看了仍然不懂的,用"@#$%"符号表示.

错误理解必然一大堆,只能期望日后补救拉,只是希望不要错得太离谱.

2006.11.10

一.消息响应事件的模式,那么消息怎么起作用的?
   消息以调用一个窗口的窗口过程的形式来表明自己的存在.伴随4个参数:
消息所指窗口句柄(包含很多窗口的信息),
消息ID(整型值,消息类型,以WM_开头),
两个名为wParam和lParam的32位参数.

.@#$%
API函数RegisterClass注册的类为WNDCLASS
而"窗口类"指从MFC的CWnd派生的C++类

三.匈牙利命名法:
b,c/ch,By,clr,(x,y),(cx,cy),w,dw,n,I,l,s,sz/str,p,fn,h,wnd,MSG,WM_,g,AFXAPI,WINAPI,SW_,WS_,MM_
函数名不使用下划线
所有的类型和常量都是大写字母,但名字中可以允许有下划线
所有C++的类必须以大写C为前缀,类名字的每一个子名的第一个字母都必须大写

四.熟悉一些常用的AFX函数

五.接触到的一些类:
CCmdTarget--CWinTread--CWinApp
Cwnd--CFrameWnd,凡是以Wnd结尾的类都是Cwnd类,框架窗口类


六.protected:对外相当于pravite,对内相当于public

七.头文件AfxWin.h,里面包含所有类的声明,可以仔细看一看

八.查看CWinApp的类定义
待查:分析CWinApp类;
     DECLARE_DYNAMIC;

 

2006.11.11

一.应用程序窗口必须由InitInstance创建,故即使最小的MFC程序都必须覆盖这个虚函数.
   同理,CMainWnd继承自CFrameWnd,它必须有自己的构造函数,调用Create创建一个窗口.
   CWinApp的一个非常重要的成员函数CWnd* m_pMainWnd;它是public的,指向CWnd类

二@#$%
protected:friend class CWinApp;WinApp是CFrameWnd的友元

三.ShowWindow(m_nCmdShow);UpdateWindow();
   m_nCmdWindow表示窗口CWnd的显示状态,CWinApp的public成员函数,其值以SW_前缀,默认为SW_SHOWNORMAL

四.从Winmain.cpp中可以看到:
   AfxWinMain在幕后操纵这一切的运行

五.现在可以把框架窗口看作顶层窗口看待,它是应用程序与外部世界的主要接口.

六.消息映射:将消息和成员函数相互关联的表.
BEGIN_MESSAGE_MAP (CMainWindow, CFrameWnd)
    ON_WM_PAINT ()
END_MESSAGE_MAP ()

CMainWindow为消息所属的类; CFrameWnd为消息所属的类的基类,因为消息可以继承.
使用ON_MESSAGE(WM_SETTEXT,OnSetText);创建自己的消息映射.

 

2006.11.13

主题:在窗口中绘图

一.GDI(Graphics Device Interface)图形设备接口负责图形输出.
   具体由设备描述表(DC)来做.

二.4类设备描述表类:
CPaintDC
CClientDC
CWindowDC
CMetaFileDC

二.创建一个设备描述表后,如CPaintDC dc(this);
就可以调用各种画图函数了,dc.Fun();
   6个GDI对象:跟设备描述表dc是什么关系?
基类:CGDIObject类
画笔Pen
画刷Brush
字体Font
位图Bitmap
调色板Palette
区域Region
  
  它们实际上刻画了设备描述表dc的属性,并不是dc的函数,
通过dc.SelectObject(GDI对象指针)选入设备描述表.

三.回忆WM_PAINT消息是怎么产生的:
移动了窗口
原来遮掩的部分显示出来
窗口大小改变
 
CPaintDC类只能干这个事,只能局限于响应WM_PAINT消息,所以要选用CClientDC类

四.取得全屏访问权,入屏幕截取程序,很少见
传递NULL指针
CClientDC dc(NULL);

一般情况下,取this指针给构造函数即可:
CPaintDC dc(this);
CPaintDC *pDC = new CPaintDC(this);
this指向调用对象本身,不可改变.
 

五.掌握设备描述表属性Attribute的常见术语
文本颜色 CDC::SetTextColor
背景颜色 CDC::SetBKColor
背景模式 CDC::SetBKMode
映射模式 CDC::SetMapMode
绘图模式 CDC::SetROP2
分别由相关函数调用,非常方便.
既然有Set函数,毫无疑问,就有Get函数

六.比如OnPaint函数,每次调用完以后,函数内定义的设备描述表自然被销毁
想要保存它的状态,请使用
CDC::SaveDC,对应就有:
CDC::RestoreDC来恢复

七.映射模式:
一个逻辑单位对应的距离,画图的时候只要告诉GDI多少单位即可,
实际上这些模式都扩大了本来的表示范围,使得大尺寸的东东都可以压缩比例显示
8种映射模式,非常简单:
(1)默认MM_TEXT
(2)公制模式:y轴反转,完全符合数学坐标,所以y值一定要用负的.
(3)可编程模式:不反转,但允许反转
   MM_ISOTROPIC  :x,y同等缩放
   MM_ANISOTROPIC:x,y独立缩放

八.可编程模式
SetViewportExt
SetWindowExt


九.补充:堆和栈的区别
首先必须弄懂C/C++中内存区的分配:

堆:顺序随意

栈:先进后出

一个由c/C++编译的程序占用的内存分为以下几个部分

1、栈区(stack)— 编译期间就分配好的内存空间,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。 一切管理由系统负责.

2、堆区(heap) — 程序运行期间动态分配的内存空间,你可以根据程序的运行情况确定要分配的堆内存的大小.由alloca,new申请,并由free/delete释放.注意它与数据结构中的堆是两回事。

3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后有系统释放
static指函数调用后仍然不消失的局部变量.

4、文字常量区 —常量字符串就是放在这里的。 程序结束后由系统释放

5、程序代码区—存放函数体的二进制代码。

栈:在Windows下,栈是由高地址向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在 WINDOWS下,栈的大小是2M,如果申请的空间超过栈的剩余空间时,将提示overflow。
速度快.
堆:堆是由底向高地址扩展的数据结构,是不连续的内存区域。
容易产生碎片.


举例:
 CPoint ptX; 和 CPoint ptX = new CPoint();两者的区别是什么?
前者栈分配,后者堆分配,用完后delete.

与C++不同,Java自动管理栈和堆,程序员不能直接地设置栈或堆。


十.终于他妈的明白GetClientRect的作用了:
CRect rect;
GetClientRect(&rect);
将当前窗口的尺寸(比如非最大非最小的状态)来初始化这个矩形,
而不是用矩形来初始化窗口大小(难怪rect没初始化)                                                                                                           

十一.
CRect rect;
GetClientRect(&rect);
dc.SetMapMode(MM_ANISOTROPIC);                    //可编程模式
dc.SetWindowExt(500,600);                         //逻辑尺寸就是500*600单位
dc.SetViewportExt(rect.Width(),rect.Height());    //设备单位/像素:rect.Width(),rect.Height()
水平方向--每单位多少像素:rect.Width()/500
垂直方向--每单位多少像素:rect.Height()/600                                                          缩放比例就这么来.

SetWindowExt(x1, y1);                     
SetViewportExt(x2, y2);

SetWindowExt(100*x1, 100*y1);                      
SetViewportExt(596*x2, 596*y2);
效果完全一样,无非是一个比例而已.

在同比例缩放模式MM_ISOTROPIC下,
缩放比例取min{x2/x1, y2/y1}!

两个范围设置函数仅仅干了一件事,设置比例,其后画图使用逻辑单位,跟它们就没有直接关系了.                                                                                                                                                                                                                                                                        

2006.11.15

一.这句话不明白:
"使用SetWindowExt和SetViewportExt时要注意:在MM_ISOTROPIC映射模式下,
应该首先调用SetWindowExt.否则,部分客户区可能会因落在窗口的逻辑范围之外而不能使用。
而在MM_ANISOTROPIC映射模式下,窗口范围和视口范围中先设置哪一个都无关紧要。"

实际上,我实验发现:只要掌握放大因子k=min{x2/x1, y2/y1},和窗口真实逻辑大小(X,Y),那么画出来的图形(x,y)
大小就是(kx,ky),只要kx<=X, ky<=Y,那么就不会有客户区落在窗口的逻辑范围之外的事情发生.

二.坐标转换
CDC::LPtoDP
CDC::DPtoLP

三.移动原点,终于搞懂了
SetViewportOrg(x,y)将视口原点移至(x,y)等价于通知Windows把逻辑点(0,0)映射成设备点(x,y),
什么意思?就是把逻辑原点移到设备点(x,y)的位置,在这个新位置安家作为新的逻辑原点,
对设备坐标没有丝毫改变,所谓映射,只不过是一种简单的移动关系,并不改变设备坐标
同理,SetWindowOrg(x, y)将逻辑原点(x,y)移到原设备原点(0, 0)的位置

述说的时候,(x, y)和(0, 0);逻辑点和设备点
两个"反义词"不要同时反说,否则等于没说.
统一说:把逻辑点(*,*)移到设备点(*,*)的位置

四.两个设备坐标值:
用户坐标值
屏幕坐标值
互相转化;
CWnd::ClientToScreen
CWnd::ScreenToClient

四.获取设备信息:
CDC::GetDeviceCaps

五.GDI画笔CPen
样式PS_
宽度
颜色
Windows自动选用当前画笔给图形加边框,若选用PS_NULL"NULL笔",边框就没有了.

扩展笔:@#$%

六.GDI画刷CBrush
画刷用来干什么?作为dc的属性,它一旦选入,就自动填充一切画出的封闭图形
类型:
单色
带阴影线:6种,默认白色背景
带图案:填充指定的位图

@#$%:
移动画刷原点:选入设备之前
brush.UnrealizeObject();
dc.SetBrushOrg(x0, y0);//注意,画刷原点以设备坐标值给出
dc.SelectObject(&brush);


七.CDC文本函数:
@#$%
DrawText
TextOut等

八.GDI字体CFont
一个很好用的API函数::ZeroMemory(指针,内存大小)将一块内存清零.
LOGFONT为字体结构,如:
LOGFONT lf;
::ZeroMemory(&lf, sizeof(lf));
初始化lf;
lf.lfEscapement和lf.Orientation指定文本旋转角度的10倍值


CRect::OffsetRect(x,y)设置一个矩形坐标偏移量.

光栅字体:位图保存,不适合缩放,MS Sans Serif
TrueType字体:任意缩放
Times New Roman
Arial
Courier
Symbol

八.备用对象:一种非常好的GDI对象,系统预定义,无须显式创建,也无须删除,非常安全.
CDC::SelectStockObject选入DC
CDC::CreateStockObject赋给已有对象

九.怎么删除GDI对象?
堆上创建的/new,需要CGDIObject::DeleteObject显式删除

十.怎么取消GDI对象?
变相的方法:通过选入另外一个GDI对象将当前GDI对象从设备描述表中"提取"出来.
先保存调用SelectObject时返回的指针,
然后将这个指针重选默认对象,或者选入备用对象
来取代当前对象.

实际上,@#$%

十一.CString::Format:
与C语言的printf功能一样,支持printf所有的格式,
如string.(_T("d%"), i/100);
然后dc.TextOut即可.

十二.滚动条:窗口样式参数,将来学到更高级的滚动条控件,就应该可以抛弃这种低级的方式
滚动条向它所属的窗口发送消息,一切响应有窗口来做,滚动条很少自己响应消息.

Create的第三个参数
WS_VSCROLL , WS_

设置:范围,位置,页面大小
方法一:
CWnd::SetScrollRange和CWnd::SetScrollPos

参数TRUE意味着重不重画,小技巧

方法二:CWnd::SetScrollInfo,很好用,如:
    SCROLLINFO si;
    si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;
    si.nMin = 0;
    si.nMax = nHScrollMax;
    si.nPos = m_nHScrollPos;
    si.nPage = m_nHPageSize;

    SetScrollInfo (SB_HORZ, &si, TRUE);


十三.滚动条滑块大小与窗口尺寸同步变化:
WM_SIZE消息:
窗口建立的时候,就有WM_SIZE的消息传来,以后只要窗口尺寸改变,就有WM_SIZE消息传来
用OnSize处理.
afx_msg void Onsize(UINT nType, int cx, int cy);轻松解决//类型+新宽度+新高度


十四.滚动条消息类型自动传递给nCode参数,位置信息pos都是自动传递给消息处理函数,不用担心
WM_HSCROLL,WM_VSCROLL不是具体的消息类型,SB_才是,
OnHScroll,OnVScroll.

但是在任何事件中,更新滚动条的位置都是程序员的事情,滚动条自己不会干.

小技巧:比如有时忽略"拖动滚动条"消息SB_THUMBTRACK,
       而只管"释放滚动条"SB_THUMBPOSITION


十五.ScrollWindow(x,y)://向右滚动x个像素,向下滚动y个像素
数据快速拷贝滚动滚动,空出来的地方激活OnPaint去重画



待续……

posted on 2006-11-15 13:56 哈哈 阅读(2266) 评论(2)  编辑 收藏 引用

评论:
# re: 我的MFC学习笔记(Cont) 2006-12-19 21:37 | MOKEY
支持博主  回复  更多评论
  
# re: 我的MFC学习笔记(Cont) 2007-04-03 14:20 | phoenix
我这学期才开始学MFC~~用GDI画图~以后有什么不懂的就问你哦  回复  更多评论
  

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理