Pocket PC WTL 图片查看器






4.80/5 (17投票s)
正在使用的一些 WTL 类和函数:CAppWindow、CFullScreenFrame、CStdDialog、CF_DIB 剪贴板支持等等……
| 
 
 | 
引言
ImageView 是一个用 WTL 编写的 PPC 应用程序。
从 WTL 7.1 开始,WTL 正式支持 PPC 2002 和 2003。这种支持在即将发布的 OpenSource WTL 7.5 版本中继续。然而,PPC 系统要求应用程序具有某些行为,这些行为不应包含在通用 WTL 类中。
ImageView 使用了 atlppc.h,这是一组有限的 PPC WTL 类和函数,已提交包含在 WTL 7.5 中。ImageView 使用了 CodeProject 中描述的类 CZoomScrollImpl:将缩放添加到 WTL CScrollImpl。
ImageViewPPC 项目、atlppc.h 和 zoomscrl.h 使用 WTL 7.1 和当前的 7.5 分发版本,通过 EVC 3.0 到 4.2 和 PPC 2002/2003 SDK 进行编译。
背景
WTL 带有一个非常丰富的示例,BmpView,它在桌面和 Pocket PC 设备之间具有代码兼容性。通过 BmpZoom,我将其功能扩展到连续缩放。
João Paulo Figueira 提供了 PicView,一个适用于 PPC 2002 的优秀 MFC 图片查看器。
ImageView 受这两个模型的启发,旨在成为(如果可能的话)一个更好的图像查看器。
| 功能 | ImageView | BmpZoom | PicView | 
| 缩放 | 连续 | 连续 | 整数步长 | 
| 缩放 UI | 菜单中的跟踪条,按键 | 菜单中的跟踪条 | 菜单按钮(放大 - 缩小) | 
| 表现良好 | 是 | 否 | MFC 行为 | 
| 图像属性页 | 是 | 是 | 否 | 
| 剪贴板复制和粘贴 | 是 | 未完成 | 否 | 
| 标题栏 | 是 | 否 | 是 | 
| 全屏视图 | 是 | 否 | 是 | 
| 点按和滚动 | 是 | 否 | 是 | 
| 显示/隐藏滚动条 | 是 | 否 | 否 | 
| 系统注册 | 是 | 否 | 否 | 
PPC WTL 类和函数
标准属性页和对话框
CStdPropertySheet
template <class T, bool t_bShowSip = true> class CStdPropertySheet : public CPropertySheetImpl<T>
类描述
标准的 PPC 属性页,带有标题、空菜单栏和 SIP,具体取决于 t_bShowSip。
类用法
从 CStdPropertySheet 派生一个属性页类,如 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
类描述
标准 PPC 对话框的基类,具有以下特性:
- PPC 对话框标题显示和对话框标题准备例程//... // 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() // ...
- 关闭命令处理程序,根据 t_bModal的值调用EndDialog( wID)或DestroyWindow()。//... // Standard dialog ending: may be used with any command LRESULT OnCloseCmd(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/) //...
- ID 为 IDC_INFOSTATIC // == IDC_STATIC -1的静态控件的背景COLOR_INFOBK设置。//... // IDC_INFOSTATIC background setting LRESULT OnColorStatic(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled) //...
类用法
从 CDialogImpl 和 CStdDialog 派生一个对话框类,并通过对话框类消息映射连接所需的功能。CStdDialogImpl 为所有功能完成了此操作。
///////////////////////////////////////////////////////////////////////////////
// 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>
类描述
用于派生的标准 PPC 对话框实现。
类用法
从 CStdDialogImpl 派生一个对话框类,并像 ImageView 的 CMoveDlg 和 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> >
类描述
标准 CSimpleDialog(仅限模态)具有初始设置 t_shidiFlags,默认值为 CSimpleDialog 设置。
类用法
在 ImageView OnAppAbout 处理程序中实例化。
// mainfrm.h
//...
LRESULT OnAppAbout(WORD /*wNotifyCode*/, WORD /*wID*/, 
                   HWND /*hWndCtl*/, BOOL& /*bHandled*/)
 {
  CStdSimpleDialog<IDD_ABOUTBOX, 
               SHIDIF_DONEBUTTON | SHIDIF_FULLSCREENNOMENUBAR> dlg;
  return FSDoModal( dlg);
 }
//...

应用程序行为
CAppInfoBase
class CAppInfoBase
类描述
用于应用程序状态保存/恢复到注册表的帮助程序。在 HKEY_CURRENT_USER 下打开或创建一个 sAppKey 注册表项。Save 和 Restore 成员将程序变量传输到该项。
//...
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); 
 }
//...
类用法
如果需要保存/恢复嵌套变量,请从 CAppInfoBase 派生。CAppInfoBase 具有用于 CString 类型的专用 Save 和 Restore 成员。在您的派生类中添加其他所需的专用化。
//...
#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
类描述
CAppInfoBase 与 CAppWindow<T> 相关联。
template < class T >
class CAppInfoT : public CAppInfoBase
{
public:
 CAppInfoT() : CAppInfoBase( T::m_szAppKey){}
};
类用法
CAppWindow<T> 将其 CAppInfo 类型定义为 CAppInfoT<T>。
//...
template <class T>
class CAppWindow
{
public:
 typedef class CAppInfoT<T> CAppInfo;
//...
在 CRegisterDlg::Register 中实例化 CAppInfoT。
// ImageViewdlg.h
//...
class CRegisterDlg : public CStdDialogImpl<CRegisterDlg>
{
public:
//...
 void Register( ImageType typ, BOOL bRegister)
 {
  CAppInfoT<CMainFrame> info;
//...
CAppWindow
template <class T> class CAppWindow
类描述
PPC 应用程序框架窗口的基类,具有以下特性:
- 命令行参数传输到 OnCreate处理程序和上一个实例//... 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消息支持- //... 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); //...
- 通过相关系统调用支持激活、停用和设置更改//... 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); } //...
- 应用程序状态持久性支持。//... 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; } //...
类用法
- 在您的 AppWizard 生成的应用程序 ::_tWinMain中- 定义 LPCTSTR CMainFrame::m_szAppKey,
- 调用带两个参数的 CMainFrame::ActivatePreviousInstance,
- 将对 Run的调用更改为CMainFrame::AppRun,并且
- 删除 Run函数。
 
- 定义 
// 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);
//...
}
- 在您的框架定义中- 将 CAppWindow添加到您的主窗口继承列表,
- 链接消息映射,
- 删除 ActivatePreviousInstance成员定义,
- 定义 AppHibernate、AppNewInstance和AppSave成员的全部或部分,
- 在 OnCreate处理程序中处理命令行参数,和/或恢复应用程序状态和数据。
 
- 将 
// 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
  {
//...
帧大小和位置
AtlFixUpdateLayout
void AtlFixUpdateLayout( HWND hWndFrame, HWND hWndMenuBar)
函数描述
WTL 7.1 的框架定位修复(目前 7.5 仍然需要)。
函数用法
- 如果您不使用 CFullScreenFrame,在您的 AppWizard 生成的CMainFrame::OnCreate中,在菜单栏创建后调用AtlFixUpdateLayout。//... LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { CreateSimpleCEMenuBar(IDR_MAINFRAME, SHCMBF_HMENU); AtlFixUpdateLayout( m_hWnd, m_hWndCECommandBar); //...
- 如果您想使用 CFullScreenFrame,定义一个调用AtlFixUpdateLayout,然后调用基类UpdateLayout的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
类描述
启用全屏的框架窗口类
- bool m_bFullScreen保存当前状态;
- void SetFullScreen( bool bFull)设置请求的状态。
- template <class D> int FSDoModal( D& dlg)恢复任务栏(如果已隐藏),调用- dlg.DoModal(),隐藏任务栏(如果已恢复),并返回- dlg.DoModal()的返回值。
类用法
- 在您的框架定义中- 对于 WTL 7.1(以及目前的 7.5),定义一个调用 AtlFixUpdateLayout,然后调用基类UpdateLayout的UpdateLayout成员。
- 将 CFullScreenFrame添加到您的主窗口继承列表,
- 实现对 SetFullScreen的调用,
- 如果需要,检查 m_bFullScreen,
- 通过 FSDoModal调用模态对话框和属性页。
 
- 对于 WTL 7.1(以及目前的 7.5),定义一个调用 
// 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 支持
DIB 结构
- struct DIBINFO16创建大小为 16 位的- DIB描述符,带有- BI_BITFIELDS。
DIB 函数描述
- HBITMAP AtlGetDibBitmap( LPBITMAPINFO pbmi)从压缩的- DIB构建一个- HBITMAP。
- HBITMAP AtlCopyBitmap( HBITMAP hbm , SIZE size, bool bAsBitmap = false)将- HBITMAP复制到尺寸为- size的压缩- DIBINFO16或- DDB,具体取决于- bAsBitmap。
- HLOCAL AtlCreatePackedDib16( HBITMAP hbm, SIZE size)从给定的- HBITMAP创建一个大小为- size的压缩- DIBINFO16。
- bool AtlSetClipboardDib16( HBITMAP hbm, SIZE size, HWND hWnd)将剪贴板- CF_DIB格式设置为从- HBITMAP复制的大小为- DIBINFO16。
- HBITMAP AtlGetClipboardDib( HWND hWnd)从剪贴板- CF_DIB格式返回一个- HBITMAP。
DIB 函数用法
使用这组函数来支持 CF_DIB 剪贴板格式,PPC 中不支持进程间 CF_BITMAP 剪贴板对象。
// 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 在 ImageView 的 CImagePage 和 CViewPage 中用于填充 CStatic 控件位图。
// 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 功能
连续缩放功能
CImageViewView 派生自 CZoomScrollImpl,后者实现了该功能。此类的完整描述在 CodeProject 中:将缩放添加到 WTL CScrollImpl。
使用方向键和菜单栏中的跟踪条进行缩放的用户界面
此功能需要一些步骤
- 在资源编辑器中创建一个 ID 为 ID_ZOOM的菜单栏按钮
- 在 CImageViewView中声明一个CTrackbarCtrl成员。
// ImageViewview.h
//...
class CImageViewView : 
 public CWindowImpl< CImageViewView > ,
 public CZoomScrollImpl< CImageViewView > 
{
public:
 DECLARE_WND_CLASS(NULL)
 CTrackBarCtrl m_ZoomCtrl;
//...
- 子类化菜单栏以转发跟踪条消息
- 在 ID_ZOOM按钮的位置创建CImageViewView::m_ZoomCtrl。
// 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);
//...
- 为当前图像设置跟踪条范围。
// 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 ));
 }
- 管理跟踪条状态
- 启用/禁用
// 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
- 背景
// 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 );
 }
- 将键盘消息转发到跟踪条
- 在跟踪条移动时设置缩放
// 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;
 }
//...
表现良好的系统交互
CMainFrame 派生自 CAppWindow,后者实现了该功能。请参阅 CAppWindow 类用法。
图像属性页
CMainFrame 派生自 CStdPropertySheet(请参阅 类用法)并通过 FSDoModal 激活。
// mainfrm.h
//...
 LRESULT OnProperties(WORD /*wNotifyCode*/, WORD /*wID*/, 
                      HWND /*hWndCtl*/, BOOL& /*bHandled*/)
  {
   CImageProperties prop( m_sFile, m_view);
   return FSDoModal( prop);
  }
//...
创建时会提供三个属性页的相关数据。
// 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 和 CViewPage 使用 AtlCopyBitmap 显示图像的小副本。
剪贴板复制和粘贴
ImageView 直接使用 DIB 支持函数。
可选标题栏
CImageViewview::m_TitleBar 是一个带有 CCS_TOP 样式的 CStatusBarCtrl,CFrameWindowImplBase::UpdateLayout 目前忽略它。这在 CMainFrame::UpdateLayout 中处理。
标题栏可见性在 CMainFrame::AppSave 中保存并在创建时恢复。
//
//... 
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);
//...
当缩放或图像名称更改时,设置带有 SBT_OWNERDRAW 标志的文本会触发 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);
  }
全屏视图
CMainFrame 派生自 CFullScreenFrame,后者实现了该功能。请参阅 CFullScreenFrame 类用法。
点按和滚动图像移动
上次触控笔位置保存在 CImageViewView::m_ptMouse 中,并且视图偏移量在 WM_MOUSEMOVE 消息上更改。
// 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;
 }
//...
可选滚动条
由于 Windows CE 不支持方便的 ::ShowScrollBar(),实现此功能需要一些工作:
- 声明 CImageViewView::m_bShowScroll以保存滚动条可见性。
- 实现 CImageViewView::ShowScrollBars函数和CImageViewView::OnShowScrollBars命令处理程序。
- 覆盖 CZoomScrollImpl::SetScrollOffset和CZoomScrollImpl::SetZoom,这些函数由CImageViewView调用。
- 覆盖 CZoomScrollImpl::OnSize处理程序。
// 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;
 }
- 实现滚动条可见性持久化
- 更新菜单项检查
// 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
 }
//...
系统注册为图像文件默认程序
此功能由 CRegisterDlg 处理。
图像文件通过 HKEY_CLASSES_ROOT\xxxxxx\Shell\Open\Command 注册表项的默认字符串值与默认程序关联,其中 xxxxx 取决于文件类型。当用户点击文件名时,shell 会执行在那里找到的命令。但是,只执行位于 \Windows 的程序。
CRegisterDlg::InitDialog 检查当前的 ImageView 位置,如果位置不在 \Windows,则调用 CMoveDlg::DoModal。
// 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 如果用户允许,则移动 ImageView.exe,并根据请求在旧程序位置创建快捷方式。
// 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;
 }
};

在 IDOK 返回时,或者如果 ImageView 位置正确,CRegisterDlg::InitDialog 初始化图像文件类型的注册状态。一个帮助类 CImageTypeKey : public CRegKey 提供注册表访问。
// 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 使用 CAppInfoT<CMainFrame> 在注册时保存现有键,并在注销时恢复它。
// 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;
 }
结论
通过 WTL 7.1 或 7.5 以及 atlppc.h 中有限的 PPC 特定类和函数集,您(和我)可以轻松编写出色的真实 Pocket PC 应用程序。




