65.9K
CodeProject 正在变化。 阅读更多。
Home

为 WTL CScrollImpl 添加缩放功能

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.35/5 (7投票s)

2004年10月23日

CPOL

8分钟阅读

viewsIcon

72928

downloadIcon

2746

CZoomScrollImpl 扩展了 WTL 的 CScrollImpl,以支持连续缩放。

引言

缩放功能有助于在小屏幕上查看大型图像文件。

此实现旨在同时在仅提供部分 GDI 功能的 PPC 设备和具有 Win32 系统的台式计算机上执行。

早期版本依赖于针对 PPC 的私有 WTL 适配。当 WTL 7.1 发布并包含 Pocket PC 支持后,是时候将其作为附加组件了。

zoomscrl.h 和示例文件将与 WTL 7.1 在 Visual Studio .NETEVC 3.04.0 中干净地编译和执行。

WTL 7.5 即将推出

WTL 现在发展迅速。WTL 7.5 正在 SourceForge.net 进行开发,在我撰写本文时,当前工作版本是 WTL 7.5.4291.0

此版本仍然缺少 CSize 标量运算符,并且没有解决所有 Pocket PC 问题。因此,如果您使用此版本编译,将会遇到一些奇怪的 PPC 行为……

zoomscrl.h 将与 WTL 7.5.4291.0 在 Visual Studio .NETEVC 3.04.0 中干净地编译和执行。

BmpZoom 项目将与 WTL 7.5.4291.0 在 Visual Studio .NET 中干净地编译和执行。

BmpZoomPPC 项目将与 WTL 7.5.4291.0 在 EVC 3.04.0 中干净地编译但奇怪地执行。

当 WTL 7.5 实现 CSize 标量运算符时,编译器将不喜欢 zoomscrl.h 中相同的外部定义。只需注释掉或删除 zoomscrl.h 第 39 行

#define _WTL_NO_SIZE_SCALAR // remove this line when 

       // WTL supports CSize scalar operators 

CSize 标量运算符

在使用比例尺时,CSize 类是常用的载体,但它缺少乘法和除法运算符。在这里,它们是在外部实现的,希望它们能成为最终 WTL 7.5 版本 CSize 类的一部分。

模板化的写法允许最小化和充分的编译。

  • template < class Num > inline CSize operator * ( tagSIZE s, Num n)
  • template < class Num > inline void operator *= ( tagSIZE & s, Num n)
  • template < class Num > inline CSize operator / ( tagSIZE s, Num n)
  • template < class Num > inline void operator /= ( tagSIZE & s, Num n)

这些运算符允许简单易于维护的写法,例如:

CSize halfSize = mySize / 2;

Class CZoomScrollImpl<T> 成员

该类继承自 CScrollImpl<T>,后者负责滚动部分。我们添加了一个缩放层,并重写了 CScrollImpl 方法,为它们提供缩放后的尺寸和位置。

数据成员

CSize m_sizeTrue 存储未缩放的图像尺寸,CScrollImpl::m_sizeAll 存储缩放后的尺寸。double m_fzoom 存储缩放因子,可以通过 double GetZoom() 访问。

数据访问补充

该类提供了一些额外的数据访问成员,允许如下写法:

CSize sizePage = GetScrollPage();

这些成员是:

  • CSize GetScrollSize()
  • CSize GetScrollPage()
  • CSize GetScrollLine()
  • CPoint GetScrollOffset()

缩放操作和访问方法

SetZScrollSize 由重写的 SetScrollSize() 调用,并在计算出缩放后的尺寸后,调用基类 CScrollImpl<T>::SetScrollSize()。此成员有两种形式:

  • void SetZScrollSize( CSize sizeTrue, double fzoom = 1., BOOL bRedraw = TRUE )
  • void SetZScrollSize( int cx, int cy, double fzoom=1., BOOL bRedraw = TRUE )

SetZoom 更改缩放因子,缩放页面和行滚动参数,并使用新的缩放因子保持初始中心点,如果 bRedrawTRUE 则重绘图像。

  • void SetZoom( double fzoom, BOOL bRedraw = TRUE )
  • double GetZoom() 返回当前的缩放因子。

辅助坐标方法

WndtoTrueTruetoWnd 方法为单个点、矩形和点数组计算相应的窗口和图像坐标。

  • CPoint WndtoTrue( CPoint ptW )
  • CPoint TruetoWnd( CPoint ptT )
  • voidWndtoTrue( LPPOINT aptW, int nPts ) // 就地坐标转换
  • voidWndtoTrue( LPRECT prectW ) // 就地坐标转换
  • void TruetoWnd( LPPOINT aptT, nPts ) // 就地坐标转换
  • void TruetoWnd( LPRECT prectT ) // 就地坐标转换

绘图操作方法

这些成员假定数据成员已正确设置,至少已调用一次 SetScrollSize(),并根据当前的缩放和滚动设置绘制位图或内存 DC。

  • void DrawBitmap( HBITMAP hbm, HDC hdestDC, DWORD dwROP = SRCCOPY)
  • void DrawDC( HDC hsourceDC, HDC hdestDC, DWORD dwROP = SRCCOPY)

消息映射和处理程序

BEGIN_MSG_MAP(CZoomScrollImpl<T>)
    MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBkgnd)
#if _WTL_VER <= 0x0710
    MESSAGE_HANDLER(WM_SIZE, OnSize)
#endif //    _WTL_VER <= 0x0710

    CHAIN_MSG_MAP(CScrollImpl<T>)
END_MSG_MAP()

WTL 7.1 的 CScrollImpl::OnSize 处理程序有时在滚动条的外观/消失方面存在问题,这在当前 WTL 7.5 中已修复。

如果设置了 SCRL_ERASEBACKGROUND 扩展样式(在 CScrollImpl 中为默认值),我们会无闪烁地擦除背景。否则,我们不做任何操作并将 bHandled 设置为 FALSE

使用该类

添加缩放功能

从现有的 CScrollImpl 派生类窗口开始,将其继承更改为 CZoomScrollImpl,并修改 OnDraw 成员以利用绘图方法。

如果存在,请从您的消息映射中删除 WM_ERASEBKGND 处理程序,并链接到 CZoomScrollImpl<CMyView>

检查到目前为止没有明显的变化……您现在已经启用了缩放功能,缩放因子为 1.0,并且无法更改。

现在实现一些用户界面来调用 SetZoom()。这是困难的部分。

绘图问题

该类在栅格图像上进行缩放和滚动。

矢量绘图可以绘制到全尺寸的 DIB 或内存 DC,然后将生成的栅格图像进行缩放和滚动。

如果您想在缩放后的图像上绘制**固定大小的符号或标签**,可以在您的 DoPaint 成员中直接绘制到缩放的背景,例如:

void DoPaint( CDCHandle dc)
{
    DrawBitmap( m_bmp, dc); // Zoomed and scrolled bitmap is shown 

    // Your unzoomed drawing routine here : use the TruetoWnd() 

    // helpers if necessary

}

从 BmpView 到 BmpZoom

BmpView 是 WTL 发行版中一个非常丰富的示例。在 WTL 7.1 中,提供了 Pocket PC 版本 BmpViewPPC

BmpZoomBmpZoomPPC 在不进行其他更改的情况下,为初始示例添加了缩放功能。

通用更改标记为 // ZOOM,PPC 特有更改标记为 // ZOOMPPC。一些 BmpViewPPC 的 bug 修复标记为 // BMPVIEWPPC

CBitmapView 缩放启用

CBitmapView 被大大简化。我们更改了继承,删除了 WM_ERASEBKGND 处理程序,并将 DoPaint 成员简化为两行。在加载新位图时,我们在 SetBitmap 中调用 SetZScrollSize 以将缩放因子重置为 1.0。

class CBitmapView : public CWindowImpl<CBitmapView>, 
    public CZoomScrollImpl< CBitmapView > // ZOOM


{
    //...

    void SetBitmap(HBITMAP hBitmap)
    {
        //...

        SetScrollOffset(0, 0, FALSE);
        SetZScrollSize( m_size); // ZOOM

    }
    
    BEGIN_MSG_MAP(CBitmapView)
        CHAIN_MSG_MAP(CZoomScrollImpl<CBitmapView>); // ZOOM

    END_MSG_MAP()
 
    void DoPaint( CDCHandle dc)
    {
        if( !m_bmp.IsNull())
        DrawBitmap( m_bmp, dc); // ZOOM 

    }
}

实现滑块

这是比较棘手的部分。我们在工具栏中安装一个滑块通用控件,以便用户可以根据需要设置缩放。

更改在 CMainFrame 中,并且部分适用于桌面和 PPC。

首先,我们向 CMainFrame 添加一个 CTrackbarCtrl 成员。

//...

    CBitmapView m_view;
    CTrackBarCtrl m_ZoomCtrl; // ZOOM

OnCreate 处理程序的末尾,我们创建已禁用的滑块并正确放置它。

桌面滑块创建

我们将控件创建为框架的子控件,并提供适当的大小。

我们必须 SetLineSize(0),因为 AddSimpleReBarBand() 会向我们的控件发送 TB_BUTTONCOUNT,它会被理解为 TBM_GETLINESIZE(两者都定义为 WM_USER+24)。滑块控件将返回 0,Rebar 条带将被正确安装。

我们将滑块的条带大小设置为工具栏留下的空间,并将滑块的行大小重置为 1。

int OnCreate(LPCREATESTRUCT /*lpCreateStruct*/)
{ 

    //...

#ifndef _WIN32_WCE
    CRect rCtrl(0,0,120,22);
    m_ZoomCtrl.Create( m_hWnd,rCtrl,NULL ,WS_CHILD | WS_VISIBLE | 
        TBS_AUTOTICKS | TBS_ENABLESELRANGE );
    m_ZoomCtrl.SetLineSize(0); // see explanation

    AddSimpleReBarBand( m_ZoomCtrl, _T("Zoom"), FALSE, 140, FALSE);
#if _WTL_VER <= 0x0710 
    ::SendMessage( m_hWndToolBar, RB_MAXIMIZEBAND, 1, TRUE);
#else
    CReBarCtrl rebar = m_hWndToolBar;
    rebar.MaximizeBand(1,TRUE);
#endif // _WTL_VER <= 0x0710

    m_ZoomCtrl.SetLineSize(1);

#else
    // ...

PPC 滑块创建

对于 PPC,我们需要子类化菜单栏 - 在其中我们已经在资源编辑器中创建了一个 ID 为 ID_ZOOM 的菜单项“Zoom Control”。

#ifdef WIN32_PLATFORM_PSPC // ZOOMPPC

class CCtrlMenuBar : public CWindowImpl< CCtrlMenuBar, 
                            CCECommandBarCtrlT<CToolBarCtrl> >
{
public:
    DECLARE_WND_SUPERCLASS( _T("CtrlMenuBar"), _T("ToolbarWindow32") );
    BEGIN_MSG_MAP(CCtrlMenuBar)
    if ( uMsg != WM_NOTIFY )
        FORWARD_NOTIFICATIONS()
    END_MSG_MAP()
};
#endif // WIN32_PLATFORM_PSPC // ZOOMPPC

子类化的菜单栏会将 WM_HSCROLL 消息转发给我们的框架。

我们定义一个 CCtrlMenuBar 成员,并在菜单栏创建后对其进行子类化。

class CMainFrame : public CFrameWindowImpl<CMainFrame>, 
    public CUpdateUI<CMainFrame>,
    public CMessageFilter, public CIdleHandler
#ifndef _WIN32_WCE
    , public CPrintJobInfo
#endif // _WIN32_WCE

{
public:
    DECLARE_FRAME_WND_CLASS(NULL, IDR_MAINFRAME)

#ifndef _WIN32_WCE
    CCommandBarCtrl m_CmdBar;
    CRecentDocumentList m_mru;
    CMruList m_list;
#else
    CCtrlMenuBar m_CmdBar; // ZOOMPPC

#endif // _WIN32_WCE
//...

int OnCreate(LPCREATESTRUCT /*lpCreateStruct*/) 
{
    //... 

#else // WIN32_PLATFORM_PSPC

    CreateSimpleCEMenuBar(IDR_MAINFRAME, SHCMBF_HMENU);
    m_CmdBar.SubclassWindow(m_hWndCECommandBar); // ZOOMPPC

#endif // WIN32_PLATFORM_PSPC

#endif // _WIN32_WCE

OnCreate 的末尾,我们将控件创建为菜单栏的子控件,并使用我们 ID_ZOOM 按钮的尺寸和位置。

//...

#else
    CRect rZoom;
    m_CmdBar.GetRect(ID_ZOOM,rZoom);
    m_ZoomCtrl.Create( m_hWndCECommandBar,rZoom, NULL, 
        WS_CHILD | WS_VISIBLE | WS_BORDER | TBS_TOP , 0, ID_ZOOM );
    m_ZoomCtrl.SetWindowPos(HWND_TOP,rZoom.left,rZoom.top + 1, 
                  rZoom.Width(),rZoom.Height() - 1,SWP_SHOWWINDOW); 

#endif //_WIN32_WCE

    m_ZoomCtrl.EnableWindow(FALSE);

// ZOOM end

我们在创建时禁用滑块:此时我们没有任何东西可以缩放。

滑块准备

当打开新图像时,我们将滑块范围设置为 100 到*允许完整图像适应当前窗口大小的缩放因子的百倍*。因此,通过将滑块位置(一个整数)除以 100.0,我们可以得到所需的缩放因子(如果位置是218,则缩放因子应为2.18)。当有可缩放内容时,我们会启用滑块。

此代码在 OnFileRecentOnFileOpen 处理程序中重复。

//...

    CSize sImg=m_view.GetScrollSize();
    CSize sClient=m_view.m_sizeClient;
    m_ZoomCtrl.SetRange(100, max( (100 * sImg.cx) /sClient.cx , 
                             (100 * sImg.cy) /sClient.cy));
    m_ZoomCtrl.SetPos(100);
    m_ZoomCtrl.EnableWindow();

//...

当图像被删除时,我们禁用滑块并将其位置设置为最左边。

void OnEditClear(UINT /*uNotifyCode*/, int /*nID*/, CWindow /*wnd*/)
{
    //...

    m_view.SetBitmap(NULL);
    m_ZoomCtrl.SetPos(0); // ZOOM

    m_ZoomCtrl.EnableWindow(FALSE); // ZOOM

    //...

滑块缩放

当我们的框架收到 WM_HSCROLL 消息时,它可能只来自滑块。我们将此行添加到框架消息映射中:

     MSG_WM_HSCROLL(OnZoomCtrl) // ZOOM

我们将 SetZoom 设置为滑块缩略图位置的百分之一。

void OnZoomCtrl( int /*iType*/, short /*iTrackPos*/, HWND /*hWndTrackBar*/ )
{
    if ( !m_view.m_bmp.IsNull())
        m_view.SetZoom( m_ZoomCtrl.GetPos() / 100.0 );
}

这应该是我们示例描述的结尾,但 BmpZoomPPC 仍然有些奇怪的行为……

WTL 7.1 PPC 的弱点

我们可以编译甚至运行 BmpZoomPPC,但是:

  • 菜单项始终处于活动状态:WTL 7.5.4291.0 中已修复。
  • 框架未正确放置。
  • 属性表会疯狂断言,并且不符合 PPC 的风格。

这些早期的问题应在 WTL 7.5 最终版本中解决,其中一部分已在 WTL 7.5.4291.0 中修复。

在此期间,我们通过一些本地 CMainFrame 成员进行解决。

  • LRESULT OnInitMenuPopup(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
  • void UpdateLayout(BOOL bResizeBars = TRUE)

我们还修改了 props.h 中的 CBmpPropertiesCPageTwo 类,使属性表和页面符合 PPC 标准。

我们离一个行为良好的 PPC 应用程序还有很长的路要走。我希望在 CodeProject PPC 部分发布一个行为良好的 WTL 图像查看器。到时候再见 这里

历史

  • 更新于 2004-10-31,针对 WTL 7.5.4291.0 问题
© . All rights reserved.