我写东西很少婆婆妈妈,可能是比较懒散,如果2个字能说完,绝对不会说三个字以上,有朋友开玩笑说“言语珍贵”,因为简单,很多人不理解,所以也有“跳跃性思维”的说法。
对一个问题,我喜欢”自悟“,一定要知道所以然,即使有一些细节不知,但是整个过程是不应该出现错误,对于问题,我喜欢“心神领会”,只有领会了,才可以游刃有余,
在使用的时候方可如鱼得水,其次领会一个问题,可以让大脑轻松,而不被宿主在大脑中的问题所困扰,这也是所谓的快乐学习,但是弄懂一个问题,是要以时间为代价的,生命如此短暂,我又能弄懂几个问题?
蹉跎的岁月让人一天天变老,让人一天天憔悴,让人的脑细胞在生与死的斗争中,终于死的越来越多,这就是所谓的规律。哀叹时光的流逝,自己却无能无力,只能反省,反省……。
时间在让我们老去的同时,让我们越来越聪明,越来越有智慧,人就因为有太多的不明白,所以才活着,如果都明白了,估计也就是在死亡的那一刻吧!
过于追去完美,过于追求,为什么,让自己在和时间的赛跑中成了一个失败者,无心留恋逝去的光阴,无心幻想剩下的岁月,只有无力的哀叹!
08年的时候,机缘巧合看了《深入浅出MFC》,也许是因为有一门课程就是VC++开发的,但是当时看那本书,很是晦涩难懂,而因为一些原因,VC++开发的课程也夭折了,一晃,
已经5年过去了,凭借自己的坚持,终于将《深入浅出MFC》看完了,也了解了很多以前觉得晦涩难懂的东西,可能真是自己当时的知识不够而已,也许是自己天资愚笨。用时间去战胜问题是我最大的总结。
言归正传,MFC窗口对象和窗口句柄是两个完全不同的东西,前者是一个C++对象,而后者是一个window对象或者window控件或者windows的一种资源,后者其实就类似一个身份证的东西,唯一标识了它的身份,而这个身份又是前者的一个成员变量,这可能是我们最初的认识吧。
但是当遇到子类化这个概念的时候,懵了,一头雾水,丈二的和尚摸不着头脑?子类化,简单地说就是替换了window窗口句柄的处理过程,书上都这么说,我也就这么说,其实这个还是要从前面的一句话说起,这个所谓的C++对象是如何和window句柄关联起来的?
create函数,这个不应该陌生,在create函数中调用了createx函数等一系列相关的函数,而这些函数做了大概以下事情,注册一个窗口,创建一个窗口返回一个句柄,这个时候这个句柄就放在了m_hWnd成员变量中,在《深入浅出MFC》中,在主窗体创建的时候还有一个偷天换日的动作,那就是将默认的处理函数替换成了我afxwndproc,而这个就对应了所在类的消息映射函数,其实这个就是一个子类化过程,暂且就这么理解吧。所以create函数已经帮我们子类化了,如果细细跟踪下去,会遇到setwindowlong等函数,这些可以自己动手去跟踪下,我点到为止。create函数因为将句柄和C++对象关联起来了,我们当然就可以用C++对象对窗口进行操作了,其实里面调用的还是win32 api,只是这些都被封装在了这个C++对象中,现在应该有豁然开朗和柳暗花明又一村的感觉了吧,当然句柄和C++也就是这个MFC对象关联不仅仅是create函数这一种方式,subclasswindow和DDX也是可以的,跟踪下去这些包含create都是调用了attach和setwindowlong这个函数,源码面前无秘密,大家可以去源码中看看。
这里我只是写了一个字符串而已:
void CMFCTestDlg::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); //DDX_Control(pDX, IDC_EDIT1, m_edit); m_edit.SubclassWindow(GetDlgItem(IDC_Test)->m_hWnd); m_edit.SetWindowTextW(L"liuyu"); } CWnd* CWnd::GetDlgItem(int nID) const { ASSERT(::IsWindow(m_hWnd)); if (m_pCtrlCont == NULL) return CWnd::FromHandle(::GetDlgItem(m_hWnd, nID)); else return m_pCtrlCont->GetDlgItem(nID); }
如果向我上面的这种写法,是实现不了子类化的,为什么?因为我这个操作只是将控件和类关联起来了?这可以从头文件中看出来:
CEdit m_edit;
通俗来讲子类化就是用自己的窗口处理函数来处理特定消息,并将自己其他消息还给标准(默认)窗口处理函数,而CEdit是微软定义的基类,里面定义了对EDIT控件默认的消息处理函数.
看到这个CEdit,我们似乎应该知道原因,这个是一个基类,或者说是从CWnd派生的一个窗口对象。在子类化介绍的时候,往往会用一个对输入的字符做判断这个例子,如果我们没有在CEdit这个类做特殊处理,我们就无法实现对输入字符的判断?那我们可以不可以在CEdit中添加,其实理论上是可行的,但是不这么做,你想下,这样将基类都破坏了,微软的人看了,还不会被气死,所以我们可以这么做,从CEdit派生一个类,回想主窗体创建的那个过程?这两个就类似,当然可以在这个派生类中添加消息处理函数了。
一般在使用子类化的时候,从基类中派生一个子类,这样我们就会自己去定制想要的功能!一旦有了子类,我们就可以在子类中添加要处理的函数,并且采用自己熟悉的方式进行句柄关联……,这算是我对子类化的一点个人看法,如果有什么想法,可以跟我探讨!下面附上我的一些操作代码:
头文件
CEdit m_edit;
CmyEdit m_edit1;
所谓的子类化
void CMFCTestDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
//DDX_Control(pDX, IDC_EDIT1, m_edit);
m_edit1.SubclassWindow(GetDlgItem(IDC_Test)->m_hWnd);
//m_edit.SetWindowTextW(L"ss");
}
派生类中对OnChar的处理,这里我将第一个输入的字母A改成了B,不要注释掉CEdit::OnChar(nChar, nRepCnt, nFlags),但是可以注释下看看效果。
void CmyEdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) { // TODO: 在此添加消息处理程序代码和/或调用默认值
CString ch;
GetWindowText(ch); // 获得已输入的字符序列
if(strlen(ch)==0&&nChar == 'A') { SetWindowTextW(this->m_hWnd,L"B");
return ; }
CEdit::OnChar(nChar, nRepCnt, nFlags);
}
前面我们说到过create这函数,这个函数其实就是创建一个window的句柄对象或者说window对象(区别C++对象)
那么如果我们有了一个子类,不是通过拖放控件的方式,我们可以自己用create函数去做这个子类化,代码如下:
BOOL CMFCTestDlg::OnInitDialog() { CDialogEx::OnInitDialog(); // 将“关于...”菜单项添加到系统菜单中。 // IDM_ABOUTBOX 必须在系统命令范围内。 ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); ASSERT(IDM_ABOUTBOX < 0xF000); CMenu* pSysMenu = GetSystemMenu(FALSE); if (pSysMenu != NULL) { BOOL bNameValid; CString strAboutMenu; bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX); ASSERT(bNameValid); if (!strAboutMenu.IsEmpty()) { pSysMenu->AppendMenu(MF_SEPARATOR); pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu); } } // 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动 // 执行此操作 SetIcon(m_hIcon, TRUE); // 设置大图标 SetIcon(m_hIcon, FALSE); // 设置小图标 // TODO: 在此添加额外的初始化代码 CRect r(20,10,300,500); //GetClientRect(&r); m_edit1.Create(WS_VISIBLE|WS_CHILD,r,this,10003); return TRUE; // 除非将焦点设置到控件,否则返回 TRUE }