从零开始的WTL入门教程(7) 创建使用自定义资源控件,使用DDX,自定义DDX
先介绍一个WTL增强数据交换工具 DDX
DDX是一套类似于MSG_MAP的宏命令,其实质就是简化和统一窗口与数据之间相互传值的调用代码。
使用起来比较简单,举个例子。
首先在需要使用DDX的类中继承CWinDataExchange父类
然后使用DDX命令 将要绑定的控件ID和对应类型的变量进行绑定
当然,要创建ID_TF_FIRST这个资源ID并赋值给之前创建的CEdit;
之后在需要交换数据的地方使用
DoDataExchange(FALSE) 方法就可以将数据从变量传递到控件,或 DoDataExchange(TRUE) 从控件传递到变量了
在自定义控件中可以定义自己的DDX宏,这样可以方便的统一输入和输出,便于代码阅读。
使用资源文件设置自定义视图
使用纯代码创建自定义视图很简单,继承就行了。
如果希望不需要通过代码创建只要拖控件的话,则要使用Custom Control控件,并在使用前注册,并使用自定义的窗口类关联。
首先拖一个 Custom Control 类型的资源到对应的视图中 ,然后指定一个资源ID和自定义一个Class名称。
然后回到Main函数中注册这个Class
创建一个自定义窗口类并关联资源
这样,该资源代表的控件就是你自定义的类对应的控件了,如果需要移动到其他窗口,可以直接在资源文件中操作,只要在使用时将对象与资源做关联就行了。
给自定义的资源视图添加控件
使用资源自定义视图则不需要调用Create方法,也就无法在这里创建子控件了,但是类与资源绑定时会调用SubclassWindow。就可以在这里重写该方法添加创建子控件的操作。
class FirstCustomItem :public CWindowImpl<FirstCustomItem,CWindow>
...
...
CButton btnL;
CButton btnR;
BOOL SubclassWindow(_In_ HWND hWnd) {
//先调用父类的SubclassWindow
BOOL result = CWindowImpl::SubclassWindow(hWnd);
if (result)
{
CRect lRect;
GetClientRect(&lRect);
lRect.right = lRect.right / 2;
CRect rRect;
GetClientRect(&rRect);
rRect.left = rRect.right / 2;
btnL.Create(m_hWnd, lRect, "0", WS_VISIBLE | WS_CHILD, 0UL, 0U, NULL);
btnR.Create(m_hWnd, rRect, "0", WS_VISIBLE | WS_CHILD, 0UL, 1U, NULL);
}
return result;
}
};
...
...
添加自己的DDX
DDX宏本质上也只是方法调用,实际与“给自己的控件添加输入输出接口并手动调用”是一样的。其意义在于可以对一类输入输出统一处理统一声明。因为C++代码往往行数是比较多的。DDX实质上只是让程序更易读。
添加一个自己的类继承CWinDataExchange
template <class T>
class FirstDialogDDX :
public CWinDataExchange<T>
{
#define DDX_FD_TEXT(nID, var) \
if(nCtlID == (UINT)-1 || nCtlID == nID) \
{ \
if(!ddxFdText(nID, var, sizeof(var), bSaveAndValidate)) \
return FALSE; \
}
void ddxFdText(UINT nID, CString& nValue, BOOL bSave)
{
//获取句柄
T* pT = static_cast<T*>(this);
HWND hWndCtrl = pT->GetDlgItem(nID);
ATLASSERT(hWndCtrl != NULL);
//发送操作消息
if (bSave)
{
//写入
}
else
{
//取出
}
}
};
如果你对前面分析其他组件的过程有所记忆这里就不再去DDX中分析了,可以自己去看一看。
原理非常简单。
模仿框架自带的DDX添加一个新的宏定义。和对应的调用方法。
bSave就是调用DoDataExchange时填入的参数,根据它判断操作方向。
然后回到自定义的类中添加自定义的消息和消息处理
接下来回到自定义的类中添加消息接收和处理方法
#define WM_WRITE WM_USER + 1
#define WM_HANDLE_POINTER WM_USER + 3
#define WM_READ WM_USER + 2
class FirstCustomItem :public CWindowImpl<FirstCustomItem,CWindow>
{
public:
BEGIN_MSG_MAP(OnInitDialog)
MSG_WM_PAINT(OnPaint)
MSG_WM_SIZE(OnSize);
MESSAGE_HANDLER(WM_READ, notifactionRead)
MESSAGE_HANDLER(WM_WRITE, notifactionWrite)
END_MSG_MAP()
添加消息接收和对应的处理方法
LRESULT notifactionRead(...) {
return readData();
}
LRESULT notifactionWrite( UINT , LPARAM lparm, WPARAM rparm, BOOL& /*bHandled*/) {
writeData(lparm);
return 0;
}
BOOL readData() {
CString strL;
btnL.GetWindowTextA(strL);
CString strR;
btnR.GetWindowTextA(strR);
if (strL == "1" && strR == "1")
{
return TRUE;
}
return FALSE;
}
void writeData(BOOL state) {
if (state)
{
btnL.SetWindowTextA("1");
btnR.SetWindowTextA("1");
}
else
{
btnL.SetWindowTextA("0");
btnR.SetWindowTextA("0");
}
}
顺便去自定义控件给按钮添加一个事件
LRESULT OnLClick(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
CString btnStr;
btnL.GetWindowTextA(btnStr);
if (btnStr == "0")
{
btnL.SetWindowTextA("1");
}
else
{
btnL.SetWindowTextA("0");
}
return 0;
}
LRESULT OnRClick(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
CString btnStr;
btnR.GetWindowTextA(btnStr);
if (btnStr == "0")
{
btnR.SetWindowTextA("1");
}
else
{
btnR.SetWindowTextA("0");
}
return 0;
}
好了 接下来就可以通过DDX将这个自定义控件与一个BOOL值之间做交互了
你应该看出来了 。这个控件 就是一个有两个可点击按钮并能通过DDX输出按钮上数字的&运算后取得的BOOL值的复合控件。
回到使用它的地方 用DDX做绑定
BEGIN_DDX_MAP(FirstDialog)
DDX_FD_BOOL(IDC_CUSTOM_FIRST,m_SocketState)
END_DDX_MAP()
BOOL m_SocketState = 1;
给对话框的两个按钮添加事件用来重置状态和进行DDX操作
LRESULT FirstDialog::OnBnClickedOk(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
// TODO: 在此添加控件通知处理程序代码
m_SocketState = 0;
DoDataExchange(TRUE);
return 0;
}
LRESULT FirstDialog::OnBnClickedCancel(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
// TODO: 在此添加控件通知处理程序代码
DoDataExchange(FALSE);
if (m_SocketState)
{
(GetDlgItem(IDC_STATIC)).SetWindowTextA("TRUE");
}
else
{
(GetDlgItem(IDC_STATIC)).SetWindowTextA("FALSE");
}
return 0;
}
看一看做了个什么。
下一篇: jquery实现购物车