Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

A Pocket PC WTL image viewer

0.00/5 (No votes)
13 Dec 2004 1  
Some proposed WTL classes and functions at work: CAppWindow, CFullScreenFrame, CStdDialog, CF_DIB clipboard support, and more...

Introduction

ImageView is a PPC application written with WTL.

From WTL 7.1, WTL officially supports PPC 2002 and 2003. This support is continued in the forthcoming OpenSource WTL 7.5 release. However, PPC systems require from applications behaviors that are not, and should not be, included in generic WTL classes.

ImageView puts at work atlppc.h, a limited set of PPC WTL classes and functions which have been submitted for inclusion in WTL 7.5. ImageView makes use of class CZoomScrollImpl described in CodeProject: Add zooming to WTL CScrollImpl.

The ImageViewPPC project, atlppc.h and zoomscrl.h compile with WTL 7.1 and current 7.5 distribution with EVC 3.0 to 4.2 and PPC 2002/2003 SDK.

Background

With WTL comes a very rich sample, BmpView written with code compatibility between desktop and Pocket PC devices. With BmpZoom, I have extended its functionality to continuous zooming.

João Paulo Figueira has given PicView, a nice MFC picture viewer for PPC 2002.

ImageView is inspired from these two models and aims to be (if possible) a better image viewer.

Feature

ImageView

BmpZoom

PicView

Zoom

Continuous Continuous Integer steps

Zoom UI

Trackbar in menu, keys Trackbar in menu Menu buttons (in - out)

Well behaved

Yes No MFC behavior

Image property sheet

Yes Yes No

Clipboard copy and paste

Yes Not completed No

Title Bar

Yes No Yes

Full screen view

Yes No Yes

Tap-and-scroll

Yes No Yes

Show/hide scroll bars

Yes No No

System registration

Yes No No

PPC WTL classes and functions

Standard property sheet and dialogs

CStdPropertySheet

template <class T, bool t_bShowSip = true> 
class CStdPropertySheet : public CPropertySheetImpl<T>
Class description

Standard PPC property sheet with title, empty menubar with SIP depending on t_bShowSip.

Class usage

Derive a property sheet class from CStdPropertySheet as ImageView CImageProperties.

// ImageViewdlg.h
//...
class CImageProperties : public CStdPropertySheet<CImageProperties, false>
{
public:
 CFilePage m_FilePage;
 CImagePage m_ImagePage;
 CViewPage m_ViewPage;
 CImageProperties( LPCTSTR sname, CImageViewView & rview) :
     m_FilePage(sname), m_ImagePage( rview.m_bmp), m_ViewPage( rview) 
 {
     SetTitle( rview.m_sImageName);
     if ( *sname )
         AddPage( m_FilePage);
     AddPage( m_ImagePage);
     AddPage( m_ViewPage);
 }
};

CStdDialog

template <class T, bool t_bModal = true> 
class CStdDialog
Class description

Base class for standard PPC dialogs featuring:

  • PPC dialog title display, and dialog title preparation routine
    //...
    // Title painting
     LRESULT OnPaintTitle(UINT /*uMsg*/, WPARAM /*wParam*/, 
                          LPARAM /*lParam*/, BOOL& bHandled)
    //...
     LRESULT OnInitStdDialog(UINT /*uMsg*/, WPARAM /*wParam*/, 
                             LPARAM /*lParam*/, BOOL& bHandled)
    // Title preparation: move the dialog controls down to make room for title
     void StdDialogTitleInit()
    // ...
  • Close command handler, calling EndDialog( wID) or DestroyWindow() depending on the value of t_bModal.
     //...
     // Standard dialog ending: may be used with any command
     LRESULT OnCloseCmd(WORD /*wNotifyCode*/, WORD wID, 
                        HWND /*hWndCtl*/, BOOL& /*bHandled*/)
     //...
  • Background COLOR_INFOBK setting for static controls of ID: IDC_INFOSTATIC // == IDC_STATIC -1 .
     //...
     // IDC_INFOSTATIC background setting
     LRESULT OnColorStatic(UINT /*uMsg*/, WPARAM wParam, 
                           LPARAM lParam, BOOL& bHandled)
     //...
Class usage

Derive a dialog class from both CDialogImpl and CStdDialog and connect the wanted features through the dialog class message map. CStdDialogImpl does that for all features.

///////////////////////////////////////////////////////////////////////////////
// CStdDialogImpl - implementation of standard PPC dialog

  
template <class T, bool t_bModal = true>  
class CStdDialogImpl : public CDialogImpl<T> , 
                       public CStdDialog<T, t_bModal>
{
public:
 BEGIN_MSG_MAP(CStdDialogImpl)
  MESSAGE_HANDLER(WM_PAINT, OnPaintTitle)
  MESSAGE_HANDLER(WM_CTLCOLORSTATIC, OnColorStatic)
  MESSAGE_HANDLER(WM_INITDIALOG, OnInitStdDialog)
  COMMAND_RANGE_HANDLER(IDOK, IDCANCEL, OnCloseCmd)
 END_MSG_MAP()
};

CStdDialogImpl

template <class T, bool t_bModal = true>
class CStdDialogImpl : public CDialogImpl<T> , public CStdDialog<T, t_bModal>
Class description

Standard PPC dialog implementation for derivation.

Class usage

Derive a dialog class from CStdDialogImpl, and chain the message map as ImageView CMoveDlg and CRegisterDlg.

// ImageViewdlg.h
//... 
 
/////////////////////
// CMoveDlg :  Called by CRegisterDlg to move ImageView.exe to \Windows folder
 
class CMoveDlg : public  CStdDialogImpl<CMoveDlg>
{
public:
 CString m_sAppPath;
 CString m_sApp;
 
 enum { IDD = IDD_MOVE };
 
 BEGIN_MSG_MAP(CMoveDlg)
  MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
  COMMAND_HANDLER(IDC_MOVE, BN_CLICKED, OnMove)
  CHAIN_MSG_MAP(CStdDialogImpl<CMoveDlg>)
 END_MSG_MAP()
//...

CStdSimpleDialog

template< WORD t_wDlgTemplateID,
 UINT t_shidiFlags = SHIDIF_DONEBUTTON | SHIDIF_SIPDOWN | SHIDIF_SIZEDLGFULLSCREEN>
class CStdSimpleDialog :
 public CSimpleDialog< t_wDlgTemplateID, FALSE>,
 public CStdDialog< CStdSimpleDialog< t_wDlgTemplateID, t_shidiFlags> >
Class description

Standard CSimpleDialog (modal only) with initial settings t_shidiFlags which default to CSimpleDialog settings.

Class usage

Instantiate as in ImageView OnAppAbout handler.

// mainfrm.h
//...


LRESULT OnAppAbout(WORD /*wNotifyCode*/, WORD /*wID*/, 
                   HWND /*hWndCtl*/, BOOL& /*bHandled*/)
 {
  CStdSimpleDialog<IDD_ABOUTBOX, 
               SHIDIF_DONEBUTTON | SHIDIF_FULLSCREENNOMENUBAR> dlg;
  return FSDoModal( dlg);
 }

//...


Application behavior

CAppInfoBase

class CAppInfoBase
Class description

Helper for application state save/restore to registry. Opens or creates a sAppKey registry key under HKEY_CURRENT_USER. Save and Restore members transfer program variables to that key.

//...

class CAppInfoBase
{
public:
 CRegKey m_Key;

 CAppInfoBase( _U_STRINGorID sAppKey) 
 {
  m_Key.Create( HKEY_CURRENT_USER, sAppKey.m_lpstr);
  ATLASSERT( m_Key.m_hKey);
 }

 template< class V>
 LONG Save( V& val, _U_STRINGorID sName)
 {
  return ::RegSetValueEx( m_Key, sName.m_lpstr, 0, REG_BINARY, 
                         (LPBYTE) &val, sizeof(V));
 }

 template< class V>
 LONG Restore( V& val, _U_STRINGorID sName)
 {
  DWORD valtype;
  DWORD bufSize = sizeof(V);
  return ::RegQueryValueEx( m_Key, sName.m_lpstr, 0, &valtype, 
                           (LPBYTE)&val, &bufSize); 
 }
//...
Class usage

Derive from CAppInfoBase if you need to save/restore nested variables. CAppInfoBase has specialized Save and Restore members for CString type. Add other needed specializations in your derived class.

//...
#if defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__)
 
 LONG Save( CString& sval, _U_STRINGorID sName)
 {
   return m_Key.SetValue( sval, sName.m_lpstr);
 }

 LONG Restore( CString& sval, _U_STRINGorID sName)
 {
  DWORD size = MAX_PATH;
  LONG res = m_Key.QueryValue( sval.GetBuffer( size), sName.m_lpstr, &size);
  sval.ReleaseBuffer();
  return res;
 }
//...

CAppInfoT

template < class T >
class CAppInfoT : public CAppInfoBase
Class description

CAppInfoBase associated with CAppWindow<T>.

template < class T >
class CAppInfoT : public CAppInfoBase
{
public:
 CAppInfoT() : CAppInfoBase( T::m_szAppKey){}
};
Class usage

CAppWindow<T> defines its CAppInfo type as CAppInfoT<T>.

//...
template <class T>
class CAppWindow
{
public:
 typedef class CAppInfoT<T> CAppInfo;
//...

Instantiate CAppInfoT as in CRegisterDlg::Register.

// ImageViewdlg.h
//...
class CRegisterDlg : public CStdDialogImpl<CRegisterDlg>
{
public:
//...
 void Register( ImageType typ, BOOL bRegister)
 {
  CAppInfoT<CMainFrame> info;
//...

CAppWindow

template <class T>
class CAppWindow
Class description

Base class for PPC application frame window featuring:

  • Command line parameters transmission to OnCreate handler and to previous instance:
    //...
    template <class T>
    class CAppWindow
    {
    public:
    //...
    // Same as AppWizard generated Run + lpstrCmdLine in CreateEx
     static int AppRun(LPTSTR lpstrCmdLine = NULL, 
                       int nCmdShow = SW_SHOWNORMAL)
    //... 
    // Same as AppWizard generated ActivatePreviousInstance
    // + SendMessage WM_COPYDATA
     static HRESULT ActivatePreviousInstance(HINSTANCE hInstance, 
                                             LPCTSTR  lpstrCmdLine ) 
    //...
    // Operations overriden in derived class
     bool AppNewInstance( LPCTSTR lpstrCmdLine)
    //...
    // Message map and handlers
     MESSAGE_HANDLER( WM_COPYDATA, OnNewInstance)
    //...
     LRESULT OnNewInstance(UINT /*uMsg*/, WPARAM /*wParam*/, 
             LPARAM lParam, BOOL& /*bHandled*/)
     {
      T* pT = static_cast<T*>(this);
      PCOPYDATASTRUCT pcds = (PCOPYDATASTRUCT) lParam;
      return pT->AppNewInstance((LPCTSTR)pcds->lpData);
     }
    //...
  • WM_HIBERNATE message support:
    //...
    template <class T> 
    class CAppWindow 
    { 
    public: 
    //... 
     bool m_bHibernate;
    //... 
    // Operations overriden in derived class 
     bool AppHibernate( bool bHibernate) 
    //... 
    // Message map and handlers  
     MESSAGE_HANDLER( WM_HIBERNATE, OnHibernate) 
     MESSAGE_HANDLER( WM_ACTIVATE, OnActivate) 
    //... 
     LRESULT OnHibernate(UINT /*uMsg*/, WPARAM /*wParam*/, 
             LPARAM /*lParam*/, BOOL& /*bHandled*/) 
     {  
      T* pT = static_cast<T*>(this);  
      return m_bHibernate = pT->AppHibernate( true);  
     } 
    //... 
     LRESULT OnActivate(UINT uMsg, WPARAM wParam, 
             LPARAM lParam, BOOL& bHandled)  
     { 
      T* pT = static_cast<T*>(this);
      if ( m_bHibernate)
        m_bHibernate = pT->AppHibernate( false); 
    //...
  • Activation deactivation and setting change support through relevant system calls:
    //...
    template <class T> 
    class CAppWindow 
    { 
    public: 
    //...
     SHACTIVATEINFO m_sai;
    //... 
    // Message map and handlers  
      MESSAGE_HANDLER( WM_ACTIVATE, OnActivate)
      MESSAGE_HANDLER( WM_SETTINGCHANGE, OnSettingChange)
    //... 
     LRESULT OnActivate(UINT uMsg, WPARAM wParam, 
                        LPARAM lParam, BOOL& bHandled)
     {
      T* pT = static_cast<T*>(this);
    //...
      return SHHandleWMActivate( pT->m_hWnd, wParam, lParam, &m_sai, 0);
     }
     LRESULT OnSettingChange(UINT uMsg, WPARAM wParam, 
                             LPARAM lParam, BOOL& bHandled)
     {
      T* pT = static_cast<T*>(this);
      bHandled = FALSE;
      return SHHandleWMSettingChange( pT->m_hWnd, wParam, lParam, &m_sai);
     }
    //...
  • Application state persistence support.
    //...
    template <class T> 
    class CAppWindow 
    { 
    public: 
    //...
      typedef class CAppInfoT<T> CAppInfo;
    
      static LPCTSTR m_szAppKey;
    // Operations overriden in derived class
    //...  
     void AppSave() 
    //... 
    // Message map and handlers
       MESSAGE_HANDLER( WM_CLOSE, OnClose)
    //... 
     LRESULT OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
     {
      T* pT = static_cast<T*>(this);
      pT->AppSave();
      return bHandled = FALSE;
     }
    //...
Class usage
  • In your AppWizard generated application ::_tWinMain
    • define LPCTSTR CMainFrame::m_szAppKey,
    • call CMainFrame::ActivatePreviousInstance with two parameters,
    • change the call to Run into CMainFrame::AppRun and
    • delete the Run function.
// ImageView.cpp
//...

LPCTSTR CMainFrame::m_szAppKey = L"Software\\CodeProject\\ImageView";

int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, 
                     LPTSTR lpstrCmdLine, int nCmdShow)
{
 
 HRESULT hRes = CMainFrame::ActivatePreviousInstance(hInstance, lpstrCmdLine ); 
//...
 int nRet = CMainFrame::AppRun(lpstrCmdLine, nCmdShow);
//...
}
  • In your frame definition
    • Add CAppWindow to your main window inheritance list,
    • chain the message map,
    • delete ActivatePreviousInstance member definition,
    • define all or part of AppHibernate, AppNewInstance and AppSave members,
    • process the command line parameters, and/or restore the application status and data in the OnCreate handler.
// mainfrm.h
//...

class CMainFrame : 
//...
  public CAppWindow<CMainFrame>
{
//...
// CAppWindow operations
 bool AppHibernate( bool bHibernate)
 {
  if ( bHibernate) // go to sleep
   if ( m_sFile.IsEmpty()) // clipboard or no image
    return false;
   else
    m_view.m_bmp.DeleteObject();  
  else // wake up 
   if ( HBITMAP hbm = LoadImageFile( m_sFile))
    m_view.m_bmp.Attach( hbm);
   else // file was moved or deleted during hibernation
    CloseImage();
  return bHibernate;
 }

 bool AppNewInstance( LPCTSTR lpstrCmdLine)
 {
  return SetImageFile( lpstrCmdLine) != NULL;
 }

 void AppSave()
 {
  CAppInfo info;
  BOOL bTitle = m_view.m_TitleBar.IsWindowVisible();
  info.Save( bTitle, L"TitleBar");
  info.Save( m_view.m_bShowScroll, L"ScrollBars");
  info.Save( m_bFullScreen, L"Full");
  info.Save( m_sFile, L"Image");
  info.Save( m_view.GetScrollOffset(), L"Scroll");
  info.Save( m_view.m_fzoom, L"Zoom");
 }
//...
// Message map and handlers
 BEGIN_MSG_MAP(CMainFrame)
//...
  CHAIN_MSG_MAP(CAppWindow<CMainFrame>)
//...
 LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, 
                  LPARAM lParam, BOOL& /*bHandled*/)
 {
  CAppInfo info;

  // Full screen delayed restoration 
  bool bFull = false;
  info.Restore( bFull, L"Full");
  if ( bFull)
   PostMessage( WM_COMMAND, ID_VIEW_TOOLBAR);
//...   
  // TitleBar creation
  BOOL bTitle = TRUE;
  info.Restore( bTitle, L"TitleBar");
  DWORD dwStyle = WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | CCS_TOP;
  if ( bTitle)
   dwStyle |= WS_VISIBLE;
  CreateSimpleStatusBar( L"", dwStyle);
//...

  // Image initialization 

 
  LPCTSTR pCmdLine = (LPCTSTR)((LPCREATESTRUCT)lParam)->lpCreateParams;
  if ( *pCmdLine )// open the command line file
   SetImageFile( pCmdLine);
  else   // restore previous image if existing
  {
//...

Frame size and position

AtlFixUpdateLayout

void AtlFixUpdateLayout( HWND hWndFrame, HWND hWndMenuBar)
Function description

Frame positioning fix for WTL 7.1 (today still needed with 7.5).

Function usage
  • If you do not use CFullScreenFrame, in your AppWizard generated CMainFrame::OnCreate, call AtlFixUpdateLayout after menubar creation:
    //...
    
    LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, 
                     LPARAM /*lParam*/, BOOL& /*bHandled*/)
    {
       CreateSimpleCEMenuBar(IDR_MAINFRAME, SHCMBF_HMENU);
       AtlFixUpdateLayout( m_hWnd, m_hWndCECommandBar);
       //...
  • If you want to use CFullScreenFrame, define a UpdateLayout member that calls AtlFixUpdateLayout and then the base class UpdateLayout.
    // ...
        void CMainFrame::UpdateLayout(BOOL bResizeBars = TRUE)
        {
        AtlFixUpdateLayout( m_hWnd, m_hWndCECommandBar);
        CFrameWindowImplBase<CMainFrame>::UpdateLayout( bResizeBars)
        }
    // ...

CFullScreenFrame

template < class T, bool t_bHasSip = true>
class CFullScreenFrame
Class description

Full screen enabled frame window class:

  • bool m_bFullScreen holds the current state;
  • void SetFullScreen( bool bFull) sets the requested state.
  • template <class D> int FSDoModal( D& dlg) restores the taskbar if hidden, calls dlg.DoModal(), hides the taskbar if it was restored, and returns dlg.DoModal() return value.
Class usage
  • In your frame definition
    • For WTL 7.1 (and presently 7.5), define a UpdateLayout member that calls AtlFixUpdateLayout and then the base class UpdateLayout.
    • Add CFullScreenFrame to your main window inheritance list,
    • implement calls to SetFullScreen,
    • check m_bFullScreen if required,
    • call modal dialogs and property sheets through FSDoModal.
// mainfrm.h
//...

class CMainFrame : 
//... 
  public CFullScreenFrame<CMainFrame, false>,
//...
{
//...

// CFrameWindowImplBase::UpdateLayout override
 void UpdateLayout(BOOL bResizeBars = TRUE)
 {

  CRect rectWnd, rectTool;
 
#if _WTL_VER <= 0x0710 || defined(_WTL_NO_PPC_FRAME_POSITION)
 
  AtlFixUpdateLayout( m_hWnd, m_hWndCECommandBar);
//...

 // Message map and handlers
 BEGIN_MSG_MAP(CMainFrame)
//...
  COMMAND_ID_HANDLER(ID_FILE_OPEN, OnFileOpen)
//...
  COMMAND_ID_HANDLER(ID_APP_ABOUT, OnAppAbout)
  COMMAND_ID_HANDLER(ID_FILE_REGISTER, OnRegister)
  COMMAND_ID_HANDLER(ID_VIEW_PROPERTIES, OnProperties)
//...
  COMMAND_ID_HANDLER(ID_VIEW_TOOLBAR, OnFullScreen)
//...

 
// File and image transfers
 LRESULT OnFileOpen(WORD /*wNotifyCode*/, WORD /*wID*/, 
                    HWND /*hWndCtl*/, BOOL& /*bHandled*/)
 {
//...
  CFileDialog dlg( TRUE, NULL, NULL, OFN_HIDEREADONLY | 
                  OFN_OVERWRITEPROMPT, sFiles);
 
 if( FSDoModal( dlg) == IDOK)
   SetImageFile( dlg.m_szFileName);
//...

// Dialogs and property sheet activation
 LRESULT OnAppAbout(WORD /*wNotifyCode*/, WORD /*wID*/, 
                    HWND /*hWndCtl*/, BOOL& /*bHandled*/)
 {
  CStdSimpleDialog<IDD_ABOUTBOX, 
             SHIDIF_DONEBUTTON | SHIDIF_FULLSCREENNOMENUBAR> dlg;
  return FSDoModal( dlg);
 }

 LRESULT OnRegister(WORD /*wNotifyCode*/, WORD /*wID*/, 
                    HWND /*hWndCtl*/, BOOL& /*bHandled*/)
 {
  CRegisterDlg dlg;
  return FSDoModal( dlg);
 }

 LRESULT OnProperties(WORD /*wNotifyCode*/, WORD /*wID*/, 
                      HWND /*hWndCtl*/, BOOL& /*bHandled*/)
 {
  CImageProperties prop( m_sFile, m_view);
  return FSDoModal( prop);
 }
//...

 LRESULT OnFullScreen(WORD /*wNotifyCode*/, WORD /*wID*/, 
                      HWND /*hWndCtl*/, BOOL& /*bHandled*/)
 {
  SetFullScreen( !m_bFullScreen );
  UISetCheck( ID_VIEW_TOOLBAR, m_bFullScreen);
  return TRUE;
 }
//...

DIB support

DIB structure

  • struct DIBINFO16 creates descriptors for sized 16 bits DIB with BI_BITFIELDS.

DIB functions description

  • HBITMAP AtlGetDibBitmap( LPBITMAPINFO pbmi) builds a HBITMAP from a packed DIB.
  • HBITMAP AtlCopyBitmap( HBITMAP hbm , SIZE size, bool bAsBitmap = false) copies a HBITMAP to a size dimensioned packed DIBINFO16 or DDB depending on bAsBitmap.
  • HLOCAL AtlCreatePackedDib16( HBITMAP hbm, SIZE size) creates a packed DIBINFO16 of size size from a given HBITMAP.
  • bool AtlSetClipboardDib16( HBITMAP hbm, SIZE size, HWND hWnd) sets the clipboard CF_DIB format to a sized DIBINFO16 copied from a HBITMAP.
  • HBITMAP AtlGetClipboardDib( HWND hWnd) returns a HBITMAP from the clipboard CF_DIB format.

DIB functions usage

Use this set of functions to support the CF_DIB clipboard format, CF_BITMAP clipboard objects are not inter-process enabled in PPC.

// mainfrm.h
//...

// UpdateUI operations and map
 virtual BOOL OnIdle()
 {
  UIEnable( ID_EDIT_PASTE, IsClipboardFormatAvailable( CF_DIB));

//...
 BEGIN_UPDATE_UI_MAP(CMainFrame)
  UPDATE_ELEMENT(ID_EDIT_COPY, UPDUI_MENUPOPUP | UPDUI_TOOLBAR)
  UPDATE_ELEMENT(ID_EDIT_PASTE, UPDUI_MENUPOPUP | UPDUI_TOOLBAR)
//...
// Message map and handlers
 BEGIN_MSG_MAP(CMainFrame)
//...
  COMMAND_ID_HANDLER(ID_EDIT_PASTE, OnPaste)
//...
  CHAIN_MSG_MAP_ALT_MEMBER(m_view, 1)
//... 
 LRESULT OnPaste(WORD /*wNotifyCode*/, WORD /*wID*/, 
                 HWND /*hWndCtl*/, BOOL& /*bHandled*/)
 {
  if ( CBitmapHandle hbm = AtlGetClipboardDib( m_hWnd))
  {
   m_sFile.Empty();
   m_view.SetImage( hbm, L"pasted");
   UIEnable(ID_ZOOM, true);
   UIEnable(ID_FILE_CLOSE, true);
   UIEnable(ID_EDIT_COPY, true);
   UIEnable(ID_VIEW_PROPERTIES, true);
  }
  else
   AtlMessageBox( m_hWnd, L"Could not paste image from clipboard", 
                 IDR_MAINFRAME, MB_OK | MB_ICONERROR);
  return 0;
 }
//...
// ImageViewview.h
//...
// Message map and handlers

 BEGIN_MSG_MAP(CImageViewView)
//... 
 ALT_MSG_MAP( 1 ) // Forwarded by frame
//...
  COMMAND_ID_HANDLER(ID_EDIT_COPY, OnCopy)
//...
//  Clipboard copy 
 LRESULT OnCopy(WORD /*wNotifyCode*/, WORD /*wID*/, 
                HWND /*hWndCtl*/, BOOL& /*bHandled*/)
 {
  if ( !AtlSetClipboardDib16( m_bmp , m_sizeAll, m_hWnd))
   AtlMessageBox( m_hWnd, L"Could not copy image to clipboard", 
                 IDR_MAINFRAME, MB_OK | MB_ICONWARNING);
  return 0;
 }
//...

AtlCopyBitmap is used in ImageView CImagePage and CViewPage to fill-up CStatic controls bitmaps.

// ImageViewdlg.h
//...


class CImagePage : public CPropertyPageImpl<CImagePage>
{
//...
 CBitmapHandle m_bmp;
//...
 BEGIN_MSG_MAP(CImagePage)
  MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
//...
 LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, 
         LPARAM /*lParam*/, BOOL& /*bHandled*/)
 {
//...
  DIBSECTION ds;
  bool bOK = ::GetObject( m_bmp, sizeof(DIBSECTION), &ds) == sizeof(DIBSECTION);
//...
  CStatic sImg = GetDlgItem( IDC_IMAGE);
  CRect rectImg;
  sImg.GetWindowRect( rectImg);
  CSize sizeImg( ds.dsBmih.biWidth, ds.dsBmih.biHeight);
  double fzoom = max( (double)sizeImg.cx / rectImg.Width(),
      (double)sizeImg.cy / rectImg.Height());
  CBitmapHandle hbm = AtlCopyBitmap( m_bmp, sizeImg / fzoom, true);
  sImg.SetBitmap( hbm);
//...

ImageView features

Continuous zooming facility

CImageViewView derives from CZoomScrollImpl which implements the feature. This class is fully described in CodeProject: Add zooming to WTL CScrollImpl.

Zoom user interface with arrow keys and trackbar in menu bar

This feature requires some steps:

  • Create a menu bar button of ID: ID_ZOOM in the resource editor
  • Declare a CTrackbarCtrl member in CImageViewView.
// ImageViewview.h
//...

class CImageViewView : 
 public CWindowImpl< CImageViewView > ,
 public CZoomScrollImpl< CImageViewView > 
{
public:
 DECLARE_WND_CLASS(NULL)

 CTrackBarCtrl m_ZoomCtrl;
//...
  • Subclass the menubar to forward trackbar messages
  • Create CImageViewView::m_ZoomCtrl in place of the ID_ZOOM button.
// mainframe.h
//... 
// Selective message forwarding macros

#define FORWARD_MSG(msg) if ( uMsg == msg ) \
 { lResult = ::SendMessage( GetParent(), uMsg, 
               wParam, lParam ); return bHandled = TRUE;}
#define FORWARD_NOTIFICATION_ID(uID) 
        if (( uMsg == WM_NOTIFY) && ( wParam == uID)) \
 { lResult = ::SendMessage( GetParent(), uMsg, wParam, lParam ); 
   return bHandled = TRUE;}
 

class CMainFrame :
//... 
{

 
// CZoomMenuBar: MenuBar forwarding trackbar messages
 
 class CZoomMenuBar : public CWindowImpl< CZoomMenuBar, 
                      CCECommandBarCtrlT<CToolBarCtrl> >
 {
 public:
  DECLARE_WND_SUPERCLASS( L"ZoomMenuBar", L"ToolbarWindow32");

  BEGIN_MSG_MAP(CZoomMenuBar)
   FORWARD_MSG(WM_HSCROLL)
   FORWARD_MSG(WM_CTLCOLORSTATIC)
   FORWARD_NOTIFICATION_ID(ID_ZOOM)
  END_MSG_MAP()
 };

// Data and declarations
public:
//...
 CZoomMenuBar m_MenuBar;
//...
// Creation and destruction
 LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, 
                  LPARAM lParam, BOOL& /*bHandled*/)
 {
//...
  // MenuBar creation
  CreateSimpleCEMenuBar( IDR_MAINFRAME, SHCMBF_HIDESIPBUTTON);
  m_MenuBar.SubclassWindow( m_hWndCECommandBar);
  m_MenuBar.LoadStdImages( IDB_STD_SMALL_COLOR);
  UIAddToolBar( m_hWndCECommandBar);
  
  // Trackbar creation
  CRect rZoom;
  m_MenuBar.GetRect( ID_ZOOM, rZoom);
  rZoom.top -= 1;
  m_view.m_ZoomCtrl.Create( m_hWndCECommandBar, rZoom, 
                   NULL ,WS_CHILD | TBS_TOP | TBS_FIXEDLENGTH, 0, 
   ID_ZOOM );
  m_view.m_ZoomCtrl.SetThumbLength( 9); 
  rZoom.DeflateRect( 1, 1);
  m_view.m_ZoomCtrl.SetWindowPos( HWND_TOP, rZoom.left, rZoom.top + 1, 
   rZoom.Width(), rZoom.Height(), SWP_SHOWWINDOW); 
  UIAddChildWindowContainer( m_hWndCECommandBar);
//...
  • Set the trackbar range for the current image.
// ImageViewview.h
//...

// Image operation
 void SetImage( HBITMAP hBitmap, LPCTSTR sname = NULL, 
                double fZoom = 1.0, POINT pScroll = CPoint( -1,-1))
 {
  CSize sizeImage( 1, 1);
  m_bmp.Attach( hBitmap ); // will delete existing if necessary

  if( m_bmp.IsNull())
//...
  else
  {
//...
   m_bmp.GetSize( sizeImage);
//...
  }
//...
  // Set trackbar range and position
  sizeImage *= 100;
  CRect rect;
  SystemParametersInfo( SPI_GETWORKAREA, NULL, rect, FALSE);
  m_ZoomCtrl.SetRange( 100, max( sizeImage.cx / rect.Size().cx , 
                      sizeImage.cy / rect.Size().cy));
  m_ZoomCtrl.SetPageSize(100);
  m_ZoomCtrl.SetPos( (int)(100 * fZoom ));
 }
  • Manage the trackbar state
    • Enable/Disable
// mainframe.h
//... 

class CMainFrame :
//... 
{
//...


// Data and declarations
public:
//...
 CZoomMenuBar m_MenuBar;
//...
// File and image operations
//...

 bool SetImageFile( LPCTSTR szFileName, double fZoom = 1.0 , 
                    POINT ptScroll= CPoint( -1, -1))
 {
  CBitmapHandle hBmp;
  if ( szFileName && *szFileName)
   hBmp = LoadImageFile( szFileName);
  bool bOK = !hBmp.IsNull();
//...
  UIEnable(ID_ZOOM, bOK);
//...
 } 

// UpdateUI operations and map
 virtual BOOL OnIdle()
 {
//...
  UIUpdateChildWindows();
//...
  }
  BEGIN_UPDATE_UI_MAP(CMainFrame)
//...
  UPDATE_ELEMENT(ID_ZOOM, UPDUI_CHILDWINDOW)
//...
// Creation and destruction
 LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, 
                  LPARAM lParam, BOOL& /*bHandled*/)
 {
//...
  
  // Trackbar creation
//...
  m_view.m_ZoomCtrl.Create( m_hWndCECommandBar, rZoom, 
                   NULL ,WS_CHILD | TBS_TOP | TBS_FIXEDLENGTH, 0, 
   ID_ZOOM );
//...
  UIAddChildWindowContainer( m_hWndCECommandBar);
//...
    • Frame
    • Background
// ImageViewview.h
//...


// Image operation
 void SetImage( HBITMAP hBitmap, LPCTSTR sname = NULL, 
                double fZoom = 1.0, POINT pScroll = CPoint( -1,-1))
 {
  CSize sizeImage( 1, 1);
  m_bmp.Attach( hBitmap ); // will delete existing if necessary

  if( m_bmp.IsNull())
  {
   m_sImageName.Empty();
   m_ZoomCtrl.ModifyStyle( WS_BORDER, NULL, SWP_DRAWFRAME);
   m_TitleBar.SetText( 0, L"No image");
  }
  else
  {
   m_sImageName = sname;
   m_bmp.GetSize( sizeImage);
   m_ZoomCtrl.ModifyStyle( NULL, WS_BORDER, SWP_DRAWFRAME);
   m_TitleBar.SetText( 0, NULL, SBT_OWNERDRAW);
  }
//...
// Message map and handlers
 BEGIN_MSG_MAP(CImageViewView)
//...
 ALT_MSG_MAP( 1 ) // Forwarded by frame
  MESSAGE_HANDLER(WM_CTLCOLORSTATIC, OnZoomColor)
//...
//  Zoom trackbar interaction
 LRESULT OnZoomColor(UINT /*uMsg*/, WPARAM /*wParam*/, 
                     LPARAM /*lParam*/, BOOL& /*bHandled*/)
 {
  return (LRESULT)::GetSysColorBrush( m_bmp.IsNull() ? 
                   COLOR_BTNFACE : COLOR_BTNHIGHLIGHT );
 }
  • Forward the keyboard messages to the trackbar
  • Set zoom on trackbar move
// ImageViewview.h
//...

// Message map and handlers
 BEGIN_MSG_MAP(CImageViewView)
 MESSAGE_RANGE_HANDLER( WM_KEYFIRST, WM_KEYLAST, OnKey)

//...
 ALT_MSG_MAP( 1 ) // Forwarded by frame
  MESSAGE_HANDLER(WM_HSCROLL, OnZoom)
//...
// Key translation and forwarding
 LRESULT OnKey(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
 {
  switch ( wParam )
  {
   case VK_UP : 
    wParam = VK_PRIOR;
    break;
   case VK_DOWN :
    wParam = VK_NEXT;
    break;
  }
  return m_ZoomCtrl.SendMessage( uMsg, wParam, lParam);
 }
//...
 
 LRESULT OnZoom(UINT /*uMsg*/, WPARAM wParam, 
                LPARAM /*lParam*/, BOOL& /*bHandled*/)
 {
  ATLASSERT( !m_bmp.IsNull());
  double fzoom;
 
  switch LOWORD(wParam)
  {
  case SB_THUMBTRACK :
  case SB_THUMBPOSITION :
   fzoom = (short int)HIWORD(wParam) / 100.0; 
   break;
  default : 
   fzoom = m_ZoomCtrl.GetPos() / 100.0;
  }
  if ( fzoom != m_fzoom)
  {
   SetZoom( fzoom);
   m_TitleBar.SetText( 0, NULL, SBT_OWNERDRAW);
  }
  return TRUE;
 }
//...

Well behaved system interaction

CMainFrame derives from CAppWindow which implements the feature. See CAppWindow class usage.

Image property sheet

CMainFrame derives from CStdPropertySheet (see class usage) and is activated through FSDoModal.

// mainfrm.h
//...

 LRESULT OnProperties(WORD /*wNotifyCode*/, WORD /*wID*/, 
                      HWND /*hWndCtl*/, BOOL& /*bHandled*/)
  {
   CImageProperties prop( m_sFile, m_view);
   return FSDoModal( prop);
  }
//...

The three property pages are supplied at creation with relevant data.

// ImageViewdlg.h
//...

/////////////////////
// CFilePage: Current image file propertes 

class CFilePage : public CPropertyPageImpl<CFilePage>
{
public:
 enum { IDD = IDD_PROP_FILE };

 CString m_sPath;

 CFilePage( LPCTSTR sPath) : m_sPath( sPath) { }
//...

/////////////////////
// CImagePage: Current image properties 

class CImagePage : public CPropertyPageImpl<CImagePage>
{
public:
 enum { IDD = IDD_PROP_IMAGE };
 CBitmapHandle m_bmp;

 CImagePage(HBITMAP hbmp) : m_bmp(hbmp) {}
//...

/////////////////////
// CViewPage: Current view properties 

class CViewPage : public CPropertyPageImpl<CViewPage>
{
 public:

 CImageViewView& m_rview;

 CViewPage( CImageViewView& rview) : m_rview( rview){}
//...

CImagePage and CViewPage use AtlCopyBitmap to display a small copy of the image.

Clipboard copy and paste

ImageView makes direct use of the DIB support functions.

Optional title bar

CImageViewview::m_TitleBar is a CStatusBarCtrl with CCS_TOP style, which CFrameWindowImplBase::UpdateLayout presently ignores. This is addressed in CMainFrame::UpdateLayout.

Title bar visibility is saved in CMainFrame::AppSave and restored at creation.

//
//... 

class CImageViewView : 
 public CWindowImpl< CImageViewView > ,
 public CZoomScrollImpl< CImageViewView > 
{
public:
 DECLARE_WND_CLASS(NULL)

 CTrackBarCtrl m_ZoomCtrl;
 CStatusBarCtrl m_TitleBar;
//...
// mainfrm.h
//...

 
class CMainFrame : 
  public CFrameWindowImpl<CMainFrame,CWindow,CCeFrameTraits>, 
  public CUpdateUI<CMainFrame>,
  public CMessageFilter, public CIdleHandler, 
  public CFullScreenFrame<CMainFrame, false>,
  public CAppWindow<CMainFrame>
{
//... 

//#if _WTL_VER <= 0x0710 
// Uncomment when WTL supports CCS_TOP status bars ( see #1039656).


// CFrameWindowImplBase::UpdateLayout override
 void UpdateLayout(BOOL bResizeBars = TRUE)
 {
  CRect rectWnd, rectTool;
#if _WTL_VER <= 0x0710 || defined(_WTL_NO_PPC_FRAME_POSITION)
  AtlFixUpdateLayout( m_hWnd, m_hWndCECommandBar);
#else 
// No support of CCS_TOP status bar(#1039656)
// && AtlFixUpdateLayout not compiled
 
  // resize frame 
  ATLASSERT( m_MenuBar.IsWindow());
  GetWindowRect( rectWnd);
  m_MenuBar.GetWindowRect( rectTool); 
  int bottom = m_MenuBar.IsVisible() ? rectTool.top : rectTool.bottom;
  if ( bottom != rectWnd.bottom)
  { 
   rectWnd.bottom = bottom;
   MoveWindow( rectWnd, FALSE);
  }
#endif
  // resize title bar
  ATLASSERT( m_view.m_TitleBar.IsWindow());
  GetClientRect( rectWnd);
  if( m_view.m_TitleBar.GetStyle() & WS_VISIBLE)
  {
   if(bResizeBars)
    m_view.m_TitleBar.SendMessage( WM_SIZE);
   m_view.m_TitleBar.GetWindowRect( rectTool);
   rectWnd.top += rectTool.Size().cy;
  }
  // resize view
  ATLASSERT( m_view.IsWindow());
  m_view.GetWindowRect( rectTool);
  if ( rectTool != rectWnd)
   m_view.SetWindowPos( NULL, rectWnd, SWP_NOZORDER | SWP_NOACTIVATE);
 }
//#endif // _WTL_VER <= 0x0710
         // Uncomment when WTL supports
         // CCS_TOP status bars ( see #1039656).

 
// CAppWindow operations
 
 void AppSave()
 {
  CAppInfo info;
  BOOL bTitle = m_view.m_TitleBar.IsWindowVisible();
  info.Save( bTitle, L"TitleBar");
//...
 
 // Creation and destruction 
 LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, 
                  LPARAM lParam, BOOL& /*bHandled*/)
 {
//...
   // TitleBar creation
  BOOL bTitle = TRUE;
  info.Restore( bTitle, L"TitleBar");
  DWORD dwStyle = WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | CCS_TOP;
  if ( bTitle)
   dwStyle |= WS_VISIBLE;
  CreateSimpleStatusBar( L"", dwStyle);
   m_view.m_TitleBar.Attach( m_hWndStatusBar);
  UISetCheck( ID_VIEW_STATUS_BAR, bTitle);
//...

Setting SBT_OWNERDRAW flagged text when zoom or image name has changed triggers CImageViewview::OnDrawTitle.

// ImageViewview.h
//...


// Image operation
 

 
void SetImage( HBITMAP hBitmap, LPCTSTR sname = NULL, 
               double fZoom = 1.0, POINT pScroll = CPoint( -1,-1))
 {
  CSize sizeImage( 1, 1);
  m_bmp.Attach( hBitmap ); // will delete existing if necessary

  if( m_bmp.IsNull())
//...
   m_TitleBar.SetText( 0, L"No image");
  else
//...
   m_TitleBar.SetText( 0, NULL, SBT_OWNERDRAW);

//...

 

// Message map and handlers
 BEGIN_MSG_MAP(CImageViewView)
//...

 ALT_MSG_MAP( 1 ) // Forwarded by frame
//...
  MESSAGE_HANDLER(WM_HSCROLL, OnZoom)
  MESSAGE_HANDLER(WM_DRAWITEM, OnDrawTitle)
//...
 

////////////////////////////////////////////////////////////////
// ALT_MSG_MAP( 1 )  Handlers for forwarded messages
 
//  Title bar drawing
 LRESULT OnDrawTitle(UINT /*uMsg*/, WPARAM /*wParam*/, 
                     LPARAM lParam, BOOL& /*bHandled*/)
 {
  CDCHandle dc = ((LPDRAWITEMSTRUCT)lParam)->hDC;
  CRect rectTitle = ((LPDRAWITEMSTRUCT)lParam)->rcItem;
  dc.FillRect( rectTitle, AtlGetStockBrush( WHITE_BRUSH));
  rectTitle.DeflateRect( 2, 0);
  dc.SetTextColor( RGB( 0, 0, 156));
  CString sTitle = _T("Image: ") + m_sImageName;
  dc.DrawText( sTitle, -1, rectTitle, DT_LEFT | DT_SINGLELINE);
  sTitle.Format( _T("Zoom: %.2f"), GetZoom());
  dc.DrawText( sTitle, -1, rectTitle, DT_RIGHT | DT_SINGLELINE);
  return TRUE;
 }

 
//  Zoom trackbar interaction
//...
 LRESULT OnZoom(UINT /*uMsg*/, WPARAM wParam, 
                LPARAM /*lParam*/, BOOL& /*bHandled*/)
 {
  ATLASSERT( !m_bmp.IsNull());
  double fzoom;
//...

  if ( fzoom != m_fzoom)
  {
   SetZoom( fzoom);
   m_TitleBar.SetText( 0, NULL, SBT_OWNERDRAW);
  }

Full screen view

CMainFrame derives from CFullScreenFrame which implements the feature. See CFullScreenFrame class usage.

Tap-and-scroll image moving

The last stylus position is kept in CImageViewView::m_ptMouse and the view offset is changed on WM_MOUSEMOVE message.

// ImageViewview.h
//...

 
class CImageViewView : 
 public CWindowImpl< CImageViewView > ,
 public CZoomScrollImpl< CImageViewView > 
{
public:
//...

 

 CPoint m_ptMouse;
//... 

 

// Message map and handlers
 BEGIN_MSG_MAP(CImageViewView)
  MESSAGE_HANDLER(WM_SIZE, OnSize)
  MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
  MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
//...
 

// Stylus interaction: tap-and-scroll and context menu
 LRESULT OnLButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, 
                       LPARAM lParam, BOOL& /*bHandled*/)
 {
  m_ptMouse = CPoint( GET_X_LPARAM( lParam), GET_Y_LPARAM( lParam));
//...
 }

 
 LRESULT OnMouseMove(UINT /*uMsg*/, WPARAM wParam, 
                     LPARAM lParam, BOOL& /*bHandled*/)
 {
  if ( 
#ifdef _X86_
    (wParam & MK_LBUTTON) && 
#endif // _X86_
    !m_bmp.IsNull())
  {
   CPoint ptNew( GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
   SetScrollOffset( GetScrollOffset() + 
                   (( m_ptMouse - ptNew) * GetZoom()));
   m_ptMouse = ptNew;
  }
  return 0;
 }
//...

Optional scroll bars

As Windows CE does not support the handy ::ShowScrollBar(), implementing this feature requires some work:

  • Declare CImageViewView::m_bShowScroll to hold the scrollbar visibility.
  • Implement CImageViewView::ShowScrollBars function and CImageViewView::OnShowScrollBars command handler.
  • Override CZoomScrollImpl::SetScrollOffset and CZoomScrollImpl::SetZoom which are called by CImageViewView.
  • Override CZoomScrollImpl::OnSize handler.
// ImageViewview.h
//...

 
class CImageViewView : 
 public CWindowImpl< CImageViewView > ,
 public CZoomScrollImpl< CImageViewView > 
{
public:
 
 bool m_bShowScroll;

 CImageViewView() : m_bShowScroll( true) {}
 
//...
 

// Show/hide scrollbars operation
 void ShowScrollBars( bool bShow)
 {
  m_bShowScroll = bShow;

  if (bShow)
  {
   SCROLLINFO si = { sizeof(si), SIF_PAGE | SIF_RANGE | SIF_POS};

   si.nMax = m_sizeAll.cx - 1;
   si.nPage = m_sizeClient.cx;
   si.nPos = m_ptOffset.x;
   SetScrollInfo(SB_HORZ, &si);

   si.nMax = m_sizeAll.cy - 1;
   si.nPage = m_sizeClient.cy;
   si.nPos = m_ptOffset.y;
   SetScrollInfo(SB_VERT, &si);
  }
  else
  {
   SCROLLINFO si = { sizeof(si), SIF_RANGE, 0, 0};

   SetScrollInfo(SB_HORZ, &si);
   SetScrollInfo(SB_VERT, &si);
  }
  Invalidate();
 }

// CZoomScrollImpl::SetScrollOffset  override for hidden scrollbars
 void SetScrollOffset( POINT ptOffset, BOOL bRedraw = TRUE )
 {
  if ( m_bShowScroll)
   CZoomScrollImpl<CImageViewView>::SetScrollOffset( ptOffset, bRedraw);
  else
  {
   AdjustOffset( CSize( ptOffset) / m_fzoom);
   if ( bRedraw)
    Invalidate();
  }
 }

// CZoomScrollImpl::SetZoom  override for hidden scrollbars
 void SetZoom( double fzoom, BOOL bRedraw = TRUE )
 {
  if ( m_bShowScroll)
   CZoomScrollImpl<CImageViewView>::SetZoom( fzoom, bRedraw);
  else
  {
   CPoint ptCenter = WndtoTrue( m_sizeClient / 2 );
   m_sizeAll = m_sizeTrue / fzoom;
   m_fzoom = fzoom;
   CPoint ptOffset= TruetoWnd(ptCenter) + m_ptOffset - m_sizeClient/ 2;
   AdjustOffset( ptOffset);
   if ( bRedraw)
    Invalidate();
  }
 }
 
//Implementation: hidden scrollbars helper
 void AdjustOffset( CPoint ptNew, bool bScroll = false)
 {
  CSize sizeMax = CSize( m_sizeAll) - m_sizeClient;
  int x = max ( min( ptNew.x, sizeMax.cx), 0 );
  int y = max ( min( ptNew.y, sizeMax.cy), 0 );

  CPoint ptOffset( x, y);

  if ( ptOffset != m_ptOffset)
  {
   if ( bScroll)
    ScrollWindowEx( m_ptOffset.x - x, m_ptOffset.y - y, m_uScrollFlags);
   m_ptOffset = ptOffset;
  }
 }

// Message map and handlers
 BEGIN_MSG_MAP(CImageViewView)
  MESSAGE_HANDLER(WM_SIZE, OnSize)

 ALT_MSG_MAP( 1 ) // Forwarded by frame
//...

  COMMAND_ID_HANDLER(ID_VIEW_SCROLLBARS, OnShowScrollBars)
//...
 

// CZoomScrollImpl::OnSize  override for hidden scrollbars
 LRESULT OnSize(UINT /*uMsg*/, WPARAM /*wParam*/, 
                LPARAM lParam, BOOL& bHandled)
 {
  if ( m_bShowScroll)
   bHandled = FALSE;
  else
  {
   m_sizeClient = CSize( GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
   AdjustOffset( m_ptOffset, true);
  }
  return bHandled;
 }
 
//  Scroll bars show-hide
 LRESULT OnShowScrollBars(WORD /*wNotifyCode*/, WORD /*wID*/, 
                         HWND /*hWndCtl*/, BOOL& /*bHandled*/)
 {
  ShowScrollBars( !m_bShowScroll);
  return TRUE;
 }
  • Implement the scrollbar visibility persistence
  • Update menu item check
// mainfrm.h
//...

 
class CMainFrame :
///... 
{
//... 


// CAppWindow operations
 void AppSave()
 {
  CAppInfo info;
//...
  info.Save( m_view.m_bShowScroll, L"ScrollBars");
//...

// UpdateUI operations and map
//...
 
 BEGIN_UPDATE_UI_MAP(CMainFrame)
//...

  UPDATE_ELEMENT(ID_VIEW_SCROLLBARS, UPDUI_MENUPOPUP)
//...
 
// Message map and handlers
 BEGIN_MSG_MAP(CMainFrame)

//...

  COMMAND_ID_HANDLER(ID_VIEW_SCROLLBARS, OnScrollBars)
//...
 
// Creation and destruction
 LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, 
                 LPARAM lParam, BOOL& /*bHandled*/)
 {
  CAppInfo info;
//...

  // View creation
 
  info.Restore( m_view.m_bShowScroll, L"ScrollBars");
  m_hWndClient = m_view.Create(m_hWnd, rcDefault, NULL, 
                WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN);
  UISetCheck( ID_VIEW_SCROLLBARS, m_view.m_bShowScroll);
//...
 
// User interface settings
//...

 
 LRESULT OnScrollBars(WORD /*wNotifyCode*/, WORD /*wID*/, 
                     HWND /*hWndCtl*/, BOOL& bHandled)
 {
  UISetCheck( ID_VIEW_SCROLLBARS, !m_view.m_bShowScroll);
  return bHandled = FALSE; // real processing is in m_view
 }
//...

System registration as default program for image files

This feature is handled by CRegisterDlg.

Image files are associated to a default program through the HKEY_CLASSES_ROOT\xxxxxx\Shell\Open\Command registry key default string value, where xxxxx depends on the file type. When a user taps a file name, the shell executes the command found there. However, only \Windows located programs are executed.

CRegisterDlg::InitDialog checks the current ImageView location and calls CMoveDlg::DoModal if the location is not \Windows.

// ImageViewdlg.h
//...


/////////////////////
// CRegisterDlg: Register ImageView.exe
// as standard program for image files 

class CRegisterDlg : public CStdDialogImpl<CRegisterDlg>
{
public:

 enum { IDD = IDD_REGISTER };
 CString m_sAppPath;
 CString m_sApp;
//...
 
 BEGIN_MSG_MAP(CRegisterDlg)
  MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
//...
 
// Dialog initialization
 

 LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, 
                      LPARAM /*lParam*/, BOOL& bHandled)
 {
//...
 

  ::GetModuleFileName( NULL, m_sAppPath.GetBuffer( MAX_PATH + 1), MAX_PATH);
  m_sAppPath.ReleaseBuffer();
  m_sApp = m_sAppPath.Mid( m_sAppPath.ReverseFind(L'\\') + 1);
// Call CMoveDlg if ImageView.exe is not located in \Windows folder
 

  if( CString(L"\\Windows\\") + m_sApp != m_sAppPath)
  {
   CMoveDlg dlg;
   if ( dlg.DoModal() == IDCANCEL)
    EndDialog( IDCANCEL);

//...

CMoveDlg moves ImageView.exe if allowed by user, and creates, if requested, a shortcut at the old program location.

// ImageViewdlg.h
//...

/////////////////////
// CMoveDlg : Called by CRegisterDlg to move ImageView.exe to \Windows folder



class CMoveDlg : public  CStdDialogImpl<CMoveDlg>
{
public:
 CString m_sAppPath;
 CString m_sApp;

 enum { IDD = IDD_MOVE };

 BEGIN_MSG_MAP(CMoveDlg)
  MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
  COMMAND_HANDLER(IDC_MOVE, BN_CLICKED, OnMove)
  CHAIN_MSG_MAP(CStdDialogImpl<CMoveDlg>)
 END_MSG_MAP()
  
// Dialog initialization
 LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, 
                      LPARAM /*lParam*/, BOOL& bHandled)
 {
  SHINITDLGINFO shidi = { SHIDIM_FLAGS, m_hWnd, SHIDIF_FULLSCREENNOMENUBAR};
  SHInitDialog( &shidi);
  SHDoneButton( m_hWnd, SHDB_HIDE);

  GetModuleFileName( NULL, m_sAppPath.GetBuffer(MAX_PATH+1), MAX_PATH);
  m_sAppPath.ReleaseBuffer();

  SetDlgItemText( IDC_FILELOCATION, m_sAppPath);
  m_sApp = m_sAppPath.Mid( m_sAppPath.ReverseFind(L'\\') + 1);
  
  CheckDlgButton( IDC_SHORTCUT, TRUE);

  return bHandled=FALSE;
 // to prevent CDialogImplBaseT< TBase >::DialogProc settings
 }

// Move operation
 LRESULT OnMove(WORD /*wNotifyCode*/, WORD /*wID*/, 
                HWND /*hWndCtl*/, BOOL& /*bHandled*/)
 {
  CString sDest = L"\\Windows\\" + m_sApp;
  if ( ::MoveFile( m_sAppPath, sDest))
  {
   if ( IsDlgButtonChecked( IDC_SHORTCUT))
   {
    m_sAppPath.Replace( L".exe", L".lnk");
    if ( !::SHCreateShortcut( (LPTSTR)(LPCTSTR)m_sAppPath, 
                            (LPTSTR)(LPCTSTR)sDest))
     AtlMessageBox( m_hWnd, L"Cannot create shortcut to ImageView.", 
      IDR_MAINFRAME, MB_OK | MB_ICONWARNING);
   }
   EndDialog(IDOK);
  }
  else
   AtlMessageBox( m_hWnd, L"Cannot move ImageView.exe to \\Windows\\ folder.", 
    IDR_MAINFRAME, MB_OK | MB_ICONERROR);
  return 0;
 }
};

On IDOK return, or if ImageView is correctly located, CRegisterDlg::InitDialog initializes the registration status of the image file types. A helper class CImageTypeKey : public CRegKey provides the registry access.

// ImageViewdlg.h
//...

class CRegisterDlg : public CStdDialogImpl<CRegisterDlg>
{
public:
//...
 


// Image type enumeration
 
 enum ImageType { BMP = IDC_BMP, JPG, PNG, GIF } ;
 

// Implementation
// Helper class: image command registry key
 class CImageTypeKey : public CRegKey
 {
 public:
  CString m_sCmd;
  DWORD size;

  CImageTypeKey( ImageType typ) : size( MAX_PATH)
  {
   CString sKey = GetTypeString( typ);
   sKey += L"\\Shell\\Open\\Command";
   Open( HKEY_CLASSES_ROOT, sKey);
  }

  LPCTSTR GetTypeString( ImageType typ)
  {
   switch ( typ)
   {
   case BMP : return L"bmpimage"; 
   case JPG : return L"jpegimage"; 
   case PNG : return L"pngimage"; 
   case GIF : return L"gifimage"; 
   default : ATLASSERT( FALSE); return NULL;
   }
  }

  LPCTSTR GetCmd()
  {
   QueryValue( m_sCmd.GetBuffer( size), L"", &size);
   m_sCmd.ReleaseBuffer();
   return m_sCmd;
  }

  void SetCmd(LPCTSTR sCmd)
  {
   SetValue( sCmd, L"");
  }
 };

// Image type file extension
  LPCTSTR GetExtString( ImageType typ)
 {
  switch ( typ)
  {
  case BMP : return L".bmp"; ;
  case JPG : return L".jpg"; 
  case PNG : return L".png"; 
  case GIF : return L".gif"; 
  default : ATLASSERT( FALSE); return NULL;
  }
 }

// Image type registration status
 bool IsRegistered( ImageType typ)
 {
  CImageTypeKey key( typ);
  CString sCmd = key.GetCmd();
  return sCmd.Find( m_sApp) != -1 ;  
 }
//... 
 
 BEGIN_MSG_MAP(CRegisterDlg)
  MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
//...
 
// Dialog initialization
 LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, 
                      LPARAM /*lParam*/, BOOL& bHandled)
 {
//...
 
// Call CMoveDlg if ImageView.exe is not located in \Windows folder
  if( CString(L"\\Windows\\") + m_sApp != m_sAppPath)
  {
   CMoveDlg dlg;
   if ( dlg.DoModal() == IDCANCEL)
    EndDialog( IDCANCEL);
 
   ::GetModuleFileName( NULL, m_sAppPath.GetBuffer( MAX_PATH + 1), MAX_PATH);
   m_sAppPath.ReleaseBuffer();
  }
 
 // Controls initialization: IDC_BMP, IDC_JPG etc... MUST be in sequence.
  for( int iBtn = IDC_BMP, iIcon = IDC_IBMP ; 
      iBtn <= IDC_GIF ; iBtn++, iIcon++)
  {
   SHFILEINFO sfi;
   ::SHGetFileInfo( GetExtString( (ImageType)iBtn), 
                   FILE_ATTRIBUTE_NORMAL, &sfi, sizeof(sfi), 
    SHGFI_USEFILEATTRIBUTES | SHGFI_ICON | SHGFI_TYPENAME );
   SendDlgItemMessage( iIcon, STM_SETIMAGE, IMAGE_ICON, (LPARAM)sfi.hIcon);
   SetDlgItemText( iBtn, sfi.szTypeName);
   CheckDlgButton( iBtn, IsRegistered( (ImageType)iBtn));
  }
 
 return bHandled = FALSE;
 // to prevent CDialogImplBaseT< TBase >::DialogProc settings
 }

CRegisterDlg::Register uses CAppInfoT<CMainFrame> to save the existing key on registration and restore it on deregistration.

// ImageViewdlg.h
//...



// Image type registration-deregistration
 void Register( ImageType typ, BOOL bRegister)
 {
  CImageTypeKey key( typ);
  CString sOldCmd = key.GetCmd();
  CString sNewCmd = m_sAppPath;
  CAppInfoT<CMainFrame> info;

  if ( bRegister)
   sNewCmd += L" %1";
  else
   info.Restore( sNewCmd, key.GetTypeString( typ));
  
  key.SetCmd( sNewCmd);
  
  if ( bRegister)
   info.Save( sOldCmd, key.GetTypeString( typ));
  else 
   info.Delete( key.GetTypeString( typ));
 }
 
 BEGIN_MSG_MAP(CRegisterDlg)
  MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
  COMMAND_RANGE_HANDLER(IDC_BMP, IDC_GIF, OnCheckType)
  CHAIN_MSG_MAP(CStdDialogImpl<CRegisterDlg>)
 END_MSG_MAP()

//...
 


// Operation
 LRESULT OnCheckType(WORD /*wNotifyCode*/, WORD wID, 
                     HWND /*hWndCtl*/, BOOL& /*bHandled*/)
 {
  Register( (ImageType)wID, IsDlgButtonChecked( wID));
  return 0;
 }

Conclusion

With WTL 7.1 or 7.5 and the limited set of PPC specific classes and functions in atlppc.h, you (and I) can easily write nice real life Pocket PC applications.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here