在MFC中使用WTL
2004-08-04 09:03
696 查看
http://www.codeproject.com/wtl/mix_wtl_mfc.asp?df=100&forumid=14171&exp=0&select=666091#xx666091xx
Here you can find the required files, a detailed explanation in 10 steps and a working demo.
Open its precompiled header file (usually
This prevents the WTL headers to automatically merge the WTL namespace to the global namespace. This avoids conflicts with MFC classes with the same names, such as
#include <atlapp.h>
extern WTL::CAppModule _Module;
The ATL/WTL code may access the global
We add the common WTL header here to exploit precompiled headers, but you may want to include it only where you really need it.
Open the project main file, where the
The global
Modify
If not present, add the
Make a new class, derived from
Open the class header file and include any WTL header you need:
Remember that if you didn't choose to use the precompiled header at point 3 above, you need to add that line here, before any other WTL header:#include <atlwin.h>
Include the necessary header to turn a
You may want to put the above line into your precompiled header instead, like all the other WTL headers, if you use them many times, to speed up recompilation. That's completely at your choice.
Then modify the class declaration to make the conversion to
Add any other function the WTL class may require, paying attention to specify the WTL namespace for its arguments. In this example we need to implement
Using helper functions could also make writing "bridge" classes easier. For example, you could define a
// Inline helper
void DoPaint(WTL::CDCHandle dc)
{
OnDraw(CDC::FromHandle(dc));
}
protected:
// Must be implemented in derived class
virtual void OnDraw(CDC* pDC) = 0;
I used a pure virtual function, but
What I did was to copy all the
ae73
TBase
To use the template you need to pass both the derived class and the base class, as you can see in the example above about a scrolling control.
Then I added the necessary code to enable WTL message maps. I implemented the MFC function
virtual LRESULT DefWindowProc(UINT nMsg, WPARAM wParam, LPARAM lParam)
{
T* pT = static_cast<T*>(this);
ATLASSERT(::IsWindow(pT->m_hWnd));
LRESULT lResult;
if (pT->ProcessWindowMessage(m_hWnd, nMsg, wParam, lParam, lResult))
return lResult;
return TBase::DefWindowProc(nMsg, wParam, lParam);
}
Remember that
Note that you don't have to change the MFC message map macros, that skip to the MFC base class ignoring the intermediate template
I also commented out "dangerous" member functions already defined by MFC, such as
There may be also some functions identical to the MFC ones, that could be removed. Feel free to suggest improvements.
You need to explicitly specify namespaces when accessing WTL objects, which is not a bad practice anyway.
And you may find that the compiler complains about some member functions of your hybrid classes. That's because many
That's all. I just used this method a couple of times, so I'm not aware of any other problems. But I expect comments...
To test the control use the mouse on the scroll bars or move the focus on it and use the keyboard arrows, holding down the CTRL key to scroll by pages. There is no focus indicator, so you have to guess by exclusion. There is also a button to load external BMP files.
Please note that this control is not meant to be fully featured, but only an example of use for WTL templates in MFC projects. In particular, I expect bugs if the control is used in a resizable dialog, as to my experience the WTL classes that implement scrolling are not "perfect" from that point of view. I'm working on a replacement class, but no time to release it yet.
Any idea to improve this article, please let me know!
What is it?
This article presents a way to use WTL template classes on MFC window classes, that is how to transform the MFCCWndclass to its ATL/WTL counterpart
CWindow, while leaving the class usable from MFC code.
Here you can find the required files, a detailed explanation in 10 steps and a working demo.
How to do it, step by step
These are the basic steps to obtain hybrid MFC/WTL windows. They have been used on MFC dialog based projects, but should work with any MFC application.Add WTL support to an MFC application
Take an MFC project you want to modify so that it supports ATL/WTL functions and classes.Open its precompiled header file (usually
"stdafx.h") and add the following lines:
// Add support for ATL/WTL #define _WTL_NO_AUTOMATIC_NAMESPACE
This prevents the WTL headers to automatically merge the WTL namespace to the global namespace. This avoids conflicts with MFC classes with the same names, such as
CRect,
CDCand others.#include <atlbase.h>
#include <atlapp.h>
extern WTL::CAppModule _Module;
The ATL/WTL code may access the global
_Modulevariable, so it must have external linkage.#include <atlwin.h>
We add the common WTL header here to exploit precompiled headers, but you may want to include it only where you really need it.
Open the project main file, where the
CWinApp-derived class is implemented, and modify it as follows:
///////////////////////////////////////////////////////////////////////////// // The one and only CMixedWindowApp object CMixedWindowApp theApp; WTL::CAppModule _Module; // add this line
The global
_Modulevariable must be defined somewhere and this is a good place.
Modify
InitInstance()as follows:
///////////////////////////////////////////////////////////////////////////// // CMixedWindowApp initialization BOOL CMixedWindowApp::InitInstance() { // Initialize ATL _Module.Init(NULL, AfxGetInstanceHandle()); ... }
If not present, add the
ExitInstance()virtual function to the application object, for example using ClassWizard, then modify it as follows:
int CMixedWindowApp::ExitInstance() { // Terminate ATL _Module.Term(); return CWinApp::ExitInstance(); }
Make an hybrid window class
Assume we want to add scrolling capabilities to a static bitmap control. We may want to use the WTL template classCScrollImpl<...>together with the MFC class
CStatic, as there is no such a window class in MFC.
Make a new class, derived from
CStatic(or any other CWnd-derived class), for example using ClassWizard.
Open the class header file and include any WTL header you need:
// Add support for scrolling windows #include <atlscrl.h>
Remember that if you didn't choose to use the precompiled header at point 3 above, you need to add that line here, before any other WTL header:#include <atlwin.h>
Include the necessary header to turn a
CWndclass into a
CWindow. It contains a template class that defines the missing members:
// Make this class CWindow compatible #include "Wnd2Window.h"
You may want to put the above line into your precompiled header instead, like all the other WTL headers, if you use them many times, to speed up recompilation. That's completely at your choice.
Then modify the class declaration to make the conversion to
CWindowand to use the WTL template class you have chosen:
class CScrollPicture : public CWnd2Window<CScrollPicture, CStatic>, public WTL::CScrollImpl<CScrollPicture>
Add any other function the WTL class may require, paying attention to specify the WTL namespace for its arguments. In this example we need to implement
DoPaint()to work with scrolling:
public: // Inline helper void DoPaint(WTL::CDCHandle dc) { OnDraw(CDC::FromHandle(dc)); } protected: // Implement painting with scroll support void OnDraw(CDC* pDC);
Notes on implementation
It may be a good idea not to mix the code too much with MFC and WTL classes used anywhere, as it could become quite confusing. An efficient way could be defining inline helper functions that translates WTL arguments to their MFC counterpart, unless you want to write the required functions using only WTL. Note that you can safely mix WTL objects with MFC code, but you always need to specify the WTL namespace when appropriate.Using helper functions could also make writing "bridge" classes easier. For example, you could define a
CScrollWndclass to reuse (through inheritance) whenever you need scrolling capabilities and custom drawing, declaring a virtual
OnDraw()function like in
CViewand
CScrollView:public:
// Inline helper
void DoPaint(WTL::CDCHandle dc)
{
OnDraw(CDC::FromHandle(dc));
}
protected:
// Must be implemented in derived class
virtual void OnDraw(CDC* pDC) = 0;
I used a pure virtual function, but
CViewdefines a base implementation that just fires an assertion. These are just some ideas, but feel free to suggest new.
Sounds good... but how does it work?
Well, nothing special or mysterious. The ATL classCWindow, which is used by WTL, has only one member variable,
m_hWnd, that is exactly the same as the one you can find in the
CWndclass. So all the
CWindowmember functions need just that member variable, that can also be found in a
CWnd-derived class.
What I did was to copy all the
CWindowmembers, except
m_hWnd, to a include file and define a new template class.template <class T, class TBase> class CWnd2Window : public
ae73
TBase
To use the template you need to pass both the derived class and the base class, as you can see in the example above about a scrolling control.
Then I added the necessary code to enable WTL message maps. I implemented the MFC function
DefWindowProc(), that is called after
WindowProc(), when no entry is found in the MFC message map.protected:
virtual LRESULT DefWindowProc(UINT nMsg, WPARAM wParam, LPARAM lParam)
{
T* pT = static_cast<T*>(this);
ATLASSERT(::IsWindow(pT->m_hWnd));
LRESULT lResult;
if (pT->ProcessWindowMessage(m_hWnd, nMsg, wParam, lParam, lResult))
return lResult;
return TBase::DefWindowProc(nMsg, wParam, lParam);
}
Remember that
WindowProc()is called first. If it is not overridden or if you pass the message to the base implementation in
CWnd, the MFC message handler is called. If the corresponding entry is not found in the MFC message map or if you call the base implementation in your message handler or if you explicitly call
Default(), the message finally arrives to
DefWindowProc()that calls the ATL/WTL message map implementation.
Note that you don't have to change the MFC message map macros, that skip to the MFC base class ignoring the intermediate template
CWnd2Window. I don't define an MFC message map there, so this is perfectly legal.
I also commented out "dangerous" member functions already defined by MFC, such as
Attach(),
Detach(),
CreateWindow()and
DestroyWindow(), but there may be others to comment out I'm not aware of. As far as I know
Attach/
Detachdeal with
CWndmaps, while
CreateWindow/
DestroyWindowset up Windows hooks or call some virtual functions. So if you still want to use your class as a
CWnd, you need to call the original functions, not those defined by
CWindow.
There may be also some functions identical to the MFC ones, that could be removed. Feel free to suggest improvements.
So far, so good... but what changes?
Well, you have to pay attention to a few things:You need to explicitly specify namespaces when accessing WTL objects, which is not a bad practice anyway.
And you may find that the compiler complains about some member functions of your hybrid classes. That's because many
CWndand
CWindowmembers have the same name and you must resolve the ambiguity to compile.
That's all. I just used this method a couple of times, so I'm not aware of any other problems. But I expect comments...
License
I suppose you can freely use the code as long as you have the right to use ATL source code. There is no original code here, except for theCScrollPictureclass, only a nice idea. All the source code by me is released to the public domain, you may do what you want with it. As for the
CWnd2Windowclass, almost all the code is copyright of Microsoft Corporation.
The demo project
In the attached demo project you can find a very simple implementation of a scrolling picture control. Since I subclass aCStaticcontrol, I need to override the default behaviour sometimes. A much cleaner implementation would have used a
CWnd, but that way I can show that message routing works as expected, even mixing MFC message handlers with
WindowProc().
To test the control use the mouse on the scroll bars or move the focus on it and use the keyboard arrows, holding down the CTRL key to scroll by pages. There is no focus indicator, so you have to guess by exclusion. There is also a button to load external BMP files.
Please note that this control is not meant to be fully featured, but only an example of use for WTL templates in MFC projects. In particular, I expect bugs if the control is used in a resizable dialog, as to my experience the WTL classes that implement scrolling are not "perfect" from that point of view. I'm working on a replacement class, but no time to release it yet.
Any idea to improve this article, please let me know!
相关文章推荐
- 如何在WTL和MFC中使用duilib及如何静态使用duilib库!(初级讲解 附带一个Demo)
- WTL的CBitmapButton在MFC下完美使用
- duilib篇 如何在WTL和MFC中使用duilib及如何静态使用duilib库!(初级讲解 附带一个Demo)
- 探索WTL和MFC混合使用的问题,兼容VC2010编译器
- duilib篇 如何在WTL和MFC中使用duilib及如何静态使用duilib库!(初级讲解 附带一个Demo)
- Windows下WTL与MFC使用Invalidate()闪烁问题解决
- Windows Mobile下使用Native C++(WTL, MFC, Win32)开发,如何为对话框加入菜单
- 在MFC中使用纯COM方式来操纵Flash OCX (IShockwaveFlash)
- MFC下的MessageBox使用_附带CBUTTON
- MFC 中位图的使用
- 译:MFC 程序员的 WTL 教程(一)
- VC/MFC中如何使用WPF?
- MFC部分控件的使用
- mfc中comb box的使用
- MFC中使用CreateThread创建线程
- MFC中串口类的使用
- 使用MFC过程中遇到的问题以及解决方法(三)
- MFC使用第三方CSpreadSheet
- Scintilla 在MFC中的简易使用(动态、静态)
- MFC复选框CheckBox使用