第三章 应用程序框架
句柄
HWND 窗口句柄
HBITMAP 位图句柄
HICON 图标句柄
HMENU 菜单句柄
HFILE 文件句柄
HINSTANCE 当前实例句柄
HGLOBAL 全局内存对象句柄
HDC 设备环境句柄
HCURSOR 光标句柄
HFONT 字体句柄
HPEN 画笔句柄
HBRUSH 画刷句柄
HLOCAL 局部内存对象句柄
旁注:
char是char[]的首地址
同样 main是main()的首地址
消息
消息号: 事先定义好的消息标识
wParam: 字参数 消息附加信息
lParam:长字节参数 消息附加信息
消息循环:
常见的:
while(GetMessage(&Msg,NULL,0,))
{
TranslatMessage(&Msg);//将消息的虚拟键转换为字符信息 例如把
wm_keyup/wm_keydown 转换为wm_char消息,它不会消灭以前的旧消息而是产生一
个新消息
DispatchMessage(&Msg);//将消息传送到指定窗口,即把消息传递给窗口类中的
lpfnWndProc所指向的函数名
}
窗口函数WinProc()
定义了程序对于接收到的不同消息的响应,包含了对各种可能接受到的消息的处
理过程。
每一个case处理一个消息
当你不感兴趣的消息也不能不管,在一般的消息回调函数中default 都会return
defwindowproc(...)交由系统处理,否则窗口会不显示
回调函数的callback是用来区分_stdcall(delphi语系以及其他api的语言约定)与
_cdle(c语言的语言约定)的 不同函数 的。
初始化
1 定义窗口类
2 注册窗口 RegisterClass();
3 创建窗口 CreateWindow();
4 窗口显示 ShowWindow();
MFC类库
包括CWinApp CDoucument CView CFrameWnd CDocTemplate 五大类
命令相关类 CCmdTarget 是消息映射属性的基类,消息映射规定了当一个对象接
收到消息命令时,调用哪个函数进行处理。
线程基类 CWinThread 可直接使用
应用程序类 CWinApp
窗口类 CWnd
框架窗口类 CFrameWnd
文档视窗类 CDocTemplate
winmain中lCmdline参数为 系统参数之意
窗口重画cs_vredraw..为单独位为1的意思如0x00001
如要去掉,可以采用style&~cs_vredraw
窗口类wndClass
旁注:HDC
HDC hdc1 创建一个hdc的变量(hdc是指跟显示设备相关的中间层变量,只要复制
给hdc就可以被显示)
hdc1.getDC(指定要显示窗口的句柄如hwnd);
可以用TextOut(hdc1,...)来显示文字
用完要释放 ReleaseDC(hwnd,hdc1)
Class :
构造函数
它的名字就是类的名字,一般会在public中,它会在类创建的时候被调用,本来
用于自动给参数赋初值的函数,是类创建所必须的如果不写,系统会默认一个
例子:
class Point
{
public:
int x;
int y;
/* void init() ///此处用于 手工赋初值的
方法,如Point s1; s1.init();//即为初始化
{
x=0;
y=0;
}*/
Point()//此就是构造函数
{
x=0;
y=0;
}
---------------
析构函数:
它的名字就是~类名字,它会在类使用完的时候调用,本来用于释放类占据的空
间的,一般也会出现在public中
例子:
class Point
{
public:
int x;
int y;
~Point()/// 此即为析构函数
{
}
---------
函数的重载
有多个名字相同 参数个数或者类型不同的函数
例如:
Point()
{
x=0;
y=0;
}
Point(int a,int b)
{
x=a;
y=b;
}
到时候那个符合参数就用那个。。自适应的。
----------------------
this指针用于指向对象,
例如:
Class Point
{
...
void output(int x,int y)
{
this->x=x;
this->y=y;
}
}
...
Point ist;
此时类中的this就是指ist
----------------
函数的继承和派生
例如class a;
再定义b时,可采用
class b :public a
{
}
即b继承了a类,b是a的派生
====摘自csdn
继承和派生两个术语的区别在于它们的主宾关系不同。
继承是对于父类来说是被动的,可以描述为:父类XXX被子类XXX继承
而派生对于父类来说是主动的。描述为:父类XXX派生子类XXX。
关于派生类和子类的区别:应该是继承关系的层次结构的概念区别。
派生类可以是多余一级的继承关系,
子类是一级关系。
但同时也有这样的概念:子类属于派生类。
例如:基类A,
类B直接继承于A
类C直接继承于B;
那么依据上面的说法则有:
类A派生类B,类A被类B继承;类B派生类C, 类B被类C继承。
类B是类A的子类,类C是类B的子类;
类B和类C都是类A的派生类。
------------------------
多态性
就是父类的同名函数若设置为虚函数,则放弃父虚函数的类型,主要看子类。子
类有的调用子的,没有的调用父类。
----------------------
虚函数
见上述函数的多态性 ,
例子:
class animal
{
public:
....
..
virtual viod breathe()//此即为虚函数,
{
}
}
若为virtual viod breathe()=0;则为纯虚函数纯虚函数是父类用来“留白”的
,即,子类必须定义自己的这个函数
--------------
引用
就是一种别名
例子:
int a=6;
int &b=a;
则b就是a的引用,而且b变换,a也会跟着变化,
注意引用只能在定义时候定义一次,之后不会被重复定义
用法
如果b=5,则a也会变成5
一般用法 定义函数
如
change(int &a,int &b0)
{...}
使用的时候change(x,y)即使把参数x和y的 本身 进行交换。。。
======================
类的一般 用法
把文件分为cpp和h文件
h文件中只是声明类的函数,不做具体定义
如 animal.h就只用写
class animal
{
public:
animal(int heght , int weght);
viod eat();
viod sleep();
virtual viod breathe();
}
在animal.cpp中写
animal:: animal()//注意此处不必写函数返回值,因为构造函数没有返回值
{
//构造函数
}
void Animal::eat()
{
}
.....
继承animail类的fish类 定义的fish构建函数
Fish::Fish():Animal(300,400)
{
}
---------注意头文件不参与编译
--------------
::shownwindow() 表示调用全局或者平台的函数
AFX类是框架类的函数,系统把众多类联系起来的全局函数
AfxGetApp()可以获得一个CWinApp类的指针即this
CWinApp是应用程序类
先构建 CWinApp 先构建全局变量
然后构建winmain
afxwinmain是winmain的调用函数,用来完成winmain函数的功能
AfxRegisterClass注册窗口类函数,用来使用win32风格的几种窗口
Initapplication()是用来初始化管理用函数
InitInstance()调用子类的初始化函数
this 指向是CxxxApp xxxApp 定义中的xxxApp 是应用程序类
PreCreatWindows()函数中先注册窗口AfxDeferRegisterClass
()//AfxEndDeferRegisterClass判断有没有注册,如果没有就注册参数
可以通过其参数 CREATESTRUCT& cs改变有无最大化等窗口类型
主窗口的创建,是在ProcessShellComand(cmdinfo)实现的
::CreatWindowEx(...)创建窗口,其返回值是个句柄
m_pMainWnd是指向cMainFrame的指针
CWinThread::Run()完成了消息循环
注意wndcls.lpfnWndProc=DefWindowProc并非单纯把消息交给消息处理函数,所
谓的Defxxxx而是做了消息映射,所以此处该处理函数是没有相应的。--leon注解
单文档程序的子窗口是cxxxxview类
LPCTSTR 常量字符串
CxxxDOC 文档类
在C++中,结构体也是一种特殊的类
MFC和WIN32 创建窗口方法的区别和联系
WNDCLASS wndcls;
wndcls.cbClsExtra=0;
wndcls.cbWndExtra=0;
......
RegisterClass(&wndcls);
CWnd wnd;//wnd并不是窗口,窗口销毁时之销毁了句柄为空,但是成员
函数仍可以用,但是如果wnd销毁,则窗口销毁
wnd.CreateEx(...);//hwnd 内部传递并没有外漏
wnd.ShowWindow(SW_SHOWNORMAL);
wnd.UpdateWindow();
|WIN32----------------------------------------------------------------
--个MFC
v HWND hwnd;
hwnd=CreateWindowEx();
::ShowWindow(hwnd,SW_SHOWNORMAL);
::UpdateWindow(hwnd);
......
CButton::Create(LPSTSTR按键的文字,风格,大小和位置,父窗口,ID)
例子 : CButton btn;//加到class中的private中
调用
btn.Create("test",WS_Child|BS_PUSHBUTTON,CRect(0,0,1,1),this,
ID123);
btn.ShowWindow。。。
小锁 private ,钥匙protect ,什么都没有就是public
得到父窗口指针HWND GetParent();
----------------------------------------------------------------------
--------------------
----------------------------------------------------------------------
--------------------
文本编辑
创建插入符
CreateSolidCaret(20宽度,100高度);
ShowCaret();
//Ascent 升序高度,即h角到头的高度
//Descent 降序高度 ,即G从头到脚的高度
获得字体高度
CCilentDC dc(this);
TEXTMETRIC tm;//注意此处的tm为随意命名
dc.GetTextMetrics(&tm);
tm.tmhight 就是他的高度
这个用法很有用!
位图插入符
CBitmap bitmap;//如果要用,最好放到类的private中,以免析构的时候被干掉
bitmap.LoadBitmap(IDC_XXX);//CString也有类似的读取资源的方法就是
LoadString//可以在资源的String Table中添加
CreateCaret(&bitmap);
ShowCaret();
View 类, 中的OnDraw 窗口重绘时经历
CDC类定义的*pDC
CString str("Hello World!");
文本输出:
pDC->TextOut(50,50,str);//50,50就是文字的左上角的坐标
CSize 中的成员x y 分别是长度和高度
获得字符串的高度
CSize sz=pDC->GetTextExtent(str);
画矩形 pDC->Rectangle(x0,y0,x1,y1);
cstring.GetLength()取长度
//
此函数实在CxxxView类中右击选择 Add Windowsmessage Hander 从而增添的消息
截获WM_CHAR消息做的响应
void CTextView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
CClientDC dc(this);
CFont font;
font.CreatePointFont(300,"华文行楷",NULL);
CFont *pOldFont=dc.SelectObject(&font);
TEXTMETRIC tm;
dc.GetTextMetrics(&tm);
if(0x0d==nChar)//回车
{
m_strLine.Empty();
m_ptOrigin.y+=tm.tmHeight;
}
else if(0x08==nChar)// 退格
{
COLORREF clr=dc.SetTextColor(dc.GetBkColor());
dc.TextOut(m_ptOrigin.x,m_ptOrigin.y,m_strLine);
m_strLine=m_strLine.Left(m_strLine.GetLength()-1);
dc.SetTextColor(clr);
}
else
{
m_strLine+=nChar;
}
CSize sz=dc.GetTextExtent(m_strLine);
CPoint pt;
pt.x=m_ptOrigin.x+sz.cx;
pt.y=m_ptOrigin.y;
SetCaretPos(pt);
dc.TextOut(m_ptOrigin.x,m_ptOrigin.y,m_strLine);
dc.SelectObject(pOldFont);
CView::OnChar(nChar, nRepCnt, nFlags);
}
CTextView::OnTimer(UINT nIDEvent) //文字追随变色的Timer组件代码
{
// TODO: Add your message handler code here and/or call default
m_nWidth+=5;
CClientDC dc(this);
TEXTMETRIC tm;
dc.GetTextMetrics(&tm);
CRect rect;
rect.left=0;
rect.top=200;
rect.right=m_nWidth;
rect.bottom=rect.top+tm.tmHeight;
dc.SetTextColor(RGB(255,0,0));
CString str;
str.LoadString(IDS_WEIXIN);
dc.DrawText(str,rect,DT_LEFT);
rect.top=150;
rect.bottom=rect.top+tm.tmHeight;
dc.DrawText(str,rect,DT_RIGHT);
CSize sz=dc.GetTextExtent(str);
if(m_nWidth>sz.cx)
{
m_nWidth=0;
dc.SetTextColor(RGB(0,255,0));
dc.TextOut(0,200,str);
}
///
/
CView::OnTimer(nIDEvent);
}
///
=======================================================================
====
关于消息的三点:
1.类的头文件中注释宏之间(//{{AFX_VIRTUAL(xxx)。。。//}}AFX_VIRTUAL)
2.源文件的
BEGIN_MESSAGE_MAP(CXXXView, CView)
//{{AFX_MSG_MAP(CXXXView)
这个位置
//}}AFX_MSG_MAP
3.源文件的viod cXXxx::onXXX ()自定义的处理函数部分
MFC的消息映射(非WIN32的消息循环处理)
WIN32的编程中 消息处理是把所有的消息都做成虚函数,只要定了子类就可以进
行相应
这样会造成内存资源的浪费
后台有个句柄和c++类对象指针的映射表 ,有消息发生后,后台会把消息以及相
关的窗口对应起来,通过该窗口的句柄找到C++对象的指针,然后将指针传给基类
,基类通过消息循环调用了WindowProc,而WindowProc本身是虚函数,在具体子
类定义次函数时必然会调用了OnWinMsg(消息路由部分),OnWinMsg会查找 1和2
从而判断有没有该消息响应的生命,最后找到3 进行处理。--孙鑫先生原文
我理解MFC的消息映射过程是这样的:
消息产生了-》启用那个后台的映射表-》查找响应窗口的句柄-》WindowProc-》
OnWinMsg-》看看1,2部分有无定义,有的话就—》3 ;、、、、没有就算了(交
由基类处理) :)
view中无须用函数获得本窗口句柄,直接用m_hWnd就是本窗口句柄
画线函数及其用法:
win32用法
HDC hdc;
hdc=::GetDC(m_hWnd);//平台sdk的函数需要加::
MoveToEx(hdc,m_old.x,m_old.y,NULL);//先把鼠标移动到最初的点
LineTo(hdc,point.x,point.y);//画函数
::ReleaseDC(m_hWnd,hdc);//用完释放dc
MFC用法
CDC *pDC=GetDC();
pDC->MoveTo(m_old);
pDC->LineTo(point);
ReleaseDC(pDC);
注释同上
更快的MFC(自动会GetDC和ReleaseDC)
//CClientDC dc(GetParent());//若用此法,可以在工具栏上画,即目标是整个
框架,但不包括非客户区域即标题栏和菜单。
CClientDC dc(this);//注意此函数需要的是一个CWnd的指针,而不是hwnd,hwnd
只是cwnd的一个变量可以用cwnd->hwnd 来调用
dc.Moveto(m_old);
dc.LineTO(ponit);
第四种方法,CWindowDC(也自动会GetDC和ReleaseDC)
CWindowDC dc(this);//若用GetParent可以全窗画
dc.MoveTo(m_old);
dc.LineTo(ponit);
桌面本身就是窗口!
获取桌面窗口的句柄: GetDesktopWindow()可以获取一个CWnd;
注意,此处若用CWnd::的就是获取CWnd类的指针
若用sdk的即::类的就是获取这个窗口的句柄!
CPen画笔类
CPen pen(笔形,宽度,RGB颜色);
需要选入设备描述表中!!
应采用SelectObject可以选择pPen,pBrush,pFont,pBitmap(使用时需要加& 如
&pen,其返回值也是指针,需要用 *pen接)
其返回值是先前一个状态的设备,比如定义了新画笔,那么返回值就是旧画笔。
CBrush画刷类
颜色画刷
CBrush brush(RGB(255,0,0));
CClientDC dc(this);
dc.FillRect(CRect(m_old,point),&brush);//填充
//画红色矩形
位图画刷
CBitmap bitmap;
bitmap.LoadBitmap(IDB_XXXX);
CBrush brush(&bitmap);
CClientDC dc(this);
dc.FillRect(CRect(m_old,point),&brush);//填充
画矩形最快的方法:
dc.Rectangle(CRect(m_old,point));
透明画刷方法:
CBrush:*pB=CBrush::FromHandle((HBURSH)GetStockObject(NULL_BRUSH));
dc.SelectObject(pB);
dc.Rectangle....
---------------------
Class p
{
public:
void a()
{}
static void b()
』//静态使用成员函数可以在任何地方写
如, 静态环境不能调用非静态(没有static定义)的成员变量,
非静态的可以引用静态参数
静态的必须初始化!!即在定义前就要做好!
p point;
p:b();就可以
{}
}
==============================================
ss菜单:
新增菜单命令响应函数的四大文件篇
MainFrame
第三优先级
App
第四优先级
Doc
第二优先级
View
最高优先级
个级别都是如果有函数,就不再允许别的类相应
-----------
消息分类
标准消息:
它是CWnd派生的类,它派生的类中都可以接受到这个消息
除了WM_COMMAND外,所有已WM开头的消息
命令消息
菜单,快捷键,工具栏按钮的消息
都是以WM_COMMAND呈现,要区分 需要菜单项的ID号码
CCmdTarget(CWnd的父类)派生的类都可以接受这类消息
通告消息
有控件产生的消息,按键的单击,区分需要ID。。。
也是以WM_COMMAND呈现
CCmdTarget(CWnd的父类)派生的类都可以接受这类消息
CWnd类可以接受所有的消息,但是CCmdTarget只能接受后两者
命令消息的路由:
AfxWndProc->AfxCallWandProc->WindowProc->OnWndMsg ->.1 OnNotify(通告消
息) ->OnCmdMsg
.2 OnCommand(命令
消息)
标记菜单——带对勾的那种
实现方法:
在相应函数处加入OnCreate中
GetMenu()->GetSubMenu(0)->CheckMenuItem(ID_按键,
MF_BYCOMMAND|MF_CHECKED);//MF_UNCHECKED
0 代表是第几个子菜单
GetMenu是CWnd的一个成员函数,它返回一个指向整个菜单栏的指针
而GetSubMenu只是返回子菜单的指针
CheckMenuItem是在菜单中放置或者取消一个标记
注意如果用索引法,一定能够要计算分割栏!
设置默认菜单(字体变粗)
GetMenu()->GetSubMenu(0)->SetDefaultItem(ID);
一个子菜单只能有一个缺省菜单,即最后一次命名的。
设置图片菜单
SetMenuItemBitmaps
可以有连个位图,选中一个位图,没选中一个位图。
CBitmap cc;
cc.loadbitmap(图片的ID);
SetMenuItemBitmaps(ID_FILE_OPEN,MF_BYCOMMAND,&m_cbp,&m_cbp);
按键变灰色的函数:GetMenu()->GetSubMenu(0)->EnableMenuItem(ID,
MF_BYCOMMAND/MF_DISABLE|MF_GRAYED);需要在mainframe的构造函数中加
m_bAutoMenuEnable=0;
GetSystemMetrics();//获得系统参数,详情请参考msdn可以查到很多有用的数据
移走菜单 : SetMenu(NULL);
回复菜单:
CMenu menu;
menu.LoadMenu(菜单的ID);
SetMenu(&menu);
1.10
CMenu类