template
class CEditT : public TBase
{
public:
// Constructors
CEditT(HWND hWnd = NULL) : TBase(hWnd)
{ }
CEditT< TBase >& operator =(HWND hWnd)
{
m_hWnd = hWnd;
return *this;
}
HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL,
DWORD dwStyle = 0, DWORD dwExStyle = 0,
ATL::_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL)
{
return TBase::Create(GetWndClassName(), hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, MenuOrID.m_hMenu, lpCreateParam);
}
// Attributes
static LPCTSTR GetWndClassName()
{
return _T("EDIT");
}
BOOL CanUndo() const
{
ATLASSERT(::IsWindow(m_hWnd));
return (BOOL)::SendMessage(m_hWnd, EM_CANUNDO, 0, 0L);
}
int GetLineCount() const
{
ATLASSERT(::IsWindow(m_hWnd));
return (int)::SendMessage(m_hWnd, EM_GETLINECOUNT, 0, 0L);
}
回想一下,一个简单的Win32windows应用程序是如何建立一个窗口的,你大致就可以想象出来。EDIT控件是一个已经注册过的特殊窗口,因为没有任何代码掩盖,显然它就在windows操作系统内部。其实不必太关心它具体在哪个dll里面,因为它已经调入内存了。
这个Edit控件的二进制代码显然是在内存中的。WTL给出了调用那些代码的方法,就是通过发送消息。毕竟你是看不到Edit源代码,所以这种调用方式很容易想象。
对于一个对话框程序,我想说的是,我们在对话框上放置几个控件,就算不编写任何代码,这些控件还是会显示出来,这说明了那些控件在对话框内部已经被建立了,不管有没有一个实体变量指向它。理解这一点非常重要。
对话框上面我们放置了5个控件,两个label就不说了。编辑框控件: IDC_EDIT1,弹出列表框控件: IDC_COMBO1, 还有一个 Picture Control 控件: IDC_MYCTRL
对于编辑框控件,我们不定义任何实体变量指向它。对于弹出列表框控件,我们将定义一个CComboBox 变量指向它,而图形控件,我们将拓展这个控件的功能,建立一个简单的自定义控件,并定义一个变量指向它。对于图形控件,它的类型设置成了Owner Draw,也就是自绘。
// MainDlg.h : interface of the CMainDlg class
//
/////////////////////////////////////////////////////////////////////////////
#pragma once
#include "testctrl.h"
class CMainDlg : public CDialogImpl, public CUpdateUI,
public CMessageFilter, public CIdleHandler
{
private:
CComboBox m_combo;
CPicLooker m_myCtrl;
public:
enum { IDD = IDD_MAINDLG };
testctrl.h 是我们定义一个自定义控件,它实际上市对 CStatic控件进行了扩展。以后我们也会讲从零如何建立一个windows控件。如果够用,从一个现有的控件扩展总是简单的多。编程,达到目的是最重要的,是否最佳方法,经常倒不那么重要。记得前些年,看到经常有人嘲笑印度软件人员编程序使用数组,那没多少道理。其实数组用好,用的不出问题,并不是很容易。
#pragma once
#include "stdafx.h"
class CPicLooker : public CWindowImpl,
public COwnerDraw
{
public:
CPicLooker(void)
{
}
~CPicLooker()
{
}
BEGIN_MSG_MAP(CPicLooker)
MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBkgnd)
CHAIN_MSG_MAP_ALT( COwnerDraw,1)
DEFAULT_REFLECTION_HANDLER()
END_MSG_MAP()
public:
LRESULT OnEraseBkgnd(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
return 1;
}
void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct )
{
CDCHandle dc = lpDrawItemStruct->hDC;
RECT& rcItem = lpDrawItemStruct->rcItem;
dc.FillRect( &rcItem, 21);
dc.MoveTo( rcItem.left, rcItem.top);
dc.LineTo(rcItem.right, rcItem.bottom);
}
};
this->GetDlgItem(IDC_EDIT1).SetWindowText(_T("编辑缺省值"));
m_combo = this->GetDlgItem(IDC_COMBO1);
m_combo.AddString(_T("选择A"));
m_combo.AddString(_T("选择B"));
m_combo.AddString(_T("选择C"));
m_combo.AddString(_T("选择D"));
m_combo.AddString(_T("选择E"));
m_combo.AddString(_T("选择F"));
m_combo.AddString(_T("选择G"));
// 设置对话框的缺省值
m_combo.SetCurSel(2);
m_myCtrl.SubclassWindow(this->GetDlgItem(IDC_MYCTRL));
m_combo = this->GetDlgItem(IDC_COMBO1);
这样的方式,而是采用了子类化方法。
m_myCtrl.SubclassWindow(this->GetDlgItem(IDC_MYCTRL));
具体的内含就是要windows执行子类的消息循环, 否则消息循环仍然在原来的CStatic里面,就无法执行我们自定义类的代码了。当你添加完这些之后,运行程序,编辑框和弹出列表框会运行的很好,但是我们的自定义控件没有工作。我们的自定义控件实际使用了windows的消息反射机制。这里暂时不解释那么多了,只需要添加一行代码。
BEGIN_MSG_MAP(CMainDlg)
MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
COMMAND_ID_HANDLER(ID_APP_ABOUT, OnAppAbout)
COMMAND_ID_HANDLER(IDOK, OnOK)
COMMAND_ID_HANDLER(IDCANCEL, OnCancel)
// 添加消息反射
REFLECT_NOTIFICATIONS()
END_MSG_MAP()