自从上次我写了BCB里使用WTL的之后,有来信讨论关于BCB里使用WTL有没有价值的问题。这个问题其实和实际应用有关,每个人所在领域不同就有不同观点。
偶就不讨论这个问题了,直接用一个VCL和WTL混用的实例来说明吧:
VCL里的Hint功能是通过HintWindow来实现的,如果不通过一些手段的话它的样子是相当土的?我们这里就用2K以上系统新加入的气泡提示来代替VCL的Hint。
首先要明确一点,在VCL应用中,Application->Run已经有一个消息循环了,我们WTL里的_Moudle.Run()就不能再使用了,所以WTL里一些功能是用不上的,如:CIdleHandler, CMessageFilter等。
好,动工:
1.起手式
新建Application, 保存(假设以默认文件名保存,即Unit1.cpp,Project1.cpp这样),进入工程选项,包含路径和库路径里加入ATL,WTL所在文件夹[见BCB与WTL(一)],Conditionals里加入USING_ATL和STRICT。
新建一个头文件(假设名为vclwtl.h),写上VCL,WTL都要用的头文件和一些定义:
- #define WINVER 0x0500
- #define _WIN32_WINNT 0x0501
- #define _WIN32_IE 0x0501
- #include <vcl.h>
- #include <atlbase.h>
- #include <atlapp.h>
- extern CAppModule _Module;
- #include <atlwin.h>
- #include <atlmisc.h>
把工程里所有的cpp文件(这里只有两个:Unit1.cpp,Project1.cpp)第一行由原来的#include <vcl.h>改成#include "vclwtl.h"
试着编译一下,应该能够成功编译。
2.定义_Module
不管这个_Module有没有用,定义还是需要地。在Project1.cpp里写上_Module的定义,变成这样子:
- //---------------------------------------------------------------------------
- #include "wtlh.h" //上一步里改的
- #pragma hdrstop
- //---------------------------------------------------------------------------
- USEFORM("Unit1.cpp", Form1);
- //---------------------------------------------------------------------------
- CAppModule _Module; // 声明
- WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInstance,
- LPSTR lpCmdLine, int nCmdShow)
- {
- try
- {
- _Module.Init(NULL,hInst); // 初始化
- Application->Initialize();
- Application->CreateForm(__classid(TForm1), &Form1);
- Application->Run();
- _Module.Term(); // 终止,没有_Module.Run()哦
- }
- catch (Exception &exception)
- {
- Application->ShowException(&exception);
- }
- catch (...)
- {
- try
- {
- throw Exception("");
- }
- catch (Exception &exception)
- {
- Application->ShowException(&exception);
- }
- }
- return 0;
- }
- //---------------------------------------------------------------------------
好,现在我们就可以很嚣张地在VCL里使用WTL组件了,喔耶~~
3.引入CToolTipCtrl
因为CToolTipCtrl在atlctrls.h里,所以在Unit1.h写上#include <atlctrls.h>。在TForm1里加入成员变量CToolTipCtrl m_tip;
再编译一下,嗯?出错了?说没有ImageList_Read函数!晕,这个函数不是在commctrl.h里好好地睡着么?我找了半天也没找到怎样正确设置编译开关使其通过,算了,直接在#include <atlctrls.h>之前
加四句:
- WINCOMMCTRLAPI HIMAGELIST WINAPI ImageList_Read(LPSTREAM pstm);
- WINCOMMCTRLAPI BOOL WINAPI ImageList_Write(HIMAGELIST himl, LPSTREAM pstm);
- WINCOMMCTRLAPI HRESULT WINAPI ImageList_ReadEx(DWORD dwFlags, LPSTREAM pstm, REFIID riid, PVOID* ppv);
- WINCOMMCTRLAPI HRESULT WINAPI ImageList_WriteEx(HIMAGELIST himl, DWORD dwFlags, LPSTREAM pstm);
如果各位路过知道有更好的办法请留言哈。
再编译试试?通过,下一步!
4.在TForm1上放一个Button1(用来演示回调方式显示ToolTip),放一个Label1(演示静态以及非TWinControl显示ToolTip的方式),在它们的Hint属性里写上一些话(演示用)。
在TForm1的构造函数里写:
- __fastcall TForm1::TForm1(TComponent* Owner)
- : TForm(Owner)
- {
- m_tip.Create(Handle,NULL,NULL,WS_POPUP | TTS_NOPREFIX | TTS_BALLOON); //是气泡提示哦
- m_tip.Activate(TRUE); //激活
- m_tip.AddTool(Button1->Handle); //加入Button1,没有指定字符串就是回调方式
- m_tip.AddTool(Handle, Label1->Hint.c_str(), &TRect(Label1->BoundsRect), 10); //加入Label1,注意,Label1是没有Handle的,就指定它的父窗体和它所在的位置。这里也直接指定了字符串。
- }
要让ToolTip可用,在WTL程序里一般是让Tooltip在PreTranslateMessage里用RelayEvent方法来更新状态,可是我们的VCL里貌似没有PreTranslateMessage功能啊?那就用TApplicationEvents来代替吧!
在TForm1上放上TApplicationEvents,处理它的OnMessage事件:
- void __fastcall TForm1::ApplicationEvents1Message(tagMSG &Msg,
- bool &Handled)
- {
- Handled = false;
- if(Msg.hwnd == Button1->Handle || Msg.hwnd == Handle) // AddTool里用了哪些Handle就要测试哪些,不过偷懒的话不测试也行。
- {
- m_tip.RelayEvent(&Msg);
- }
- }
好,开始,编译,运行!
鼠标指向Label1,停留一下,气泡提示出来了吧?指向Button1,停一小时,还是没出来?对了,我说过是要演示回调方式的,呵呵:)
对于回调方式,在需要显示提示时ToolTips会向该控件的父控件发送code为TTN_NEEDTEXT的WM_NOTIFY消息(小提示,VCL也会把它以CN_NOTIFY的方式反射给控件本身),我们通过处理这个消息来指定要显示什么样的提示。
对于Button1,它的父控件就是TForm1,于是我们重载TForm1的WndProc函数:
- void __fastcall TForm1::WndProc(Messages::TMessage &Message)
- {
- TForm::WndProc(Message);
- if(Message.Msg == WM_NOTIFY && TTN_NEEDTEXT == ((LPNMHDR)Message.LParam)->code) //TTN_NEEDTEXT的通知消息
- {
- LPNMTTDISPINFO lpnmtdi = (LPNMTTDISPINFO) Message.LParam;
- HWND hwnd = GetDlgItem(Handle,lpnmtdi->hdr.idFrom); //从ID得到HWND
- TWinControl *ctrl = FindControl(hwnd); //从HWND得到VCL控件指针
- if(ctrl)
- {
- lpnmtdi->lpszText = lpnmtdi->szText; //也可以使用别的内存,这里使用它自带的80字节(提示,默认就是这样的,所以这行可以不写)
- strcpy(lpnmtdi->lpszText,(ctrl->Hint + Now().DateTimeString()).c_str()); //设置提示信息
- lpnmtdi->hinst = NULL; //告诉ToolTip lpnmtdi->lpszText指向的是个字符串而不是资源ID。
- }
- }
- }
再次编译运行,Button1的提示出来了吗?
5.最后
WTL里还有很多宝藏等着我们呢,大家快去挖吧:-)
提示,试试使用m_tip.SetTipBkColor来改变颜色。
<script type="text/javascript"> </script> <script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript"></script>