A Pocket PC WTL image viewer






4.80/5 (17 votes)
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)
orDestroyWindow()
depending on the value oft_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
intoCMainFrame::AppRun
and - delete the
Run
function.
- define
// 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
andAppSave
members, - process the command line parameters, and/or restore the application status and data in the
OnCreate
handler.
- Add
// 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 generatedCMainFrame::OnCreate
, callAtlFixUpdateLayout
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 aUpdateLayout
member that callsAtlFixUpdateLayout
and then the base classUpdateLayout
.// ... 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, callsdlg.DoModal()
, hides the taskbar if it was restored, and returnsdlg.DoModal()
return value.
Class usage
- In your frame definition
- For WTL 7.1 (and presently 7.5), define a
UpdateLayout
member that callsAtlFixUpdateLayout
and then the base classUpdateLayout
. - 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
.
- For WTL 7.1 (and presently 7.5), define a
// 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 bitsDIB
withBI_BITFIELDS
.
DIB functions description
HBITMAP AtlGetDibBitmap( LPBITMAPINFO pbmi)
builds aHBITMAP
from a packedDIB
.HBITMAP AtlCopyBitmap( HBITMAP hbm , SIZE size, bool bAsBitmap = false)
copies aHBITMAP
to asize
dimensioned packedDIBINFO16
orDDB
depending onbAsBitmap
.HLOCAL AtlCreatePackedDib16( HBITMAP hbm, SIZE size)
creates a packedDIBINFO16
of sizesize
from a givenHBITMAP
.bool AtlSetClipboardDib16( HBITMAP hbm, SIZE size, HWND hWnd)
sets the clipboardCF_DIB
format to a sizedDIBINFO16
copied from aHBITMAP
.HBITMAP AtlGetClipboardDib( HWND hWnd)
returns aHBITMAP
from the clipboardCF_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 inCImageViewView
.
// 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 theID_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 andCImageViewView::OnShowScrollBars
command handler. - Override
CZoomScrollImpl::SetScrollOffset
andCZoomScrollImpl::SetZoom
which are called byCImageViewView
. - 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.