http://www.developer.com/

Back to article

Windows Mobile Development Made Easier with WTL


April 2, 2007

What Is WTL?

The Windows Template Library (WTL), available either from the Microsoft web site or from Source Forge and now in version 8.0, is just what its name states: it is a collection of templates that extends ATL to give you, a developer, all you need to build a better and more complex GUI. This includes frames and dialogs, various controls, GDI objects, and so forth. It is still not a framework, so you're free to use those classes as you want for different use-cases. The last, but not the least, feature is that WTL is supported on both Windows Mobile and Smartphone SDKs, where with the latter you don't have an MFC library. As a result, you have small and efficient code, which is pretty close to a pure Win32 program.

One more useful source of documentation is located here. It is an unofficial site, but it does provide CHM help file for WTL 7.5, which is a very good backend for those who need to study WTL.

Version 7.1 WTL contains Application Wizards for eVC 3.0 and 4.0 as well as for VS 2003. Version 7.5 comes with wizards for MS VS 2005 for desktop and mobile. The latest release is WTL 8.0; it contains minor changes to improve compatibility with VS2005 IDE. Although, even in WTL's documentation Windows Mobile 5.0 SDK isn't listed, you can compile and run apps with Windows Mobile 5.0 SDK after doing the necessarily additions to the standard 'include' folders search path in the IDE.

As the library's documentation says, WTL was not intended for porting between different platforms but rather to allow developers to use the same library everywhere. That may be a real key feature in many aspects, such as common knowledge and re-use of existing code. You might be able to minimize or at least significantly reduce the amount of time needed to get a take-off start in the mobile environment.

Having said all this, you can turn to a quick review of what WTL offers you as a developer.

Building a Simple Application

Once you have installed a wizard, you can use it to create several types of projects similar to MFC ones. The whole process includes just a few steps, depending on which IDE you are using (eVC or Visual Studio 200X); so, in the case of VS2005, first you select a wizard:



Click here for a larger image.

Then, after selecting the required platforms, you can choose a project type suitable for your needs,



Click here for a larger image.

and finally decide what kind of view you want to use in your project for SDI and Multi-threaded SDI architectures:



Click here for a larger image.

There are minor differences between eVC 4.0 and VS2005—for example, in supported view types—but mainly they are the same.

WTL comes with several sample projects, so you can explore them to get more details on how this all is built. Some of them will require minor adjustments to get compiled under Windows Mobile, but that's even for your own benefit because it gives you a good opportunity to learn it quicker. Here I'd like to put Dialog-based application's _tMain function to illustrate how close to a bare Win32 app it is:

int WINAPI _tWinMain(HINSTANCE hInstance,
                     HINSTANCE /*hPrevInstance*/,
                     LPTSTR lpstrCmdLine, int nCmdShow)
{
   HRESULT hRes = CMainDlg::ActivatePreviousInstance(hInstance, 
                                                     lpstrCmdLine);

   if(FAILED(hRes) || S_FALSE == hRes)
   {
      return hRes;
   }

   hRes = ::CoInitializeEx(NULL, COINIT_MULTITHREADED);
   ATLASSERT(SUCCEEDED(hRes));

   AtlInitCommonControls(ICC_UPDOWN_CLASS);
   SHInitExtraControls();

   hRes = _Module.Init(NULL, hInstance);
   ATLASSERT(SUCCEEDED(hRes));

   int nRet = CMainDlg::AppRun(lpstrCmdLine, nCmdShow);

   _Module.Term();
   ::CoUninitialize();

   return nRet;
}

As you can see from the SPControls sample, you need minimal effort to get functionality:

You can find the whole list of implemented classes either in readme.htm or the documentation noted above. I would recommend that you also to have a look into the WTL headers to discover which classes are unsupported on Windows Mobile platforms. Also, note that atlwince.h declares about a couple dozen WinCE specific classes, which are really great ones: CHtmlCtrlT, CStdSimpleDialogResizeImpl, or CVoiceRecorderCtrlT are just a small part of them.

Message Maps & Co.

ATL already has things similar to MFC, such as macros for message map, message handlers, command handlers, and many more. WTL doesn't add anything new here. But, the WTL wizard gives you a clue about how to declare different types of handlers:

BEGIN_MSG_MAP(CMainDlg)
   MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
   COMMAND_ID_HANDLER(ID_APP_ABOUT, OnAppAbout)
   COMMAND_ID_HANDLER(IDOK, OnOK)
   COMMAND_ID_HANDLER(IDCANCEL, OnCancel)
   COMMAND_ID_HANDLER(IDC_BUTTON_SHOW, OnShowText)
END_MSG_MAP()

// Handler prototypes (uncomment arguments if needed):
//   LRESULT MessageHandler(UINT /*uMsg*/, WPARAM /*wParam*/,
//                          LPARAM /*lParam*/, BOOL& /*bHandled*/)
//   LRESULT CommandHandler(WORD /*wNotifyCode*/, WORD /*wID*/,
//                          HWND /*hWndCtl*/, BOOL& /*bHandled*/)
//   LRESULT NotifyHandler(int /*idCtrl*/, LPNMHDR /*pnmh*/,
//                         BOOL& /*bHandled*/)

LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/,
                     LPARAM /*lParam*/, BOOL& /*bHandled*/);
LRESULT OnAppAbout(WORD /*wNotifyCode*/, WORD /*wID*/,
                   HWND /*hWndCtl*/, BOOL& /*bHandled*/);
LRESULT OnOK(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/,
             BOOL& /*bHandled*/);
LRESULT OnCancel(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/,
                 BOOL& /*bHandled*/);
LRESULT OnShowText(WORD /*wNotifyCode*/, WORD /*wID*/,
                   HWND /*hWndCtl*/, BOOL& /*bHandled*/);

Therefore, declaring a message, command, or to notify handlers is a trivial task.

Apart from numerous forms, frames, and views, WTL brings a few helper objects that allow you to simplify your code and also make it more structured. <atlapp.h> holds a few useful classes:

  • CMessageLoop: Controls message processing
  • CMessageFilter: Provides a convenient mechanism of message filtering
  • CIdleHandler: Idle periods processing handler

So, you can add a message loop to global the CAppModule _Module variable, and then add or remove desired message filters and so forth.

Using DDX with WTL

If you have things such as edit boxes or other controls on your form or dialog, it is quite likely that you will need to send data back and forth. WTL helps you here and adds its own content to provide DDX support. atlddx.h contains many DDX macros for every purpose. To get DDX functionality, you have to add CWinDataExchange to the parent's list of your dialog or view and put in DDX map macros:

class CWtlFormView : public CDialogImpl<CWtlFormView>,
                     public CWinDataExchange<CWtlFormView>
{
public:
   ...
   BEGIN_DDX_MAP(CWtlFormView)
      DDX_TEXT(IDC_EDIT1, m_sEditText)
   END_DDX_MAP()
   ...

   CString m_sEditText;
};

Those macros declare your class implementation of the DoDataExchange() function, which you will use in the code to exchange data with desired controls:

DoDataExchange(FALSE);    // to copy data to UI control
DoDataExchange(TRUE);     // to get data from UI control

Using WTL Classes

Although using various WTL View classes is quite straightforward, let me illustrate how to use control-like classes your code. There are couple of techniques to do so. You will see them right now.

The simplest case is when you don't need to alter control's behavior; in other words, its WinProc may be left unchanged. For instance, if you need to connect CEdit object to EDIT dialog control, all you need to do is this:

// Attach our object to dialog control
   m_Edit = ::GetDlgItem(hwnd, IDC_EDIT1);
// call some function to illustrate the usage
   m_Edit.SetReadOnly();

As you can see, that's quite simple. This way, you can add an object-oriented style even to a Win32 application.

Another method should be used when you want to change the control's behavior in any way. This is well known as subclassing. As a more complicated sample to illustrate this method, consider the CBitmapButton control, which is originally not supported under Windows CE both in WTL and MFC 8.0. On my opinion, that's a real pity, so I have decided to revive it back.

CBitmapButton Backport

The actual job of adding bitmap button support for Windows Mobile is not that hard, so once you have copied its code from the AtlCtrlx.h header and removed all unnecessary message handlers, that's it. For the purposes of this article, you will see how to use this CBitmapButton in the next paragraphs.

So, back to subclassing, the CBitmapButtonImpl class contains the following overloaded implementation of SubclassWindow:

// overridden to provide proper initialization
BOOL SubclassWindow(HWND hWnd)
{
   #if (_MSC_VER >= 1300)
      BOOL bRet = ATL::CWindowImpl< T, TBase,
                  TWinTraits>::SubclassWindow(hWnd);
   #else // !(_MSC_VER >= 1300)
      typedef ATL::CWindowImpl< T, TBase, TWinTraits>  _baseClass;
      BOOL bRet = _baseClass::SubclassWindow(hWnd);
   endif    // !(_MSC_VER >= 1300)
      if(bRet)
         Init();
      return bRet;
}

The above function calls the base class' implementation and makes an additional initialization. I've added one more simple function to this chain

BOOL AttachToDlgItem(HWND hwndParent, UINT nDlgID)
{
   m_nDlgItem   = nDlgID;
   m_hwndParent = hwndParent;
   return SubclassWindow(::GetDlgItem(hwndParent,nDlgID));
}

to simplify manipulating and further maintaining the CBitmapButton control. All that left is to provide a few images and off you go!

LRESULT CMainDlg::OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/,
                               LPARAM /*lParam*/,
                               BOOL& /*bHandled*/)
{
   // Skip irrelevant code here
   ...
      // Attach to OK button
      m_btnOK.AttachToDlgItem(m_hWnd,IDOK);
      m_btnOK.SetImages(0,1);
   // Attach to Cancel button
      m_btnCancel.AttachToDlgItem(m_hWnd,IDCANCEL);
      m_btnCancel.SetImages(0,1);
   // Attach to About button
      m_btnAbout.AttachToDlgItem(m_hWnd,ID_APP_ABOUT);
      m_btnAbout.SetImages(0,1);

      WTL::CBitmap bmp;
      m_ImageList.Create(64, 31, ILC_COLOR, 0, 2);

      bmp.LoadBitmap(IDB_BITMAP_N);
      m_ImageList.Add((HBITMAP)bmp);
      bmp.DeleteObject();

      bmp.LoadBitmap(IDB_BITMAP_F);
      m_ImageList.Add((HBITMAP)bmp);
      bmp.DeleteObject();

      m_btnOK.SetImageList(m_ImageList);
      m_btnCancel.SetImageList(m_ImageList);
      m_btnAbout.SetImageList(m_ImageList);

      return TRUE;
}

Here is the result of your efforts:

Conclusion

The WTL library is still alive. And now, it gets even more fuel with WinCE support. This article gives you a brief introduction to get you started quickly. If you search on the web, you can find many resources to provide even more help than comes with WTL wizards. Here is just one example, but your own hands-on experience is the best way to learn it all. Thus, come on and use it in your applications!

Download

Download the accompanying code's zip file here.

About the Author

Alex Gusev started to play with mainframes at the end of the 1980s, using Pascal and REXX, but soon switched to C/C++ and Java on different platforms. When mobile PDAs seriously rose their heads in the IT market, Alex did it too. After working almost a decade for an international retail software company as a team leader of the Windows Mobile R department, he has decided to dive into Symbian OS ™ Core development.

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date