
// Copyright (C) Recursion Company. All rights reserved.

#include "stdafx.h"
#include "vol_user_app_info.h"

static void sEnumAllVisibleChild (HWND hParent, CMUIntPArray& haryWindows)
{
    ASSERT (hParent != NULL);

	for (HWND hWnd = ::GetTopWindow (hParent); hWnd != NULL; hWnd = ::GetNextWindow (hWnd, GW_HWNDNEXT))
	{
		if (::IsWindowVisible (hWnd))
		{
			if (::GetTopWindow (hWnd) != NULL)
				sEnumAllVisibleChild (hWnd, haryWindows);

			haryWindows.Add ((UINT_P)hWnd);
		}
	}
}

static void sReDrawAllChild (HWND hParent, const CRect& rtClipScreen)
{
	CMUIntPArray haryWindows;
	sEnumAllVisibleChild (hParent, haryWindows);

	const INT_P npNumChildWindows = haryWindows.GetCount ();
	for (INT_P npIndex = npNumChildWindows - 1; npIndex >= 0; npIndex--)
	{
		const HWND hWnd = (HWND)haryWindows [npIndex];

	    CRect rtUpdate;
		::GetWindowRect (hWnd, rtUpdate);
		rtUpdate &= rtClipScreen;

		if (rtUpdate.IsRectEmpty () == FALSE)
		{
	        ::ScreenToClient (hWnd, (LPPOINT)&rtUpdate);
	        ::ScreenToClient (hWnd, ((LPPOINT)&rtUpdate) + 1);

            ::RedrawWindow (hWnd, rtUpdate, NULL, (RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_ERASENOW | RDW_UPDATENOW));
		}
	}
}

static void sRedrawWindowInsideParent (CWnd* pwndCurrent)
{
    ASSERT (pwndCurrent != NULL);

    CWnd* pParentWnd = pwndCurrent;
    while ((pParentWnd->GetStyle () & WS_POPUP) == 0)
    {
        CWnd* pWnd = pParentWnd->GetParent ();
        if (pWnd == NULL)
            break;
        pParentWnd = pWnd;
    }

    CRect rtClipScreen;
    pwndCurrent->GetClientRect (&rtClipScreen);
    pwndCurrent->ClientToScreen (&rtClipScreen);

    CRect rtParent (rtClipScreen);
    pParentWnd->ScreenToClient (&rtParent);
	pParentWnd->InvalidateRect (&rtParent);
    // pParentWnd->UpdateWindow ();

    sReDrawAllChild (pParentWnd->m_hWnd, rtClipScreen);
}

//-----------------------------------------------------------------------------------------

IMPLEMENT_DYNAMIC (CVWnd_Container, CWnd)

BEGIN_MESSAGE_MAP (CVWnd_Container, CWnd)
    //{{AFX_MSG_MAP(CVWnd_Container)
    ON_WM_PAINT ()
    ON_WM_CREATE ()
    ON_WM_DESTROY ()
    ON_WM_MOVE ()
    ON_WM_SIZE ()
    ON_WM_SETFOCUS ()
    ON_WM_SHOWWINDOW ()
    ON_WM_GETDLGCODE ()
    ON_MESSAGE (MWM_PRE_FILTER_INPUT_MSG, OnPreFilterInputMessage)
    //}}AFX_MSG_MAP
END_MESSAGE_MAP ()

void CVWnd_Container::OnConstruct ()
{
    m_clrBack = CLR_DEFAULT;
    m_enBackPicPaintMode = VPPM_LEFT_TOP;
    m_blDisableToolTipCtrlWinTheme = TRUE;
    m_pToolTipCtrl = NULL;
    m_blpTransparent = FALSE;

#ifdef _VOL_FOR_UI_DESIGNER  // 为编译布局设计器专用版本?
    m_cxCellSize = m_cyCellSize = 0;
#endif
}

CToolTipCtrl* CVWnd_Container::GetToolTipCtrl ()
{
#ifdef _VOL_FOR_UI_DESIGNER  // 为编译布局设计器专用版本?
    return NULL;
#else
    if (m_pToolTipCtrl == NULL && m_hWnd != NULL)
    {
        m_pToolTipCtrl = new CToolTipCtrl;

        if (m_pToolTipCtrl->Create (this) == FALSE)
        {
            delete m_pToolTipCtrl;
            m_pToolTipCtrl = NULL;
        }
        else
        {
            if (m_blDisableToolTipCtrlWinTheme)  // 禁止使用系统主题?
                ::SetWindowTheme (m_pToolTipCtrl->m_hWnd, _T (""), _T (""));  // 避免背景/文本颜色/边空这些属性设置无效.
        }
    }

    return (m_pToolTipCtrl->GetSafeHwnd () == NULL ? NULL : m_pToolTipCtrl);
#endif
}

BOOL CVWnd_Container::SetToolTip (CWnd* pWnd, const TCHAR* szToolTip)
{
    ASSERT (pWnd != NULL);

    CToolTipCtrl* pToolTipCtrl = GetToolTipCtrl ();
    if (pToolTipCtrl == NULL)
        return FALSE;

    pToolTipCtrl->DelTool (pWnd);

    if (IsEmptyStr (szToolTip))
        return TRUE;

    return pToolTipCtrl->AddTool (pWnd, szToolTip);
}

BOOL CVWnd_Container::sSetToolTip (CWnd* pWnd, const TCHAR* szToolTip)
{
    ASSERT (pWnd != NULL);

    const CRuntimeClass* pRuntimeClass = RUNTIME_CLASS (CVWnd_Container);

    CWnd* pParentWnd = pWnd->GetParent ();
    while (pParentWnd != NULL)
    {
        if (pParentWnd->IsKindOf (pRuntimeClass))
            return ((CVWnd_Container*)pParentWnd)->SetToolTip (pWnd, szToolTip);

        pParentWnd = pParentWnd->GetParent ();
    }

    return FALSE;
}

void CVWnd_Container::TTSetDelayTime (DWORD dwDuration, INT nDelay)
{
    CToolTipCtrl* pToolTipCtrl = GetToolTipCtrl ();

    if (pToolTipCtrl != NULL)
        pToolTipCtrl->SetDelayTime (dwDuration, (UINT)nDelay);
}

void CVWnd_Container::TTSetMargin (INT nMargin)
{
    CToolTipCtrl* pToolTipCtrl = GetToolTipCtrl ();

    if (pToolTipCtrl != NULL)
    {
        RECT rt;
        rt.left = rt.top = rt.right = rt.bottom = nMargin;
        pToolTipCtrl->SetMargin (&rt);
    }
}

void CVWnd_Container::TTSetMaxTipWidth (INT nWidth)
{
    CToolTipCtrl* pToolTipCtrl = GetToolTipCtrl ();

    if (pToolTipCtrl != NULL)
        pToolTipCtrl->SetMaxTipWidth (nWidth);
}

void CVWnd_Container::TTSetTipBkColor (COLORREF clr)
{
    CToolTipCtrl* pToolTipCtrl = GetToolTipCtrl ();

    if (pToolTipCtrl != NULL)
        pToolTipCtrl->SetTipBkColor (clr);
}

void CVWnd_Container::TTSetTipTextColor (COLORREF clr)
{
    CToolTipCtrl* pToolTipCtrl = GetToolTipCtrl ();

    if (pToolTipCtrl != NULL)
        pToolTipCtrl->SetTipTextColor (clr);
}

LRESULT CVWnd_Container::OnPreFilterInputMessage (WPARAM wParam, LPARAM lParam)
{
    if (lParam == _VOL_MSG_ACK)  // 调用格式正确?
        ProcessInputMessage ((MSG*)wParam);

    return 0;
}

void CVWnd_Container::ProcessInputMessage (MSG* pMsg)
{
    ASSERT (pMsg != NULL);

    ProcessToolTipMessage (pMsg);
}

void CVWnd_Container::ProcessToolTipMessage (MSG* pMsg)
{
    ASSERT (pMsg != NULL);

#ifndef _VOL_FOR_UI_DESIGNER  // 不为编译布局设计器专用版本?
    if (m_pToolTipCtrl->GetSafeHwnd () != NULL && m_pToolTipCtrl->GetToolCount () > 0)
    {
        const UINT msg = pMsg->message;

        if (msg == WM_LBUTTONDOWN || msg == WM_LBUTTONUP ||
                msg == WM_MBUTTONDOWN || msg == WM_MBUTTONUP ||
                msg == WM_RBUTTONDOWN || msg == WM_RBUTTONUP ||
                msg == WM_MOUSEMOVE)
        {
            m_pToolTipCtrl->RelayEvent (pMsg);
        }
    }
#endif
}

#ifdef _VOL_FOR_UI_DESIGNER  // 为编译布局设计器专用版本?

void CVWnd_Container::ShowAlignGrid (INT cxCellSize, INT cyCellSize)
{
    ASSERT (cxCellSize >= 0 && cyCellSize >= 0);  // 布局设计器调用过来时必定符合此要求

    if (m_cxCellSize != cxCellSize || m_cyCellSize != cyCellSize)
    {
        m_cxCellSize = cxCellSize;
        m_cyCellSize = cyCellSize;

        Invalidate (FALSE);
    }
}

#endif

BOOL CVWnd_Container::CreateControl (CVolNativeWndCreateParam* pCreateParam)
{
    pCreateParam->m_dwStyle &= ~WS_TABSTOP;
    // pCreateParam->m_dwStyle |= WS_CLIPCHILDREN;  // 此语句将导致设计器控制点刷新出问题

#ifndef _VOL_FOR_UI_DESIGNER  // 不为编译布局设计器专用版本?
    if (m_blpTransparent)
        pCreateParam->m_dwExStyle |= WS_EX_TRANSPARENT;
#endif

    if (CWnd::CreateEx (pCreateParam->m_dwExStyle, VolRegisterWndClass (_T ("VolContainer")),
            pCreateParam->m_szCaption, pCreateParam->m_dwStyle,
            pCreateParam->m_nLeft, pCreateParam->m_nTop, pCreateParam->m_nWidth, pCreateParam->m_nHeight,
            pCreateParam->m_pParentWnd->GetSafeHwnd (), (HMENU)pCreateParam->m_nID))
    {
        // 标志本窗口需要过滤其自身及其下属子窗口的输入信息
        CVolAppInstance::sSetWndNeedPreFilterInputMessage (m_hWnd);

        return TRUE;
    }

    return FALSE;
}

void CVWnd_Container::TakeOverBackBitmap (HBITMAP hBitmap)
{
    m_bmpBack.DeleteObject ();
    m_bmpBack.Attach (hBitmap);

    RedrawContainer ();
}

void CVWnd_Container::RedrawContainer ()
{
    if (m_hWnd == NULL)
        return;

#ifndef _VOL_FOR_UI_DESIGNER  // 不为编译布局设计器专用版本?
    if (m_blpTransparent)
        sRedrawWindowInsideParent (this);
#endif

    Invalidate (FALSE);
}

HBITMAP CVWnd_Container::CloneBackBitmap (INT_P npOutWidth, INT_P npOutHeight)
{
    return ((HBITMAP)m_bmpBack == NULL ? NULL : CloneBitmap ((HBITMAP)m_bmpBack, (INT)npOutWidth, (INT)npOutHeight));
}

void CVWnd_Container::OnPaint ()
{
    CPaintDC dcPaint (this);

    CRect rtClipBox;
    dcPaint.GetClipBox (&rtClipBox);

    CRect rtClient;
    GetClientRect (&rtClient);

    rtClipBox &= rtClient;
    if (rtClipBox.IsRectEmpty ())
        return;

#ifndef _VOL_FOR_UI_DESIGNER  // 不为编译布局设计器专用版本?
    if (m_blpTransparent == FALSE)
#endif
        dcPaint.FillRect (&rtClipBox, &CBrush (GetRealBackColor (m_clrBack)));  // 填充底色

    VolDrawBitmap (m_enBackPicPaintMode, &dcPaint, m_bmpBack, rtClient, rtClipBox, NULL);  // 绘制底图

#ifdef _VOL_FOR_UI_DESIGNER  // 为编译布局设计器专用版本?
    // 检查绘制对齐网格
    if (m_cxCellSize > 0 && m_cyCellSize > 0)
    {
        CMRect rtClientWindow (rtClient);
        ClientToScreen (&rtClientWindow);

        CMRect rtWindow;
        GetWindowRect (&rtWindow);

        // 转换到布局位置
        POINT pt;
        pt.y = rtClient.top - (rtClientWindow.top - rtWindow.top);
        INT xBeginPos = rtClient.left - (rtClientWindow.left - rtWindow.left);

        while (pt.y <= rtClient.bottom)
        {
            pt.x = xBeginPos;

            while (pt.x <= rtClient.right)
            {
                if (rtClipBox.PtInRect (pt))
                    dcPaint.SetPixelV (pt.x, pt.y, RGB (0, 0, 0));

                pt.x += m_cxCellSize;
            }

            pt.y += m_cyCellSize;
        }
    }
#endif
}

int CVWnd_Container::OnCreate (LPCREATESTRUCT lpCreateStruct)
{
    if (CWnd::OnCreate (lpCreateStruct) == -1)
        return -1;

    if (m_pEventListener != NULL)
        m_pEventListener->OnCreate ();

    return 0;
}

UINT CVWnd_Container::OnGetDlgCode ()
{
    return DLGC_STATIC;
}

void CVWnd_Container::OnDestroy ()
{
    if (m_pEventListener != NULL)
        m_pEventListener->OnDestroy ();

    if (m_pToolTipCtrl != NULL)
    {
        m_pToolTipCtrl->DestroyWindow ();
        delete m_pToolTipCtrl;
        m_pToolTipCtrl = NULL;
    }

    CWnd::OnDestroy ();
}

void CVWnd_Container::OnMove (int x, int y)
{
    CWnd::OnMove (x, y);

#ifndef _VOL_FOR_UI_DESIGNER  // 不为编译布局设计器专用版本?
    if (m_blpTransparent)
	    RedrawContainer ();
#endif

    if (m_pEventListener != NULL)
        m_pEventListener->OnMove ();
}

void CVWnd_Container::OnSize (UINT nType, int cx, int cy) 
{
    CWnd::OnSize (nType, cx, cy);

    if (m_pEventListener != NULL)
        m_pEventListener->OnSize ();
}

void CVWnd_Container::OnSetFocus (CWnd* pOldWnd)
{
    CWnd::OnSetFocus (pOldWnd);
    SetFocusToFisrtFocusableChildControl (m_hWnd);  // 将输入焦点转移到其中的子组件上
}

void CVWnd_Container::OnShowWindow (BOOL bShow, UINT nStatus)
{
    CWnd::OnShowWindow (bShow, nStatus);

    if (m_pEventListener != NULL)
        m_pEventListener->OnShow (bShow);
}

BOOL CVWnd_Container::OnCommand (WPARAM wParam, LPARAM lParam)
{
    if (lParam == 0)  // 不来自组件?
    {
        if (m_pEventListener != NULL && m_pEventListener->OnCommand ((INT)(DWORD)LOW_WORD (wParam)))
            return TRUE;
    }

    return CWnd::OnCommand (wParam, lParam);
}

//-----------------------------------------------------------------------------------------

IMPLEMENT_DYNAMIC (CVWnd_WinForm, CVWnd_Container)

BEGIN_MESSAGE_MAP (CVWnd_WinForm, CVWnd_Container)
    //{{AFX_MSG_MAP(CVWnd_WinForm)
    ON_WM_QUERYENDSESSION ()
    ON_WM_CLOSE ()
    ON_WM_DESTROY ()
    ON_WM_SHOWWINDOW ()
    ON_WM_SETTEXT ()
    ON_WM_ACTIVATE ()
    ON_WM_SIZE ()
    ON_WM_NCACTIVATE ()
    ON_WM_MENUSELECT ()
    ON_WM_GETMINMAXINFO ()
    ON_MESSAGE (MWM_NOTIFY_ICON, OnIconOper)
    ON_MESSAGE (DM_GETDEFID, OnGetDefButtonID)
    ON_MESSAGE (MWM_PRE_FILTER_INPUT_MSG, OnPreFilterInputMessage)
#ifndef _VOL_FOR_UI_DESIGNER  // 不为编译布局设计器专用版本?
    ON_MESSAGE (MWM_ON_CURRENT_CHILD_TAB_CHANGED, OnCurrentChildTabChanged)
#endif
    //}}AFX_MSG_MAP
END_MESSAGE_MAP ()

CVWnd_WinForm::CVWnd_WinForm ()
{
    m_hDialog = NULL;
    m_blpIsAlreadyActivated = FALSE;
    m_blpActivateProcessing = FALSE;
    m_blpHelpOpened = FALSE;
    m_nDefButtonID = 0;
    m_blpProcessEnterPressMessage = FALSE;

    m_pWndMenu = NULL;
    m_hTrayIcon = NULL;
    m_npBorderType = 2;  // 默认为普通固定边框
    m_blpControlBox = TRUE;
    m_blpMaxBtn = FALSE;
    m_blpMinBtn = TRUE;
    m_npWinPosOnScreen = 0;  // 默认为通常显示
    m_blpMoveSysMenuItemEnabled = TRUE;
    m_hIcon = ::LoadIcon (g_objVolApp.GetInstanceHandle (), MAKEINTRESOURCE (_VOL_USER_APP_ICON_RES_ID));
    m_blpEscCloseWindow = TRUE;
    m_npHelpContext = 0;
    m_blpHitMove = FALSE;
    m_npWinShape = 0;
    m_blpTopmost = FALSE;
    m_blpKeepTitleBarActive = FALSE;
}

#ifndef _VOL_FOR_UI_DESIGNER  // 不为编译布局设计器专用版本?

LRESULT CVWnd_WinForm::OnCurrentChildTabChanged (WPARAM wParam, LPARAM lParam)
{
    if (wParam != NULL && lParam == _VOL_MSG_ACK)
    {
        ::ProcessCurrentChildTabChanged ((HWND)wParam);
        return _VOL_MSG_ACK;
    }

    return 0;
}

#endif

BOOL CVWnd_WinForm::OnNcActivate (BOOL bActive)
{
    if (m_hDialog == NULL && bActive == FALSE && m_blpKeepTitleBarActive)
        return (BOOL)DefWindowProc (WM_NCACTIVATE, IsWindowEnabled (), 0L);
    else
        return CVWnd_Container::OnNcActivate (bActive);
}

BOOL CVWnd_WinForm::OnQueryEndSession ()
{
    if (CVWnd_Container::OnQueryEndSession ()== FALSE)
        return FALSE;

    if (m_hDialog != NULL)
        return TRUE;

    return (BOOL)OnBeforeQueryEndSession ();
}

void CVWnd_WinForm::OnClose ()
{
    if (m_hDialog != NULL || OnBeforeClose ())
        CVWnd_Container::OnClose ();
}

BOOL_P CVWnd_WinForm::OnBeforeQueryEndSession ()
{
    if (m_pEventListener != NULL)
        return GetWinFormEventListener ()->OnBeforeClose ();
    else
        return TRUE;
}

BOOL_P CVWnd_WinForm::OnBeforeClose ()
{
    if (m_pEventListener != NULL)
    {
        const HWND hWnd = m_hWnd;

        if (GetWinFormEventListener ()->OnBeforeWindowClose ()== FALSE || ::IsWindow (hWnd) == FALSE)
            return FALSE;

        if (GetWinFormEventListener ()->OnBeforeClose ()== FALSE || ::IsWindow (hWnd) == FALSE)
            return FALSE;
    }

    return TRUE;
}

void CVWnd_WinForm::SetWndMenu (CRefVolMenu* pNewWndMenu)
{
    if (pNewWndMenu != m_pWndMenu)
    {
        CRefVolMenu* pOldWndMenu = m_pWndMenu;
        m_pWndMenu = pNewWndMenu;

        if (pNewWndMenu != NULL)
            pNewWndMenu->AddRef ();

        if (pOldWndMenu != NULL)
        {
            pOldWndMenu->BindWindow (NULL, FALSE);  // 解除先前的绑定
            pOldWndMenu->Release ();
        }
    }

    if (m_hWnd != NULL)
    {
        const HWND hWnd = (m_hDialog != NULL ? m_hDialog : m_hWnd);
        ::SetMenu (hWnd, pNewWndMenu->SafeGetMenu ());

        if (m_pWndMenu != NULL)
            m_pWndMenu->BindWindow (hWnd, TRUE);
    }
}

void CVWnd_WinForm::CloseWinForm (const INT nResult)
{
    if (m_hDialog != NULL)  // 本窗口以对话框的方式显示?
        ::PostMessage (m_hDialog, MWM_NOTIFY_ENVELOPE_DIALOG, VEDN_CLOSE_DIALOG, nResult);
    else
        PostMessage (WM_CLOSE);
}

//   在创建窗口前本方法会被提前调用,此时需要根据用户所设置的相关属性将pCreateParam->m_dwStyle和
// pCreateParam->m_dwExStyle设置完整,以便调用方据此将窗口的用户区尺寸转换到窗口尺寸.
void CVWnd_WinForm::OnPrepareWindowStyle (CVolNativeWndCreateParam* pCreateParam)
{
#ifndef _VOL_FOR_UI_DESIGNER  // 不为编译布局设计器专用版本?
    if (IsEmptyStr (pCreateParam->m_szCaption) && m_blpControlBox)
        pCreateParam->m_szCaption = _T_VOL_USER_APP_NAME;

    // if (m_blpTransparent)
        // pCreateParam->m_dwExStyle |= WS_EX_TRANSPARENT;
#endif

    pCreateParam->m_dwStyle |= WS_CLIPCHILDREN;

    if (m_npBorderType != 0)
    {
        if (IsEmptyStr (pCreateParam->m_szCaption) == FALSE || m_blpControlBox)
            pCreateParam->m_dwStyle |= (WS_CAPTION | WS_BORDER);

        if (m_blpControlBox)
            pCreateParam->m_dwStyle |= (WS_SYSMENU | WS_BORDER);

    #ifndef _VOL_FOR_UI_DESIGNER  // 不为编译布局设计器专用版本?
        pCreateParam->m_dwStyle |= WS_DLGFRAME;
    #else
        pCreateParam->m_dwExStyle |= WS_EX_DLGMODALFRAME;
    #endif
    }

    switch (m_npBorderType)
    {
    case 1:  // 普通可调边框
        pCreateParam->m_dwStyle |= WS_THICKFRAME;
        break;
    case 2:  // 普通固定边框
        break;
    case 3:  // 窄标题可调边框
        pCreateParam->m_dwStyle |= WS_THICKFRAME;
    case 4:  // 窄标题固定边框
        pCreateParam->m_dwExStyle |= WS_EX_TOOLWINDOW;
        break;
    case 5:  // 凹入式可调边框
        pCreateParam->m_dwStyle |= WS_THICKFRAME;
    case 6:  // 凹入式固定边框
        pCreateParam->m_dwExStyle |= WS_EX_CLIENTEDGE;
        break;
    default:
        ASSERT (m_npBorderType == 0);  // 无边框
        break;
    }

    if (m_blpMaxBtn)
        pCreateParam->m_dwStyle |= WS_MAXIMIZEBOX;
    if (m_blpMinBtn)
        pCreateParam->m_dwStyle |= WS_MINIMIZEBOX;

#ifndef _VOL_FOR_UI_DESIGNER  // 不为编译布局设计器专用版本?
    switch (m_npWinPosOnScreen)
    {
    case 2:  // 最小化
        pCreateParam->m_dwStyle |= WS_MINIMIZE;
        break;
    case 3:  // 最大化
        pCreateParam->m_dwStyle |= WS_MAXIMIZE;
        break;
    }

    if (pCreateParam->m_pParentWnd != NULL &&  // 不为顶层窗口?
            (pCreateParam->m_dwStyle & (WS_POPUP | WS_CHILD)) == 0)  // 没有指定弹出或子窗口风格?
    {
        // 默认使用弹出窗口风格
        pCreateParam->m_dwStyle |= WS_POPUP;
        pCreateParam->m_dwStyle &= ~WS_CHILD;
    }
#else
    ASSERT (pCreateParam->m_pParentWnd != NULL);  // 此时必定有父窗口

    pCreateParam->m_dwStyle &= ~WS_THICKFRAME;
    pCreateParam->m_dwStyle |= WS_CHILD;
#endif
}

BOOL CVWnd_WinForm::CreateControl (CVolNativeWndCreateParam* pCreateParam)
{
    // 窗口风格相关的参数已经在先前的OnPrepareWindowStyle调用中被处理

    m_hDialog = NULL;

#ifndef _VOL_FOR_UI_DESIGNER  // 不为编译布局设计器专用版本?
    const HWND hParentWnd = pCreateParam->m_pParentWnd->GetSafeHwnd ();
    if (hParentWnd == NULL || (pCreateParam->m_dwStyle & WS_POPUP) != 0)  // 为顶层或弹出窗口?
    {
        ASSERT ((pCreateParam->m_dwStyle & WS_CHILD) == 0);

        // 获得用作获取当前显示器的窗口句柄
        HWND hMonitorWnd = hParentWnd;
        if (hMonitorWnd == NULL)  // 没有指定父窗口?
        {
            // 去使用程序主窗口
            CWinApp* pWinApp = AfxGetApp ();
            if (pWinApp != NULL)
                hMonitorWnd = pWinApp->m_pMainWnd->GetSafeHwnd ();
        }

        RECT rtMonitor;
        GetMonitorRect (hMonitorWnd, &rtMonitor);

        if (m_npWinPosOnScreen == 1)  // 居中显示?
        {
            pCreateParam->m_nLeft = (rtMonitor.left + rtMonitor.right - pCreateParam->m_nWidth) / 2;
            pCreateParam->m_nTop = (rtMonitor.top + rtMonitor.bottom - pCreateParam->m_nHeight) / 2;
        }
        else
        {
            // 否则确保当前窗口与父窗口或主窗口位于同一个显示器上
            pCreateParam->m_nLeft += rtMonitor.left;
            pCreateParam->m_nTop += rtMonitor.top;
        }
    }
#endif

    //-----------------------------------------------------------------------------------------

    if (pCreateParam->m_blModalDialog)  // 为创建模态对话框?
    {
        CVolEnvelopeDialog dlg (this, pCreateParam);

        CWinApp* pWinApp = AfxGetApp ();
        if (pWinApp != NULL && pWinApp->m_pMainWnd == NULL)
            pWinApp->m_pMainWnd = &dlg;

        pCreateParam->m_nDialogResult = dlg.ShowModal ();

        if (pWinApp != NULL && pWinApp->m_pMainWnd == &dlg)
            pWinApp->m_pMainWnd = NULL;

        return TRUE;
    }
    
    return CreateWinForm (pCreateParam);
}

BOOL CVWnd_WinForm::CreateWinForm (CVolNativeWndCreateParam* pCreateParam)
{
    ASSERT (pCreateParam->m_blModalDialog == FALSE);

    const HWND hParentWnd = pCreateParam->m_pParentWnd->GetSafeHwnd ();

    HMENU hMenu;
    BOOL_P blpIsMenu;
    if (hParentWnd == NULL || (pCreateParam->m_dwStyle & WS_POPUP) != 0)  // 为顶层或弹出窗口?
    {
        ASSERT ((pCreateParam->m_dwStyle & WS_CHILD) == 0);

        SetWndMenu (pCreateParam->m_pVolMenu);  // 记录当前所使用的菜单
        hMenu = m_pWndMenu->SafeGetMenu ();
        blpIsMenu = TRUE;
    }
    else
    {
        ASSERT ((pCreateParam->m_dwStyle & WS_CHILD) != 0);

        MSAFE_RELEASE (m_pWndMenu)
        hMenu = (HMENU)pCreateParam->m_nID;
        blpIsMenu = FALSE;
    }

    if (CWnd::CreateEx (pCreateParam->m_dwExStyle,
            VolRegisterWndClass (m_strWinClassName.IsEmpty () ? _T ("VolWinForm") : m_strWinClassName.GetText ()),
            pCreateParam->m_szCaption, pCreateParam->m_dwStyle, pCreateParam->m_nLeft, pCreateParam->m_nTop,
            pCreateParam->m_nWidth, pCreateParam->m_nHeight, hParentWnd, hMenu) == FALSE)
    {
        return FALSE;
    }

    if (blpIsMenu && hMenu != NULL)
        pCreateParam->m_pVolMenu->BindWindow (m_hWnd, TRUE);

    if (m_hDialog == NULL)  // 不为对话框?
    {
        //   标志本窗口需要过滤其自身及其下属子窗口的输入信息. 为对话框时由于其有自己的消息循环,
        // 所以改从对话框的PreTranslateMessage中去调用OnPreFilterInputMessage.
        CVolAppInstance::sSetWndNeedPreFilterInputMessage (m_hWnd);
    }

    //--------------------------------------------------------------------------------

    UpdateWinBox ();
    SetWinIcon (m_hIcon);
    if (m_npWinShape != 0)
        SetWinShape (m_npWinShape);

    ASSERT (pCreateParam->m_hFont != NULL);  // 接口规范定义
    SendMessage (WM_SETFONT, (WPARAM)pCreateParam->m_hFont, MAKELPARAM (TRUE, 0));

#ifndef _VOL_FOR_UI_DESIGNER  // 不为编译布局设计器专用版本?
    CWnd* pWnd = (m_hDialog != NULL ? CWnd::FromHandle (m_hDialog) : this);

    if (m_blpTopmost)
        ::SetWindowPos (pWnd->m_hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);

    if (m_blpMoveSysMenuItemEnabled == FALSE)
    {
        CMenu* pMenu = pWnd->GetSystemMenu (FALSE);
        if (pMenu != NULL)
            pMenu->DeleteMenu (SC_MOVE, MF_BYCOMMAND);
    }

    SetWinPosOnScreen (m_npWinPosOnScreen);
#endif

    return TRUE;
}

LRESULT CVWnd_WinForm::OnPreFilterInputMessage (WPARAM wParam, LPARAM lParam)
{
#ifndef _VOL_FOR_UI_DESIGNER  // 不为编译布局设计器专用版本?
    if (lParam == _VOL_MSG_ACK)  // 调用格式正确?
    {
        ASSERT (wParam != NULL);

        if (OnPreFilterInputMessage ((MSG*)wParam))
            return _VOL_MSG_ACK;
    }
#endif

    return 0;
}

#ifndef _VOL_FOR_UI_DESIGNER  // 不为编译布局设计器专用版本?

BOOL_P CVWnd_WinForm::OnPreFilterInputMessage (MSG* pMsg)
{
    if (m_pEventListener != NULL)
    {
        const HWND hWnd = m_hWnd;

        if (GetWinFormEventListener ()->OnPreTranslateMessage (pMsg) || ::IsWindow (hWnd) == FALSE)
            return TRUE;
    }

    CVWnd_Container::ProcessInputMessage (pMsg);

    //--------------------------------------------------------------------------

    const UINT_P upKeyCode = GetKeyCodeFromMessage (pMsg);
    BOOL_P blpCheckDialogMessage = (pMsg->message != WM_CHAR);  // 某些键的WM_CHAR消息传递到IsDialogMessage可能会导致其死锁.

    if (upKeyCode != KYV_NULL)
    {
        if (::IsWindowEnabled (pMsg->hwnd) == FALSE)
            return FALSE;

        if (upKeyCode == KYV_ESC)
        {
            if (m_blpEscCloseWindow || m_hDialog != NULL)
            {
                CloseWinForm (-1);
                return TRUE;
            }

            if (m_hDialog == NULL)  // 不为对话框模式?
                blpCheckDialogMessage = FALSE;  // 避免IsDialogMessage ESC键错误关闭窗口
        }
    }

    // 处理随意移动
    if (m_blpHitMove && pMsg->message == WM_LBUTTONDOWN)
    {
        if (pMsg->hwnd == m_hWnd)
        {
            ::PostMessage ((m_hDialog != NULL ? m_hDialog : m_hWnd), 161, 2, 0);
            return TRUE;
        }

        const DWORD dwCode = (DWORD)::SendMessage (pMsg->hwnd, WM_GETDLGCODE, pMsg->wParam, (LPARAM)pMsg);
        if ((dwCode & DLGC_STATIC) != 0)  // 为静态组件?
        // if ((dwCode & (DLGC_WANTALLKEYS | DLGC_WANTARROWS | DLGC_WANTCHARS | DLGC_WANTMESSAGE | DLGC_WANTTAB |  // 当前组件不需要处理任何按键?
        //         DLGC_BUTTON | DLGC_DEFPUSHBUTTON | DLGC_UNDEFPUSHBUTTON | DLGC_RADIOBUTTON)) == 0)  // 当前组件不为按钮或单选框?
        {
            ::PostMessage ((m_hDialog != NULL ? m_hDialog : m_hWnd), 161, 2, 0);
            return TRUE;
        }
    }

    // 处理键盘消息
    if (upKeyCode != KYV_NULL)
    {
        if (upKeyCode == (KYV_C | KYS_CONTROL) || upKeyCode == (KYV_V | KYS_CONTROL) || upKeyCode == (KYV_X | KYS_CONTROL) || upKeyCode == (KYV_A | KYS_CONTROL))
        {
            TCHAR acClass [6];
            if (::GetClassName (pMsg->hwnd, acClass, NUM_ELEMENTS_OF (acClass)) > 0 && _tcscmp (acClass, _T ("Edit")) == 0)
                return FALSE;
        }

        // 处理快捷键
        if (m_pWndMenu != NULL && m_pWndMenu->TranslateMenuAccelerator (m_hWnd, pMsg))
            return TRUE;

        CVWnd_OcxControl::SPEC_KEY_TYPE enSpecKey = CVWnd_OcxControl::SKT_INVALID;

        switch (upKeyCode)
        {
        case KYV_F1:  {
            if (m_strF1HelpFileName.IsEmpty ())  // 未提供帮助文件?
                break;

            const TCHAR* szHelpFileName = m_strF1HelpFileName.GetText ();

            // 如果为相对路径,则基于程序可执行文件当前所处目录将其转换为绝对路径.
            CVolString strInstancePath;
            CVolMem memBuf;
            if (IsAbsPath (szHelpFileName) == FALSE && GetInstancePath (g_objVolApp.GetInstanceHandle (), strInstancePath))
                szHelpFileName = LinkOSPath (strInstancePath.GetText (), szHelpFileName, memBuf, OS_PATH_CHAR);

            if (IsOSFileExist (szHelpFileName))
            {
                m_blpHelpOpened = TRUE;  // 设置帮助文件被打开标志

                if (m_npHelpContext == 0)
                    ::WinHelp (m_hWnd, szHelpFileName, HELP_FINDER, 0);
                else
                    ::WinHelp (m_hWnd, szHelpFileName, HELP_CONTEXT, (INT)m_npHelpContext);

                return TRUE;
            }
            break;  }

        case (KYS_SHIFT | KYV_TAB):
        case KYV_TAB:  {
            TCHAR acClass [6];
            if (::GetClassName (pMsg->hwnd, acClass, NUM_ELEMENTS_OF (acClass)) > 0 && _tcscmp (acClass, _T ("Edit")) == 0)
            {
                const HWND hNextWnd = ::GetNextDlgTabItem (m_hWnd, pMsg->hwnd, (upKeyCode != KYV_TAB));
                if (hNextWnd != NULL)
                {
                    ::SetFocus (hNextWnd);
                    return TRUE;
                }
            }
            enSpecKey = CVWnd_OcxControl::SKT_TAB;
            break;  }

        case KYV_LEFT:
        case KYV_RIGHT:
        case KYV_UP:
        case KYV_DOWN:
            enSpecKey = CVWnd_OcxControl::SKT_ARROW;
            break;

        case KYV_ENTER:
            enSpecKey = CVWnd_OcxControl::SKT_ENTER;
            if (m_nDefButtonID == 0)  // 没有默认按钮?
                blpCheckDialogMessage = FALSE;
            break;
        }

        if (enSpecKey != CVWnd_OcxControl::SKT_INVALID)
        {
            const CRuntimeClass* pRuntimeClass = RUNTIME_CLASS (CVWnd_OcxControl);

            HWND hWnd = pMsg->hwnd;
            while (hWnd != NULL && hWnd != m_hWnd)
            {
                CWnd* pWnd = CWnd::FromHandlePermanent (hWnd);
                if (pWnd != NULL && pWnd->IsKindOf (pRuntimeClass) && ((CVWnd_OcxControl*)pWnd)->IsNeedSpecKey (enSpecKey))
                {
                    blpCheckDialogMessage = FALSE;
                    break;
                }

                hWnd = ::GetParent (hWnd);
            }
        }
    }

    if (blpCheckDialogMessage)
    {
        m_blpProcessEnterPressMessage = (upKeyCode == KYV_ENTER);
        const BOOL_P blpIsDialogMessage = IsDialogMessage (pMsg);  // 处理组件控制
        m_blpProcessEnterPressMessage = FALSE;

        if (blpIsDialogMessage)  // 处理组件控制
            return TRUE;
    }

    return FALSE;
}

#endif

BOOL CVWnd_WinForm::OnCommand (WPARAM wParam, LPARAM lParam)
{
    if (lParam == 0)  // 不来自组件?
    {
        if (OnProcessCommand ((INT)(DWORD)LOW_WORD (wParam)))
            return TRUE;
    }
    else
    {
        if (m_blpProcessEnterPressMessage)  // 当前为处理回车按键所触发的?
        {
            // Windows的IsDialogMessage在用户按下回车时会错误触发类似AutoCheckBox的命令事件,在此将其滤除.
            TCHAR buf [64];
            buf [0] = '\0';
            if (::GetClassName ((HWND)lParam, buf, NUM_ELEMENTS_OF (buf) - 1) > 0 &&
                    _tcsicmp (buf, _T ("BUTTON")) == 0 &&
                    ((UINT)GetWindowLong ((HWND)lParam, GWL_STYLE) & 0x0000000FL) != BS_DEFPUSHBUTTON)
            {
                return TRUE;
            }
        }
    }

    return CWnd::OnCommand (wParam, lParam);  // 跳过CVWnd_Container中的处理
}

BOOL_P CVWnd_WinForm::OnProcessCommand (INT nCommandId)
{
    return (m_pEventListener != NULL && GetWinFormEventListener ()->OnCommand (nCommandId));
}

void CVWnd_WinForm::OnDestroy ()
{
    SetTrayIcon (NULL, _T (""));
    m_blpIsAlreadyActivated = FALSE;

    if (m_blpHelpOpened)  // 帮助文件已经被打开?
    {
        ::WinHelp (m_hWnd, NULL, HELP_QUIT, 0L);
        m_blpHelpOpened = FALSE;
    }

    if (m_hDialog != NULL)
        ::PostMessage (m_hDialog, MWM_NOTIFY_ENVELOPE_DIALOG, VEDN_DESTROY, 0);

    CVWnd_Container::OnDestroy ();
}

void CVWnd_WinForm::OnShowWindow (BOOL bShow, UINT nStatus)
{
    if (m_hDialog != NULL && nStatus == 0)  // 存在封套对话框且由于调用ShowWindow方法导致的?
        ::SendMessage (m_hDialog, MWM_NOTIFY_ENVELOPE_DIALOG, VEDN_SHOW_WINDOW, (LPARAM)bShow);

    CVWnd_Container::OnShowWindow (bShow, nStatus);
}

int CVWnd_WinForm::OnSetText (LPCTSTR szNewText)
{
    if (m_hDialog != NULL)
        ::SendMessage (m_hDialog, MWM_NOTIFY_ENVELOPE_DIALOG, VEDN_SET_TEXT, (LPARAM)szNewText);
    else
        UpdateWinBox ();

    return (int)Default ();
}

void CVWnd_WinForm::OnActivate (UINT nState, CWnd* pWndOther, BOOL bMinimized)
{
    CVWnd_Container::OnActivate (nState, pWndOther, bMinimized);

    if (m_pEventListener == NULL)
        return;

    // 避免重复进入
    if (m_blpActivateProcessing)
        return;
    m_blpActivateProcessing = TRUE;

    do
    {
        if (nState != WA_INACTIVE)  // 为激活?
        {
            if (m_blpIsAlreadyActivated == FALSE)
            {
                m_blpIsAlreadyActivated = TRUE;
                const HWND hWnd = m_hWnd;

                GetWinFormEventListener ()->OnFirstActive ();

                if (::IsWindow (hWnd) == FALSE)  // 处理方法导致本窗口对象被销毁?
                    break;
            }

            GetWinFormEventListener ()->OnActive ();
        }
        else
        {
            GetWinFormEventListener ()->OnInactive ();
        }
    }
    while (FALSE);

    m_blpActivateProcessing = FALSE;
}

LRESULT CVWnd_WinForm::OnIconOper (WPARAM, LPARAM lParam)
{
    if (m_pEventListener != NULL)
    {
        const INT nType = (lParam == WM_LBUTTONUP ? 1 :
                lParam == WM_LBUTTONDBLCLK ? 2 :
                (lParam == WM_RBUTTONUP || lParam == WM_CONTEXTMENU) ? 3 :
                0);

        if (nType != 0)
            GetWinFormEventListener ()->OnIconOper (nType);
    }

    return 0;
}

void CVWnd_WinForm::UpdateWinBox ()
{
    if (m_hWnd == NULL)
        return;

    CWnd* pWnd = (m_hDialog != NULL ? CWnd::FromHandle (m_hDialog) : this);
    DWORD dwNewStyle = 0;

    if (m_npBorderType != 0)
    {
        if (m_blpControlBox)
        {
            dwNewStyle |= (WS_CAPTION | WS_SYSMENU | WS_BORDER);
        }
        else
        {
            CString strCaption;
            pWnd->GetWindowText (strCaption);

            if (strCaption.IsEmpty ()== FALSE && strCaption != _T_VOL_USER_APP_NAME)
                dwNewStyle |= (WS_CAPTION | WS_BORDER);
        }

    #ifndef _VOL_FOR_UI_DESIGNER  // 不为编译布局设计器专用版本?
        dwNewStyle |= WS_DLGFRAME;
    #endif
    }

    if (m_blpMaxBtn)
        dwNewStyle |= WS_MAXIMIZEBOX;
    if (m_blpMinBtn)
        dwNewStyle |= WS_MINIMIZEBOX;

    const DWORD dwOldStyle = (pWnd->GetStyle () & (WS_CAPTION | WS_SYSMENU | WS_BORDER | WS_MAXIMIZEBOX | WS_MINIMIZEBOX));

    if (dwOldStyle != dwNewStyle)
        pWnd->ModifyStyle (dwOldStyle, dwNewStyle, (SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED | SWP_DRAWFRAME));
}

void CVWnd_WinForm::SetBorderType (const INT_P npBorderType)
{
    m_npBorderType = CLIP (npBorderType, 0, 6);

    if (m_hWnd == NULL)
        return;

    DWORD dwExStyle = 0, dwStyle = 0;
    if (m_npBorderType != 0)
    {
    #ifndef _VOL_FOR_UI_DESIGNER  // 不为编译布局设计器专用版本?
        dwStyle |= WS_DLGFRAME;
    #else
        dwExStyle |= WS_EX_DLGMODALFRAME;
    #endif
    }

    switch (m_npBorderType)
    {
    case 1:  // 普通可调边框
        dwStyle |= WS_THICKFRAME;
        break;
    case 2:  // 普通固定边框
        break;
    case 3:  // 窄标题可调边框
        dwStyle |= WS_THICKFRAME;
    case 4:  // 窄标题固定边框
        dwExStyle |= WS_EX_TOOLWINDOW;
        break;
    case 5:  // 凹入式可调边框
        dwStyle |= WS_THICKFRAME;
    case 6:  // 凹入式固定边框
        dwExStyle |= WS_EX_CLIENTEDGE;
        break;
    default:
        ASSERT (m_npBorderType == 0);  // 无边框
        break;
    }

#ifdef _VOL_FOR_UI_DESIGNER  // 为编译布局设计器专用版本?
    dwStyle &= ~WS_THICKFRAME;
#endif

    CWnd* pWnd = (m_hDialog != NULL ? CWnd::FromHandle (m_hDialog) : this);

    pWnd->ModifyStyleEx ((WS_EX_TOOLWINDOW | WS_EX_CLIENTEDGE | WS_EX_DLGMODALFRAME), dwExStyle,
            (SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED | SWP_DRAWFRAME));
    pWnd->ModifyStyle ((WS_DLGFRAME | WS_THICKFRAME), dwStyle,
            (SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED | SWP_DRAWFRAME));

    UpdateWinBox ();
}

void CVWnd_WinForm::SetControlBox (const BOOL_P blpControlBox)
{
    m_blpControlBox = blpControlBox;
    UpdateWinBox ();
}

void CVWnd_WinForm::SetMaxBox (const BOOL_P blpMaxBtn)
{
    m_blpMaxBtn = blpMaxBtn;
    UpdateWinBox ();
}

void CVWnd_WinForm::SetMinBox (const BOOL_P blpMinBtn)
{
    m_blpMinBtn = blpMinBtn;
    UpdateWinBox ();
}

INT_P CVWnd_WinForm::GetWinPosOnScreen () const
{
#ifndef _VOL_FOR_UI_DESIGNER  // 不为编译布局设计器专用版本?
    if (m_hWnd != NULL)
        return ::GetCurrentWinPosOnScreen (m_hDialog != NULL ? CWnd::FromHandle (m_hDialog) : this);
#endif

    return m_npWinPosOnScreen;
}

void CVWnd_WinForm::SetWinPosOnScreen (const INT_P npWinPosOnScreen)
{
    m_npWinPosOnScreen = CLIP (npWinPosOnScreen, 0, 3);

#ifndef _VOL_FOR_UI_DESIGNER  // 不为编译布局设计器专用版本?
    if (m_hWnd == NULL)
        return;

    CWnd* pWnd = (m_hDialog != NULL ? CWnd::FromHandle (m_hDialog) : this);

    if (pWnd->IsWindowVisible ())
        ::SetWinPosOnScreen (pWnd, m_npWinPosOnScreen);
#endif
}

void CVWnd_WinForm::EnableMoveSysMenuItem (const BOOL_P blpMoveSysMenuItemEnabled)
{
    m_blpMoveSysMenuItemEnabled = blpMoveSysMenuItemEnabled;

#ifndef _VOL_FOR_UI_DESIGNER  // 不为编译布局设计器专用版本?
    if (m_hWnd == NULL)
        return;

    CWnd* pWnd = (m_hDialog != NULL ? CWnd::FromHandle (m_hDialog) : this);

    if (m_blpMoveSysMenuItemEnabled == FALSE)
    {
        CMenu* pMenu = pWnd->GetSystemMenu (FALSE);
        if (pMenu != NULL)
            pMenu->DeleteMenu (SC_MOVE, MF_BYCOMMAND);
    }
    else
        pWnd->GetSystemMenu (TRUE);
#endif
}

void CVWnd_WinForm::SetWinIcon (const HICON hIcon)
{
    m_hIcon = hIcon;
    if (m_hIcon == NULL)
        m_hIcon = ::LoadIcon (g_objVolApp.GetInstanceHandle (), MAKEINTRESOURCE (_VOL_USER_APP_ICON_RES_ID));

    if (m_hWnd == NULL || m_hIcon == NULL)
        return;

    const HWND hWnd = (m_hDialog != NULL ? m_hDialog : m_hWnd);
    ::SendMessage (hWnd, WM_SETICON, FALSE, (LPARAM)m_hIcon);
    ::SendMessage (hWnd, WM_SETICON, TRUE, (LPARAM)m_hIcon);
}

void CVWnd_WinForm::SetWinShape (const INT_P npWinShape)
{
    m_npWinShape = CLIP (npWinShape, 0, 2);

    if (m_hWnd == NULL)
        return;

    CWnd* pWnd = (m_hDialog != NULL ? CWnd::FromHandle (m_hDialog) : this);

    CRgn rgn;
    if (m_npWinShape != 0)  // 不为矩形外形?
    {
        CRect rt;
        pWnd->GetWindowRect (&rt);
        INT cx = rt.Width ();
        INT cy = rt.Height ();

        switch (m_npWinShape)
        {
        case 1:  // 椭圆
            rgn.CreateEllipticRgn (0, 0, cx, cy);
            break;

        case 2:  {  // 圆角矩形
            const INT nRoundedCornerSize = MIN (cx / 5, cy / 5);
            rgn.CreateRoundRectRgn (0, 0, cx, cy, nRoundedCornerSize, nRoundedCornerSize);
            break;  }

        DEFAULT_FAIL
        }
    }

    if (pWnd->SetWindowRgn ((HRGN)rgn, TRUE))  // 设置成功?
        rgn.Detach ();  // 此时区域句柄由Windows系统负责释放
}

void CVWnd_WinForm::OnSize (UINT nType, int cx, int cy)
{
    CVWnd_Container::OnSize (nType, cx, cy);

    if (m_npWinShape != 0)
        SetWinShape (m_npWinShape);
}

void CVWnd_WinForm::SetTopmost (const BOOL_P blpTopmost)
{
    m_blpTopmost = blpTopmost;

#ifndef _VOL_FOR_UI_DESIGNER  // 不为编译布局设计器专用版本?
    if (m_hWnd == NULL)
        return;

    ::SetWindowPos ((m_hDialog != NULL ? m_hDialog : m_hWnd), (m_blpTopmost ? HWND_TOPMOST : HWND_NOTOPMOST),
            0, 0, 0, 0, (SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE));
#endif
}

static HWND sFindSpecChildControl (const HWND hWnd, const INT nFindControlID)
{
    if (hWnd == NULL || ::IsWindowVisible (hWnd) == FALSE || ::IsWindowEnabled (hWnd) == FALSE)
        return NULL;

    for (HWND hWndChild = ::GetWindow (hWnd, GW_CHILD);
            hWndChild != NULL;
            hWndChild = ::GetWindow (hWndChild, GW_HWNDNEXT))
    {
        if (::GetDlgCtrlID (hWndChild) == nFindControlID)
            return hWndChild;

        const HWND hwndFound = sFindSpecChildControl (hWndChild, nFindControlID);
        if (hwndFound != NULL)
            return hwndFound;
    }

    return NULL;
}

LRESULT CVWnd_WinForm::OnGetDefButtonID (WPARAM wParam, LPARAM lParam)
{
    if (m_nDefButtonID != 0)
    {
        if (sFindSpecChildControl (m_hWnd, m_nDefButtonID) != NULL)
            return MAKELONG (m_nDefButtonID, DC_HASDEFID);
    }

    return Default ();
}

void CVWnd_WinForm::OnMenuSelect (UINT nItemID, UINT nFlags, HMENU hSysMenu)
{
    CVWnd_Container::OnMenuSelect (nItemID, nFlags, hSysMenu);
    OnProcessMenuSelect (nItemID, nFlags, hSysMenu);
}

void CVWnd_WinForm::OnProcessMenuSelect (UINT nItemID, UINT nFlags, HMENU hSysMenu)
{
    if (m_pEventListener == NULL)
        return;

    if (nFlags == 0xFFFF || (nFlags & (MF_POPUP | MF_SYSMENU)) != 0)
    {
        GetWinFormEventListener ()->OnCommandSelect (0);
    }
    else
    {
        // if (m_pWndMenu != NULL && m_pWndMenu->IsMenuItemIDExist (nItemID))
            GetWinFormEventListener ()->OnCommandSelect ((INT)nItemID);
    }
}

void CVWnd_WinForm::OnGetMinMaxInfo (MINMAXINFO* lpMMI)
{
    CVWnd_Container::OnGetMinMaxInfo (lpMMI);

    if (m_hDialog == NULL)
        OnProcessGetMinMaxInfo (lpMMI);
}

void CVWnd_WinForm::OnProcessGetMinMaxInfo (MINMAXINFO* lpMMI)
{
    ASSERT (lpMMI != NULL);

    lpMMI->ptMinTrackSize.x = lpMMI->ptMinTrackSize.y = 1;

    if (m_pEventListener != NULL)
        GetWinFormEventListener ()->OnGetMinMaxInfo (lpMMI);
}

void CVWnd_WinForm::OnInitDialog ()
{
    if (m_pEventListener != NULL)
        GetWinFormEventListener ()->OnInitDialog ();
}

//-----------------------------------------------------------------------------------------

CVolEnvelopeDialog::CVolEnvelopeDialog (CVWnd_WinForm* pwndForm, CVolNativeWndCreateParam* pCreateParam) :
        CDialog ((const TCHAR*)NULL, (pCreateParam->m_pParentWnd == NULL ? sGetModelParentWnd (): NULL)),  // 如果未指定父窗口,则使用默认父窗口.
        m_pwndForm (pwndForm),
        m_pCreateParam (pCreateParam)
{
    ASSERT (pwndForm != NULL && pCreateParam != NULL);
    
    m_blpDestroying = FALSE;
}

BEGIN_MESSAGE_MAP (CVolEnvelopeDialog, CDialog)
    //{{AFX_MSG_MAP(CVolEnvelopeDialog)
    ON_WM_QUERYENDSESSION ()
    ON_WM_CLOSE ()
    ON_WM_PAINT ()
    ON_WM_QUERYDRAGICON ()
    ON_WM_SIZE ()
    ON_WM_DESTROY ()
    ON_WM_SETFOCUS ()
    ON_WM_NCACTIVATE ()
    ON_WM_MENUSELECT ()
    ON_WM_GETMINMAXINFO ()
    ON_MESSAGE (MWM_NOTIFY_ENVELOPE_DIALOG, CVolEnvelopeDialog::OnWinFormNotifyEvent)
    //}}AFX_MSG_MAP
END_MESSAGE_MAP ()

CWnd* CVolEnvelopeDialog::sGetModelParentWnd ()
{
    CWnd* pWnd = CWnd::GetActiveWindow ();

    if (pWnd == NULL || pWnd->m_hWnd == NULL || CWnd::FromHandlePermanent (pWnd->m_hWnd) != pWnd)
        pWnd = AfxGetMainWnd ();

    if (pWnd->GetSafeHwnd ()== NULL)
        pWnd = CWnd::GetDesktopWindow ();

    return pWnd;
}

INT CVolEnvelopeDialog::ShowModal ()
{
    BYTE abtBuf [sizeof (DLGTEMPLATE) + sizeof (WORD) * 3];
    ZERO_MEM (abtBuf, sizeof (abtBuf));
    DLGTEMPLATE* pInf = (DLGTEMPLATE*)abtBuf;

    if (m_pCreateParam->m_blForceUseDialogStyle)  // 强制使用固定的对话框风格?
    {
        // 修改相应属性值
        m_pwndForm->m_npBorderType = 2;  // 普通固定边框
        m_pwndForm->m_blpControlBox = TRUE;  // 显示控制按钮
        m_pwndForm->m_blpMaxBtn = FALSE;
        m_pwndForm->m_blpMinBtn = FALSE;
        // m_pwndForm->m_npWinPosOnScreen = 1;  // 居中显示

        pInf->style = (WS_POPUP | WS_BORDER | WS_CAPTION | WS_SYSMENU | WS_VISIBLE | WS_CLIPCHILDREN | DS_CENTER | DS_MODALFRAME);
        // pInf->dwExtendedStyle = 0;
    }
    else
    {
        // 必须设置可视标记,否则在程序中存在多UI线程时可能会导致对话框不自动显示的问题.
        pInf->style = ((m_pCreateParam->m_dwStyle & ~(WS_CHILD | WS_OVERLAPPED)) |
                WS_POPUP | WS_VISIBLE | WS_CLIPCHILDREN | DS_CENTER);

        if ((pInf->style & WS_THICKFRAME) == 0)
            pInf->style |= DS_MODALFRAME;

        pInf->dwExtendedStyle = m_pCreateParam->m_dwExStyle;
    }

    InitModalIndirect (pInf, m_pCreateParam->m_pParentWnd);

    const HWND hOldFocusWnd = ::GetFocus ();
    const INT nResult = (INT)DoModal ();

    if (hOldFocusWnd != NULL && ::IsWindow (hOldFocusWnd))
        ::SetFocus (hOldFocusWnd);

    return nResult;
}

BOOL CVolEnvelopeDialog::OnInitDialog ()
{
    CDialog::OnInitDialog ();

    ASSERT (m_pwndForm != NULL);
    m_pwndForm->m_hDialog = m_hWnd;  // 将本封套对话框的句柄置入对应窗口对象中

    m_pwndForm->SetWndMenu (m_pCreateParam->m_pVolMenu);  // 记录当前所使用的菜单
    const HMENU hMenu = m_pCreateParam->m_pVolMenu->SafeGetMenu ();
    
    //------------------------------------------------------------  计算调整封套对话框的位置和尺寸

    // 获得所指定创建参数下的非客户区部分尺寸
    CMRect rtTestClient (200, 200, 300, 300);
    CMRect rtTestWin (rtTestClient);
    AdjustWindowRectEx (&rtTestWin, m_pCreateParam->m_dwStyle, (hMenu != NULL), m_pCreateParam->m_dwExStyle);

    // 获得当前窗口的客户区位置矩形及尺寸
    CMRect rtDialog (
            m_pCreateParam->m_nLeft + rtTestClient.left - rtTestWin.left,
            m_pCreateParam->m_nTop + rtTestClient.top - rtTestWin.top,
            m_pCreateParam->m_nLeft + m_pCreateParam->m_nWidth + rtTestClient.right - rtTestWin.right,
            m_pCreateParam->m_nTop + m_pCreateParam->m_nHeight + rtTestClient.bottom - rtTestWin.bottom);

    if (m_pCreateParam->m_blForceUseDialogStyle == FALSE)  // 不为强制使用固定的对话框风格?
        m_pCreateParam->CheckBorderStyle (this);

    AdjustWindowRectEx (&rtDialog, GetStyle (), (hMenu != NULL), GetExStyle ());
    MoveWindow (&rtDialog);

    //------------------------------------------------------------  设置相应属性

    if (hMenu != NULL)
    {
        ::SetMenu (m_hWnd, hMenu);

        ASSERT (m_pCreateParam->m_pVolMenu != NULL);
        m_pCreateParam->m_pVolMenu->BindWindow (m_hWnd, TRUE);
    }

    SetWindowText (m_pCreateParam->m_szCaption);

    //------------------------------------------------------------ 

    CMRect rtClient;
    GetClientRect (&rtClient);

    // 建立被封套窗口的创建参数
    CVolNativeWndCreateParam pmWinForm (this, rtClient.left, rtClient.top, rtClient.Width (), rtClient.Height (), 0xFFFF);
    pmWinForm.m_dwStyle = (WS_CHILD | WS_VISIBLE);
    pmWinForm.m_dwExStyle = WS_EX_CONTROLPARENT;
    pmWinForm.m_szCaption = m_pCreateParam->m_szCaption;
    pmWinForm.m_hFont = m_pCreateParam->m_hFont;

    if (m_pwndForm->CreateWinForm (&pmWinForm) == FALSE)
        EndDialog (-1);

    // 同步设置默认按钮ID
    if (m_pwndForm->m_nDefButtonID != 0)
        SetDefID (m_pwndForm->m_nDefButtonID);

    m_pwndForm->SetFocus ();
    m_pwndForm->OnInitDialog ();
    return FALSE;
}

LRESULT CVolEnvelopeDialog::WindowProc (UINT message, WPARAM wParam, LPARAM lParam)
{
#ifndef _VOL_FOR_UI_DESIGNER  // 不为编译布局设计器专用版本?
    LRESULT lResult = 0;
    INT nGroupNumber = -1;
    DWORD dwControlState = 0;
    if (ProcessVolSysMessage (m_hWnd, message, wParam, lParam, lResult, &nGroupNumber, &dwControlState, m_filters))
        return lResult;
#endif

    return CDialog::WindowProc (message, wParam, lParam);
}

BOOL CVolEnvelopeDialog::PreTranslateMessage (MSG* pMsg)
{
#ifndef _VOL_FOR_UI_DESIGNER  // 不为编译布局设计器专用版本?
    if (CVolAppInstance::sIsVolPreFilterInputMessage (pMsg->message) &&
            m_pwndForm->OnPreFilterInputMessage (pMsg))
    {
        return TRUE;
    }
#endif
    return FALSE;

/*  old code:
    m_pwndForm->ProcessToolTipMessage (pMsg);

    if (GetKeyCodeFromMessage (pMsg) == KYV_ENTER)
        return FALSE;

    return CDialog::PreTranslateMessage (pMsg); */
}

BOOL CVolEnvelopeDialog::OnNcActivate (BOOL bActive)
{
    if (bActive == FALSE && m_pwndForm->m_blpKeepTitleBarActive)
        return (BOOL)DefWindowProc (WM_NCACTIVATE, IsWindowEnabled (), 0L);
    else
        return CDialog::OnNcActivate (bActive);
}

BOOL CVolEnvelopeDialog::OnQueryEndSession ()
{
    if (CDialog::OnQueryEndSession ()== FALSE)
        return FALSE;

    return (BOOL)m_pwndForm->OnBeforeQueryEndSession ();
}

void CVolEnvelopeDialog::OnClose ()
{
    if (m_pwndForm->OnBeforeClose ())
        CDialog::OnClose ();
}

void CVolEnvelopeDialog::OnMenuSelect (UINT nItemID, UINT nFlags, HMENU hSysMenu)
{
    CDialog::OnMenuSelect (nItemID, nFlags, hSysMenu);
    m_pwndForm->OnProcessMenuSelect (nItemID, nFlags, hSysMenu);
}

void CVolEnvelopeDialog::OnSize (UINT nType, int cx, int cy)
{
    CDialog::OnSize (nType, cx, cy);

    if (m_pwndForm->m_hWnd != NULL)
    {
        CMRect rtClient;
        GetClientRect (&rtClient);

        m_pwndForm->MoveWindow (&rtClient);
    }
}

BOOL CVolEnvelopeDialog::OnCommand (WPARAM wParam, LPARAM lParam)
{
    if (lParam == 0)  // 不来自组件?
    {
        if (m_pwndForm->OnProcessCommand ((INT)(DWORD)LOW_WORD (wParam)))
            return TRUE;
    }

    return CDialog::OnCommand (wParam, lParam);
}

void CVolEnvelopeDialog::OnDestroy ()
{
    m_blpDestroying = TRUE;

    CWinApp* pWinApp = AfxGetApp ();
    if (pWinApp != NULL && pWinApp->m_pMainWnd == this)
        pWinApp->m_pMainWnd = NULL;

    CDialog::OnDestroy ();
}

void CVolEnvelopeDialog::OnGetMinMaxInfo (MINMAXINFO* lpMMI)
{
    CDialog::OnGetMinMaxInfo (lpMMI);
    m_pwndForm->OnProcessGetMinMaxInfo (lpMMI);
}

LRESULT CVolEnvelopeDialog::OnWinFormNotifyEvent (WPARAM wParam, LPARAM lParam)
{
    switch (wParam)
    {
    case VEDN_DESTROY:
        if (m_blpDestroying == FALSE)  // 不为本对话框被销毁时所导致的通知?
            EndDialog (-1);
        break;

    case VEDN_SHOW_WINDOW:
        ShowWindow ((BOOL)lParam ? SW_SHOW : SW_HIDE);
        break;

    case VEDN_CLOSE_DIALOG:
        EndDialog ((INT)lParam);
        break;

    case VEDN_SET_TEXT:
        SetWindowText ((LPCTSTR)lParam);
        break;
    }

    return 0;
}

void CVolEnvelopeDialog::OnSetFocus (CWnd* pOldWnd)
{
    CDialog::OnSetFocus (pOldWnd);

    m_pwndForm->SetFocus ();
}

void CVolEnvelopeDialog::OnPaint ()
{
    if (IsIconic () && m_pwndForm->m_hIcon != NULL)
    {
        CPaintDC dc (this);
        SendMessage (WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc ()), 0);

        INT cxIcon = GetSystemMetrics (SM_CXICON);
        INT cyIcon = GetSystemMetrics (SM_CYICON);
        CRect rect;
        GetClientRect(&rect);
        INT x = (rect.Width()- cxIcon + 1) / 2;
        INT y = (rect.Height()- cyIcon + 1) / 2;
    
        dc.DrawIcon (x, y, m_pwndForm->m_hIcon);
    }
    else
    {
        CDialog::OnPaint ();
    }
}

HCURSOR CVolEnvelopeDialog::OnQueryDragIcon ()
{
    return static_cast<HCURSOR>(m_pwndForm->m_hIcon);
}

//-----------------------------------------------------------------------------------------

BEGIN_MESSAGE_MAP (CVWnd_Button, CButton)
    //{{AFX_MSG_MAP(CVWnd_Button)
    ON_WM_CTLCOLOR_REFLECT ()
    ON_CONTROL_REFLECT (BN_CLICKED, OnClicked)
    ON_WM_ERASEBKGND ()
    //}}AFX_MSG_MAP
END_MESSAGE_MAP ()

void CVWnd_Button::OnConstruct ()
{
    m_clrText = RGB (0, 0, 0);
    m_clrBack = CLR_DEFAULT;
    m_brhBack.CreateSolidBrush (GetRealBackColor (m_clrBack));
}

BOOL CVWnd_Button::CreateControl (CVolNativeWndCreateParam* pCreateParam)
{
    if (CWnd::CreateEx (pCreateParam->m_dwExStyle, _T ("BUTTON"), pCreateParam->m_szCaption, pCreateParam->m_dwStyle,
            pCreateParam->m_nLeft, pCreateParam->m_nTop, pCreateParam->m_nWidth, pCreateParam->m_nHeight,
            pCreateParam->m_pParentWnd->GetSafeHwnd (), (HMENU)pCreateParam->m_nID))
    {
        if ((pCreateParam->m_dwStyle & 0x0000000FL) == BS_DEFPUSHBUTTON)  // 为默认按钮?
        {
            CWnd* pParentWnd = pCreateParam->m_pParentWnd;
            const CRuntimeClass* pRuntimeClass = RUNTIME_CLASS (CVWnd_WinForm);

            // 将默认按钮ID登记到顶层窗口中
            while (pParentWnd != NULL)
            {
                if (pParentWnd->IsKindOf (pRuntimeClass))
                {
                    ((CVWnd_WinForm*)pParentWnd)->m_nDefButtonID = pCreateParam->m_nID;
                    break;
                }

                pParentWnd = pParentWnd->GetParent ();
            }
        }

        return TRUE;
    }

    return FALSE;
}

void CVWnd_Button::SetBackColor (COLORREF clrBack)
{
    if (m_clrBack != clrBack)
    {
        m_clrBack = clrBack;
        m_brhBack.DeleteObject ();
        m_brhBack.CreateSolidBrush (GetRealBackColor (m_clrBack));

        if (m_hWnd != NULL)
            Invalidate ();
    }
}

void CVWnd_Button::OnClicked()
{
    if (m_pEventListener != NULL)
        m_pEventListener->OnClicked ();
}

HBRUSH CVWnd_Button::CtlColor (CDC* pDC, UINT nCtlColor)
{
    pDC->SetTextColor (m_clrText);
    pDC->SetBkColor (GetRealBackColor (m_clrBack));
    return m_brhBack;
}

BOOL CVWnd_Button::OnEraseBkgnd (CDC* pDC) 
{
    // 由于窗口创建时指定了 WS_CLIPCHILDREN 风格,因此此处必须清除分组框的背景.
    if ((GetButtonStyle ()& BS_GROUPBOX) != 0)
    {
        CRect rtClient;
        GetClientRect (&rtClient);
        pDC->FillRect (&rtClient, &m_brhBack);
    }

    return CButton::OnEraseBkgnd (pDC);
}

//-----------------------------------------------------------------------------------------

BEGIN_MESSAGE_MAP (CVWnd_Label, CWnd)
    //{{AFX_MSG_MAP(CVWnd_Label)
    ON_WM_PAINT ()
    ON_WM_SETFONT ()
    ON_MESSAGE (MWM_LABEL_MESSAGE, OnLabelMessage)
    ON_WM_GETDLGCODE ()
    ON_WM_MOVE ()
    //}}AFX_MSG_MAP
END_MESSAGE_MAP ()

void CVWnd_Label::OnConstruct ()
{
    m_clrText = CLR_DEFAULT;
    m_clrBack = CLR_DEFAULT;
    m_enHorzAlignMode = VHAM_LEFT;
    m_enVertAlignMode = VVAM_VCENTER;
    m_blpMutiLines = FALSE;
    m_enBackPicPaintMode = VPPM_LEFT_TOP;
    m_blpTransparent = FALSE;
}

BOOL CVWnd_Label::CreateControl (CVolNativeWndCreateParam* pCreateParam)
{
    pCreateParam->m_dwStyle &= ~WS_TABSTOP;

#ifndef _VOL_FOR_UI_DESIGNER  // 不为编译布局设计器专用版本?
    if (m_blpTransparent)
        pCreateParam->m_dwExStyle |= WS_EX_TRANSPARENT;
#endif

    return CWnd::CreateEx (pCreateParam->m_dwExStyle, VolRegisterWndClass (_T ("VolLabel")),
            pCreateParam->m_szCaption, pCreateParam->m_dwStyle,
            pCreateParam->m_nLeft, pCreateParam->m_nTop, pCreateParam->m_nWidth, pCreateParam->m_nHeight,
            pCreateParam->m_pParentWnd->GetSafeHwnd (), (HMENU)pCreateParam->m_nID);
}

void CVWnd_Label::TakeOverBackBitmap (HBITMAP hBitmap)
{
    m_bmpBack.DeleteObject ();
    m_bmpBack.Attach (hBitmap);

    RedrawLabel ();
}

UINT CVWnd_Label::OnGetDlgCode ()
{
    return DLGC_STATIC;
}

void CVWnd_Label::OnSetFont (CFont* pFont, BOOL bRedraw)
{
    LOGFONT infFont;

    if (pFont != NULL && pFont->GetLogFont (&infFont))
    {
        m_fntDraw.DeleteObject ();
        m_fntDraw.CreateFontIndirect (&infFont);

        if (bRedraw)
            RedrawLabel ();
    }
}

void CVWnd_Label::RedrawLabel ()
{
    if (m_hWnd == NULL)
        return;

#ifndef _VOL_FOR_UI_DESIGNER  // 不为编译布局设计器专用版本?
    if (m_blpTransparent)
        sRedrawWindowInsideParent (this);
#endif

    Invalidate (FALSE);
}

void CVWnd_Label::OnMove (int x, int y)
{
	CWnd::OnMove (x, y);

#ifndef _VOL_FOR_UI_DESIGNER  // 不为编译布局设计器专用版本?
    // if (m_blpTransparent)
	    // RedrawLabel ();
#endif
}

void CVWnd_Label::OnPaint ()
{
    CPaintDC dcPaint (this);

    CRect rtClipBox;
    dcPaint.GetClipBox (&rtClipBox);

    CRect rtClient;
    GetClientRect (&rtClient);

    rtClipBox &= rtClient;
    if (rtClipBox.IsRectEmpty ())
        return;

#ifndef _VOL_FOR_UI_DESIGNER  // 不为编译布局设计器专用版本?
    if (m_blpTransparent == FALSE)
#endif
        dcPaint.FillRect (&rtClipBox, &CBrush (GetRealBackColor (m_clrBack)));  // 填充底色

    VolDrawBitmap (m_enBackPicPaintMode, &dcPaint, m_bmpBack, rtClient, rtClipBox, NULL);  // 绘制底图

    CString str;
    GetWindowText (str);
    if (str.IsEmpty ())
        return;
    const TCHAR* szCaption = str;

    if (m_fntDraw.m_hObject == NULL)
        m_fntDraw.CreateStockObject (DEFAULT_GUI_FONT);

    CFont* pOldFont = dcPaint.SelectObject (&m_fntDraw);

    UINT uFormat = DT_NOCLIP;

    if (m_enHorzAlignMode == VHAM_HCENTER)
        uFormat |= DT_CENTER;
    else if (m_enHorzAlignMode == VHAM_RIGHT)
        uFormat |= DT_RIGHT;
    else
        uFormat |= DT_LEFT;

    if (m_blpMutiLines)
        uFormat |= DT_WORDBREAK | DT_VCENTER;
    else if (m_enVertAlignMode == VVAM_VCENTER)
        uFormat |= DT_VCENTER | DT_SINGLELINE;
    else if (m_enVertAlignMode == VVAM_BOTTOM)
        uFormat |= DT_BOTTOM | DT_SINGLELINE;
    else
        uFormat |= DT_TOP | DT_SINGLELINE;

    dcPaint.SetBkMode (TRANSPARENT);
    dcPaint.SetTextColor (GetRealTextColor (m_clrText));

    dcPaint.DrawText (szCaption, &rtClient, uFormat);

    dcPaint.SelectObject (pOldFont);
}

LRESULT CVWnd_Label::OnLabelMessage (WPARAM wParam, LPARAM lParam)
{
    if (m_pEventListener != NULL)
        return (LRESULT)m_pEventListener->OnLabelMsg ((INT_P)wParam, (INT_P)lParam);
    else
        return 0;
}

//-----------------------------------------------------------------------------------------

CEditSpin::CEditSpin ()
{
    m_pEdit = NULL;
    m_blpAuto = FALSE;
}

BEGIN_MESSAGE_MAP (CEditSpin, CSpinButtonCtrl)
    //{{AFX_MSG_MAP(CEditSpin)
    ON_NOTIFY_REFLECT (UDN_DELTAPOS, OnDeltapos)
    ON_WM_CTLCOLOR_REFLECT ()
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

void CEditSpin::OnDeltapos (NMHDR* pNMHDR, LRESULT* pResult) 
{
    NM_UPDOWN* pNMUpDown = (NM_UPDOWN*)pNMHDR;

    if (m_pEdit != NULL && m_pEdit->IsWindowEnabled ()&& m_pEdit->IsWindowVisible ())
    {
        if (m_blpAuto)
        {
            CString str;
            m_pEdit->GetWindowText (str);

            INT nLow, nUpper;
            GetRange (nLow, nUpper);

            INT nValue = _ttoi ((LPCTSTR)str);
            if (pNMUpDown->iDelta < 0)
            {
                nValue--;
                if (nValue < nLow || nValue > nUpper)
                    nValue = nUpper;
            }
            else
            {
                nValue++;
                if (nValue < nLow || nValue > nUpper)
                    nValue = nLow;
            }

            TCHAR buf [64];
            m_pEdit->SetWindowText (n2str (nValue, buf));
            m_pEdit->SetSel (0, -1);
            m_pEdit->SetFocus ();
        }
        else
        {
            if (m_pEdit->m_pEventListener != NULL)
                m_pEdit->m_pEventListener->OnDeltapos (pNMUpDown->iDelta < 0 ? -1 : 1);
        }
    }

    *pResult = 0;
}

HBRUSH CEditSpin::CtlColor (CDC* pDC, UINT nCtlColor)
{
    if (m_pEdit != NULL)
        return m_pEdit->CtlColor (pDC, nCtlColor);
    else
        return NULL;
}

//-----------------------------------------------------------------------------------------

BEGIN_MESSAGE_MAP (CVWnd_Edit, CEdit)
    //{{AFX_MSG_MAP(CVWnd_Edit)
    ON_CONTROL_REFLECT (EN_CHANGE, OnChange)
    ON_CONTROL_REFLECT (EN_KILLFOCUS, OnKillfocus)
    ON_CONTROL_REFLECT (EN_SETFOCUS, OnSetfocus)
    ON_WM_CTLCOLOR_REFLECT ()
    ON_WM_NCCALCSIZE ()
    ON_WM_NCHITTEST ()
    ON_MESSAGE (WM_NCPAINT, OnEditNcPaint)
    ON_WM_SETFONT ()
    ON_WM_SIZE ()
    ON_WM_MOVE ()
    ON_WM_ENABLE ()
    ON_WM_CHAR()
    ON_WM_SHOWWINDOW ()
    ON_WM_WINDOWPOSCHANGED ()
    ON_WM_KEYDOWN ()
    //}}AFX_MSG_MAP
END_MESSAGE_MAP ()

void CVWnd_Edit::OnConstruct ()
{
    m_clrText = RGB (0, 0, 0);
    m_clrBack = RGB (255, 255, 255);
    m_npMaxAllowLength = 0;
    m_blpMutiLines = FALSE;
    m_npScrollBarMode = 0;
    m_npInputMode = 0;
    m_chPassWordChar = '*';
    m_npSpinMode = 0;
    m_npSpinMin = 0;
    m_npSpinMax = 100;

    m_brhBack.CreateSolidBrush (GetRealBackColor (m_clrBack));
    m_npFontHeight = 0;
    m_blpChangedEventDisabled = FALSE;
}

void CVWnd_Edit::OnKeyDown (UINT nChar, UINT nRepCnt, UINT uFlags)
{
    // 多行编辑框在处理ESC按键时,会去向其父窗口发送WM_CLOSE消息,此处将其屏蔽掉.
    if (m_blpMutiLines && nChar == VK_ESCAPE)
        return;

    CEdit::OnKeyDown (nChar, nRepCnt, uFlags);
}

BOOL CVWnd_Edit::CreateControl (CVolNativeWndCreateParam* pCreateParam)
{
    if (m_blpMutiLines)
    {
        pCreateParam->m_dwStyle |= (ES_MULTILINE | ES_WANTRETURN | ES_AUTOVSCROLL);

        if (m_npScrollBarMode == 1 || m_npScrollBarMode == 3)
            pCreateParam->m_dwStyle |= ES_AUTOHSCROLL;

        switch (m_npScrollBarMode)
        {
        case 1:
            pCreateParam->m_dwStyle |= WS_HSCROLL;
            break;
        case 2:
            pCreateParam->m_dwStyle |= WS_VSCROLL;
            break;
        case 3:
            pCreateParam->m_dwStyle |= WS_HSCROLL | WS_VSCROLL;
            break;
        }
    }
    else
    {
        pCreateParam->m_dwStyle |= ES_AUTOHSCROLL;
    }

    switch (m_npInputMode)
    {
    case 1:
        pCreateParam->m_dwStyle |= ES_READONLY;
        break;
    case 2:
        pCreateParam->m_dwStyle |= ES_PASSWORD;
        break;
    }

    if (CWnd::CreateEx (pCreateParam->m_dwExStyle, _T ("EDIT"),
            pCreateParam->m_szCaption, pCreateParam->m_dwStyle,
            pCreateParam->m_nLeft, pCreateParam->m_nTop, pCreateParam->m_nWidth, pCreateParam->m_nHeight,
            pCreateParam->m_pParentWnd->GetSafeHwnd (), (HMENU)pCreateParam->m_nID))
    {
        SetLimitText ((UINT)m_npMaxAllowLength);

        if (m_npInputMode == 2)
            SetPasswordChar (m_chPassWordChar);

        if (m_blpMutiLines == FALSE && m_npSpinMode != 0)
        {
            const DWORD dwStyle = (WS_CHILD | (pCreateParam->m_dwStyle & (WS_DISABLED | WS_VISIBLE)));

            CRect rtPos (0, 0, 0, 0);
            if (m_spin.Create (dwStyle, rtPos, pCreateParam->m_pParentWnd, pCreateParam->m_nID + 1000) == FALSE)
            {
                m_npSpinMode = 0;
            }
            else
            {
                /* if ((pCreateParam->m_dwExStyle & (WS_EX_CLIENTEDGE | WS_EX_DLGMODALFRAME)) != 0)
                {
                    m_spin.ModifyStyleEx (0, WS_EX_STATICEDGE,
                            (SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED | SWP_DRAWFRAME));
                } */

                m_spin.m_pEdit = this;
                m_spin.m_blpAuto = (m_npSpinMode == 1);
                m_spin.SetRange ((INT)MIN (m_npSpinMin, m_npSpinMax), (INT)MAX (m_npSpinMin, m_npSpinMax));

                AdjustSpinPos ();
            }
        }

        return TRUE;
    }

    return FALSE;
}

void CVWnd_Edit::OnChange ()
{
    if (m_pEventListener != NULL && m_blpChangedEventDisabled == FALSE)
        m_pEventListener->OnChange ();
}

BOOL CVWnd_Edit::GetConvertContent (CString& str)
{
    if (m_npInputMode == 0)
        return FALSE;

    GetWindowText (str);

    TCHAR buf [128];
    const TCHAR* psResult = _T ("");
    CVolString strDateTime;
    INT_P npValue;

    switch (m_npInputMode)
    {
    case 5:  // 输入字节
        npValue = str2n (str);
        psResult = np2str (CLIP (npValue, 0, _VOL_BYTE_MAX), buf);
        break;
    case 6:  // 输入短整数
        npValue = str2n (str);
        psResult = np2str (CLIP (npValue, _VOL_SHRT_MIN, _VOL_SHRT_MAX), buf);
        break;
    case 7:  // 输入整数
        npValue = str2n (str);
        psResult = np2str (CLIP (npValue, _VOL_INT_MIN, _VOL_INT_MAX), buf);
        break;
    case 8:  // 输入长整数
        psResult = N64ToStr (StrToN64 (str), buf, NUM_ELEMENTS_OF (buf) - 1);
        break;
    case 9:  {  // 输入单精度小数
        const FLOAT flt = (FLOAT)_ttof (str);
        _stprintf (buf, _T ("%.6G"), (DOUBLE)flt);
        psResult = buf;
        break;  }
    case 10:  // 输入双精度小数
        _stprintf (buf, _T ("%.13G"), _ttof (str));
        psResult = buf;
        break;
    case 11:  {  // 输入中文日期时间
        const DOUBLE date = StrToDateTime (str, TRUE);
        if (date > _VOL_MIN_DATE + NEAR_ZERO_DOUBLE)  // 所转换日期时间值不为最小日期时间?
            psResult = DateTimeToStr (date, strDateTime, 0, TRUE);
        break;  }
    case 12:  {  // 输入英文日期时间
        const DOUBLE date = StrToDateTime (str, FALSE);
        if (date > _VOL_MIN_DATE + NEAR_ZERO_DOUBLE)  // 所转换日期时间值不为最小日期时间?
            psResult = DateTimeToStr (date, strDateTime, 0, FALSE);
        break;  }
    case 13:  // 输入十六进制整数
        psResult = UPToHexStr (Hex2UP (str), 0, buf, NUM_ELEMENTS_OF (buf) - 1);
        break;
    default:
        return FALSE;
    }

    if (m_npInputMode != 11 && m_npInputMode != 12 && _tcscmp (psResult, _T ("0")) == 0)  // 为输入数值类型数据且值文本为0?
        psResult = _T ("");  // 将其省略

    if (_tcscmp (psResult, str) != 0)
    {
        str = psResult;
        return TRUE;
    }

    return FALSE;
}

void CVWnd_Edit::OnKillfocus ()
{
    CString str;
    if (GetConvertContent (str))
    {
        INT nStartChar, nEndChar;
        GetSel (nStartChar, nEndChar);
        SetWindowText (str);
        SetSel (nStartChar, nStartChar);
    }

    SendKillFocusNativeMessage (m_hWnd, m_pEventListener);
}

void CVWnd_Edit::OnSetfocus ()
{
    SendSetFocusNativeMessage (m_hWnd, m_pEventListener);
}

void CVWnd_Edit::SetBackColor (COLORREF clrBack)
{
    if (m_clrBack != clrBack)
    {
        m_clrBack = clrBack;
        m_brhBack.DeleteObject ();
        m_brhBack.CreateSolidBrush (GetRealBackColor (m_clrBack));

        if (m_hWnd != NULL)
            RedrawWindow (NULL, NULL, RDW_ERASE | RDW_FRAME | RDW_INVALIDATE);
    }
}

void CVWnd_Edit::OnNcCalcSize (BOOL bCalcValidRects, NCCALCSIZE_PARAMS FAR* lpncsp)
{
    CEdit::OnNcCalcSize (bCalcValidRects, lpncsp);

    if (m_blpMutiLines || m_npFontHeight == 0)
        return;

    const INT nHeight = lpncsp->rgrc [0].bottom - lpncsp->rgrc [0].top;
    lpncsp->rgrc [0].top += (nHeight - (INT)m_npFontHeight) / 2;
    lpncsp->rgrc [0].bottom = lpncsp->rgrc [0].top + (INT)m_npFontHeight;
}

void CVWnd_Edit::OnSetFont (CFont* pFont, BOOL bRedraw)
{
    if (m_blpMutiLines == FALSE)
    {
        m_npFontHeight = 0;

        if (pFont->GetSafeHandle ()!= NULL)
        {
            CClientDC dc (NULL);
            CFont* pOldFont = dc.SelectObject (pFont);
        
            TEXTMETRIC tm;
            if (dc.GetTextMetrics (&tm))
            {
                m_npFontHeight = tm.tmHeight;

            #ifndef _VOL_FOR_UI_DESIGNER  // 不为编译布局设计器专用版本?
                const INT_P npMinHeight = m_npFontHeight + GetSystemMetrics (SM_CYEDGE) * 2 + 2;

                CRect rtWin;
                GetWindowRect (&rtWin);

                if (rtWin.Height ()< npMinHeight)  // 编辑框文本区域高度太小?
                    SetWindowPos (NULL, 0, 0, rtWin.Width (), (INT)npMinHeight, (SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOREPOSITION | SWP_NOZORDER));
            #endif
            }

            dc.SelectObject (pOldFont);
        }

        // 重新计算组件用户区位置
        SetWindowPos (NULL, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOREPOSITION | SWP_NOSIZE | SWP_NOZORDER);
    }

    Default ();
}

HBRUSH CVWnd_Edit::CtlColor (CDC* pDC, UINT nCtlColor) 
{
    pDC->SetTextColor (m_clrText);
    pDC->SetBkColor (GetRealBackColor (m_clrBack));
    return m_brhBack;
}

LRESULT CVWnd_Edit::OnEditNcPaint (WPARAM wParam, LPARAM lParam)
{
    if (m_blpMutiLines)
    {
        CEdit::OnNcPaint ();
    }
    else
    {
        CWindowDC dc (this);

        CRect rtClient;
        GetClientRect (rtClient);
        CRect rtWindow;
        GetWindowRect (rtWindow);
        ScreenToClient (rtWindow);
        rtClient.OffsetRect (-rtWindow.left, -rtWindow.top);
        dc.ExcludeClipRect (rtClient);

        rtWindow.OffsetRect (-rtWindow.left, -rtWindow.top);
        dc.FillRect (&rtWindow, &m_brhBack);

        CEdit::DefWindowProc (WM_NCPAINT, 1, 0);
    }

    return 0;
}

LRESULT CVWnd_Edit::OnNcHitTest (CPoint point)
{
    if (m_blpMutiLines == FALSE)
    {
        CRect rt;
        GetClientRect (rt);
        ClientToScreen (&rt);

        if (rt.PtInRect (point) == FALSE)
            return HTBORDER;
    }

    return CEdit::OnNcHitTest (point);
}

void CVWnd_Edit::OnSize (UINT nType, int cx, int cy)
{
    CEdit::OnSize (nType, cx, cy);
    AdjustSpinPos ();
}

void CVWnd_Edit::OnMove (int x, int y)
{
    CEdit::OnMove (x, y);
    AdjustSpinPos ();
}

void CVWnd_Edit::OnWindowPosChanged (WINDOWPOS* lpwndpos) 
{
    CEdit::OnWindowPosChanged (lpwndpos);

    if (m_spin.GetSafeHwnd ()!= NULL)
    {
        m_spin.SetWindowPos (this, 0, 0, 0, 0, (SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE));
        SetWindowPos (&m_spin, 0, 0, 0, 0, (SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE));
    }
}

void CVWnd_Edit::OnEnable (BOOL bEnable)
{
    CEdit::OnEnable (bEnable);

    if (m_spin.GetSafeHwnd ()!= NULL)
        m_spin.EnableWindow (bEnable);
}

void CVWnd_Edit::OnShowWindow (BOOL bShow, UINT nStatus)
{
    CEdit::OnShowWindow (bShow, nStatus);
    
    if (m_spin.GetSafeHwnd ()!= NULL)
        m_spin.ShowWindow (bShow ? SW_SHOWNA : SW_HIDE);
}

void CVWnd_Edit::AdjustSpinPos ()
{
    if (m_spin.GetSafeHwnd ()!= NULL)
    {
        CWnd* pParent = GetParent ();
        ASSERT (pParent != NULL);

        if (pParent != NULL)
        {
            if (pParent->m_hWnd != m_spin.GetParent ()->GetSafeHwnd ())
                m_spin.SetParent (pParent);

            CRect rt;
            GetWindowRect (&rt);
            pParent->ScreenToClient (&rt);
        
            rt.left = rt.right;
            rt.right = rt.left + 18;

            m_spin.MoveWindow (&rt);
            m_spin.Invalidate ();
        }
    }
}

void CVWnd_Edit::OnChar (UINT nChar, UINT nRepCnt, UINT nFlags)
{
    do
    {
        if (nChar >= ' ')
        {
            if (m_npInputMode == 5)    // 输入字节
            {
                if (nChar < '0' || nChar > '9')
                    break;
            }
            else if (m_npInputMode == 3 || (m_npInputMode >= 6 && m_npInputMode <= 8))  // 整数文本输入,输入短整数,输入整数,输入长整数
            {
                if (nChar != '-' && (nChar < '0' || nChar > '9'))
                    break;
            }
            else if (m_npInputMode == 4 || m_npInputMode == 9 || m_npInputMode == 10)    // 小数文本输入,输入小数,输入双精度小数
            {
                if (nChar != '-' && nChar != '.' && (nChar < '0' || nChar > '9'))
                    break;
            }
        }

        CEdit::DefWindowProc (WM_CHAR, (WPARAM)nChar, MAKE_DWORD (nRepCnt, nFlags));
        return;
    }
    while (FALSE);

    MessageBeep (MB_ICONEXCLAMATION);
}

void CVWnd_Edit::SlientSetWindowText (const TCHAR* szNewCaption)
{
    m_blpChangedEventDisabled = TRUE;
    SetWindowText (szNewCaption);
    m_blpChangedEventDisabled = FALSE;
}

//-----------------------------------------------------------------------------------------

BEGIN_MESSAGE_MAP (CVWnd_ComboBox, CComboBox)
    //{{AFX_MSG_MAP(CVWnd_ComboBox)
    ON_WM_CTLCOLOR_REFLECT ()
    ON_WM_ERASEBKGND ()
    ON_CONTROL_REFLECT (CBN_CLOSEUP, OnCloseup)
    ON_CONTROL_REFLECT (CBN_DROPDOWN, OnDropdown)
    ON_CONTROL_REFLECT (CBN_EDITCHANGE, OnEditchange)
    ON_CONTROL_REFLECT (CBN_SELENDOK, OnSelendok)
    ON_CONTROL_REFLECT (CBN_KILLFOCUS, OnKillfocus)
    ON_CONTROL_REFLECT (CBN_SETFOCUS, OnSetfocus)
    ON_CONTROL_REFLECT (CBN_DBLCLK, OnDblclk)
    ON_MESSAGE_VOID (MWM_COMBO_SELENDOK, OnSelendOkNotify)
    //}}AFX_MSG_MAP
END_MESSAGE_MAP ()

void CVWnd_ComboBox::OnConstruct ()
{
    m_clrText = RGB (0, 0, 0);
    m_clrBack = CLR_DEFAULT;
    m_npRowExtra = 1;

    m_brhBack.CreateSolidBrush (GetRealBackColor (m_clrBack));
    m_blpChangedEventDisabled = FALSE;
}

void CVWnd_ComboBox::OnKillfocus ()
{
    SendKillFocusNativeMessage (m_hWnd, m_pEventListener);
}

void CVWnd_ComboBox::OnSetfocus ()
{
    SendSetFocusNativeMessage (m_hWnd, m_pEventListener);
}

void CVWnd_ComboBox::OnCloseup ()
{
    if (m_pEventListener != NULL)
        m_pEventListener->OnCloseUp ();
}

void CVWnd_ComboBox::OnDropdown ()
{
    if (m_pEventListener != NULL)
        m_pEventListener->OnDropDown ();
}

void CVWnd_ComboBox::OnDblclk ()
{
    if (m_pEventListener != NULL)
        m_pEventListener->OnDblClk ();
}

void CVWnd_ComboBox::OnEditchange ()
{
    if (m_pEventListener != NULL && m_blpChangedEventDisabled == FALSE)
        m_pEventListener->OnEditChange ();
}

void CVWnd_ComboBox::OnSelendok ()
{
    if (m_pEventListener != NULL)
        PostMessage (MWM_COMBO_SELENDOK, 0, 0);
}

void CVWnd_ComboBox::OnSelendOkNotify ()
{
    if (m_pEventListener != NULL)
        m_pEventListener->OnSelendOk ();
}

HBRUSH CVWnd_ComboBox::CtlColor (CDC* pDC, UINT nCtlColor) 
{
    pDC->SetTextColor (m_clrText);
    pDC->SetBkColor (GetRealBackColor (m_clrBack));
    return m_brhBack;
}

BOOL CVWnd_ComboBox::OnEraseBkgnd (CDC* pDC) 
{
    if ((GetStyle () & (CBS_SIMPLE | CBS_DROPDOWN | CBS_DROPDOWNLIST)) == CBS_SIMPLE &&
            (HBRUSH)m_brhBack != NULL)
    {
        CRect rtClient;
        GetClientRect (&rtClient);
        pDC->FillRect (&rtClient, &m_brhBack);
    }

    return CComboBox::OnEraseBkgnd (pDC);
}

void CVWnd_ComboBox::SetBackColor (COLORREF clrBack)
{
    if (m_clrBack != clrBack)
    {
        m_clrBack = clrBack;
        m_brhBack.DeleteObject ();
        m_brhBack.CreateSolidBrush (GetRealBackColor (m_clrBack));

        if (m_hWnd != NULL)
            RedrawWindow (NULL, NULL, RDW_ERASE | RDW_FRAME | RDW_INVALIDATE);
    }
}

static LRESULT CALLBACK sComboBoxEditProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    WNDPROC fnOldProc = (WNDPROC)::GetProp (hwnd, _T ("_VolComboBoxEditProc"));
    ASSERT (fnOldProc != NULL);

    if (uMsg >= WM_KEYFIRST && uMsg <= WM_KEYLAST)
    {
        CVWnd_ComboBox* pComboBox = (CVWnd_ComboBox*)::GetProp (hwnd, _T ("_VolComboBoxObject"));
        ASSERT (pComboBox != NULL);
        if (SendNativeMessage (hwnd, uMsg, wParam, lParam, pComboBox->m_pEventListener))
            return 0;
    }
    else if (uMsg == WM_NCDESTROY)
    {
        ::RemoveProp (hwnd, _T ("_VolComboBoxEditProc"));
        ::RemoveProp (hwnd, _T ("_VolComboBoxObject"));
        ::SetWindowLongPtr (hwnd, GWLP_WNDPROC, (LONG_PTR)fnOldProc);
    }

    return ::CallWindowProc (fnOldProc, hwnd, uMsg, wParam, lParam);
}

BOOL CVWnd_ComboBox::CreateControl (CVolNativeWndCreateParam* pCreateParam)
{
    if ((pCreateParam->m_dwStyle & (CBS_SIMPLE | CBS_DROPDOWN | CBS_DROPDOWNLIST)) != CBS_SIMPLE)
    {
        // 保证下拉部分具有足够的高度
        if (pCreateParam->m_nHeight < 160)
            pCreateParam->m_nHeight = 160;
    }

    pCreateParam->m_dwStyle |= CBS_AUTOHSCROLL | WS_VSCROLL;

    if (CWnd::CreateEx (pCreateParam->m_dwExStyle, _T ("COMBOBOX"), pCreateParam->m_szCaption, pCreateParam->m_dwStyle,
            pCreateParam->m_nLeft, pCreateParam->m_nTop, pCreateParam->m_nWidth, pCreateParam->m_nHeight,
            pCreateParam->m_pParentWnd->GetSafeHwnd (), (HMENU)pCreateParam->m_nID))
    {
        // 用作截获组合框内编辑框中的按键事件
        const HWND hEditWnd = ::FindWindowEx (m_hWnd, NULL, _T ("Edit"), NULL);
        if (hEditWnd != NULL)
        {
            ::SetProp (hEditWnd, _T ("_VolComboBoxObject"), (HANDLE)this);
            ::SetProp (hEditWnd, _T ("_VolComboBoxEditProc"), (HANDLE)::SetWindowLongPtr (hEditWnd, GWLP_WNDPROC, (LONG_PTR)sComboBoxEditProc));
        }

        // 提前设置组件字体以支持在后面设置组件行间距
        ASSERT (pCreateParam->m_hFont != NULL);
        SendMessage (WM_SETFONT, (WPARAM)pCreateParam->m_hFont, MAKELPARAM (TRUE, 0));
        pCreateParam->m_hFont = NULL;

        SetItemHeight (0, GetItemHeight (0) + (INT)m_npRowExtra);  // 设置组件行间距

        // 设置初始列表内容
        ASSERT (m_hWnd != NULL);
        ResetListItems (m_strItems.GetText ());
        m_strItems.Empty ();  // 将其清除掉,避免内存空间占用.

        // 标题在CreateEx中并不会被反应到组合框的编辑部分
        SlientSetWindowText (pCreateParam->m_szCaption);
        return TRUE;
    }

    return FALSE;
}

// szListItems为使用'\n'字符分隔的数组,每两个成员记录一个列表项目的标题文本和关联数值.
void CVWnd_ComboBox::ResetListItems (const TCHAR* szListItems)
{
    if (m_hWnd != NULL)
    {
        ResetContent ();

        if (IsEmptyStr (szListItems) == FALSE)
        {
            // 设置初始内容
            CMStringArray saryFields;
            const INT_P npNumItems = SplitStrings (szListItems, saryFields, '\n', FALSE, FALSE) / 2;
            for (INT_P npItemIndex = 0; npItemIndex < npNumItems; npItemIndex++)
            {
                CVolString strCaption (saryFields [npItemIndex * 2]);
                strCaption.TrimRight ();
                SetItemData (AddString (strCaption.GetText ()), (DWORD_PTR)_ttoi (saryFields [npItemIndex * 2 + 1]));
            }
        }
    }
    else
    {
        m_strItems.SetText (szListItems);
    }
}

void CVWnd_ComboBox::SlientSetWindowText (const TCHAR* szNewCaption)
{
    m_blpChangedEventDisabled = TRUE;
    SetWindowText (szNewCaption);
    m_blpChangedEventDisabled = FALSE;
}

//-----------------------------------------------------------------------------------------

BEGIN_MESSAGE_MAP (CVWnd_ListBox, CListBox)
    //{{AFX_MSG_MAP(CVWnd_ListBox)
    ON_WM_CTLCOLOR_REFLECT ()
    ON_CONTROL_REFLECT (LBN_SELCHANGE, OnSelchange)
    ON_CONTROL_REFLECT (LBN_KILLFOCUS, OnKillfocus)
    ON_CONTROL_REFLECT (LBN_SETFOCUS, OnSetfocus)
    ON_CONTROL_REFLECT (LBN_DBLCLK, OnDblclk)
    //}}AFX_MSG_MAP
END_MESSAGE_MAP ()

void CVWnd_ListBox::OnConstruct ()
{
    m_clrText = RGB (0, 0, 0);
    m_clrBack = RGB (255, 255, 255);
    m_npRowExtra = 1;

    m_brhBack.CreateSolidBrush (GetRealBackColor (m_clrBack));
}

void CVWnd_ListBox::OnKillfocus ()
{
    SendKillFocusNativeMessage (m_hWnd, m_pEventListener);
}

void CVWnd_ListBox::OnSetfocus ()
{
    SendSetFocusNativeMessage (m_hWnd, m_pEventListener);
}

void CVWnd_ListBox::OnSelchange ()
{
    if (m_pEventListener != NULL)
        m_pEventListener->OnSelchange ();
}

void CVWnd_ListBox::OnDblclk ()
{
    if (m_pEventListener != NULL)
        m_pEventListener->OnDblClk ();
}

HBRUSH CVWnd_ListBox::CtlColor (CDC* pDC, UINT nCtlColor) 
{
    pDC->SetTextColor (m_clrText);
    pDC->SetBkColor (GetRealBackColor (m_clrBack));
    return m_brhBack;
}

void CVWnd_ListBox::SetBackColor (COLORREF clrBack)
{
    if (m_clrBack != clrBack)
    {
        m_clrBack = clrBack;
        m_brhBack.DeleteObject ();
        m_brhBack.CreateSolidBrush (GetRealBackColor (m_clrBack));

        if (m_hWnd != NULL)
            RedrawWindow (NULL, NULL, RDW_ERASE | RDW_FRAME | RDW_INVALIDATE);
    }
}

BOOL CVWnd_ListBox::CreateControl (CVolNativeWndCreateParam* pCreateParam)
{
    pCreateParam->m_dwStyle |= (LBS_USETABSTOPS | LBS_NOINTEGRALHEIGHT | LBS_NOTIFY);

    if (CWnd::CreateEx (pCreateParam->m_dwExStyle, _T ("LISTBOX"), pCreateParam->m_szCaption, pCreateParam->m_dwStyle,
            pCreateParam->m_nLeft, pCreateParam->m_nTop, pCreateParam->m_nWidth, pCreateParam->m_nHeight,
            pCreateParam->m_pParentWnd->GetSafeHwnd (), (HMENU)pCreateParam->m_nID))
    {
        // 提前设置组件字体以支持在后面设置组件行间距
        ASSERT (pCreateParam->m_hFont != NULL);
        SendMessage (WM_SETFONT, (WPARAM)pCreateParam->m_hFont, MAKELPARAM (TRUE, 0));
        pCreateParam->m_hFont = NULL;

        SetItemHeight (0, GetItemHeight (0) + (INT)m_npRowExtra);  // 设置组件行间距

        // 设置初始列表内容
        ASSERT (m_hWnd != NULL);
        ResetListItems (m_strItems.GetText ());
        m_strItems.Empty ();  // 将其清除掉,避免内存空间占用.

        return TRUE;
    }

    return FALSE;
}

// szListItems为使用'\n'字符分隔的数组,每两个成员记录一个列表项目的标题文本和关联数值.
static void sResetListItems (CListBox* pListBox, CVolString& strItems, const TCHAR* szListItems)
{
    ASSERT (pListBox != NULL);

    if (pListBox->m_hWnd != NULL)
    {
        pListBox->ResetContent ();

        if (IsEmptyStr (szListItems) == FALSE)
        {
            // 设置初始内容
            CMStringArray saryFields;
            const INT_P npNumItems = SplitStrings (szListItems, saryFields, '\n', FALSE, FALSE) / 2;
            for (INT_P npItemIndex = 0; npItemIndex < npNumItems; npItemIndex++)
            {
                CVolString strCaption (saryFields [npItemIndex * 2]);
                strCaption.TrimRight ();
                pListBox->SetItemData (pListBox->AddString (strCaption.GetText ()), (DWORD_PTR)_ttoi (saryFields [npItemIndex * 2 + 1]));
            }
        }
    }
    else
    {
        strItems.SetText (szListItems);
    }
}

void CVWnd_ListBox::ResetListItems (const TCHAR* szListItems)
{
    sResetListItems (this, m_strItems, szListItems);
}

//-----------------------------------------------------------------------------------------

BEGIN_MESSAGE_MAP (CVWnd_CheckListBox, CCheckListBox)
    //{{AFX_MSG_MAP(CVWnd_CheckListBox)
    ON_WM_LBUTTONDOWN ()
    ON_WM_KEYDOWN ()
    ON_WM_LBUTTONDBLCLK ()
    ON_CONTROL_REFLECT (LBN_SELCHANGE, OnSelchange)
    ON_CONTROL_REFLECT (LBN_KILLFOCUS, OnKillfocus)
    ON_CONTROL_REFLECT (LBN_SETFOCUS, OnSetfocus)
    ON_CONTROL_REFLECT (LBN_DBLCLK, OnDblclk)
    //}}AFX_MSG_MAP
END_MESSAGE_MAP ()

void CVWnd_CheckListBox::OnConstruct ()
{
    m_npRowExtra = 2;
    m_blpOnlyCheckOne = FALSE;
    m_sizeRadio.Zero ();
}

void CVWnd_CheckListBox::OnKillfocus ()
{
    SendKillFocusNativeMessage (m_hWnd, m_pEventListener);
}

void CVWnd_CheckListBox::OnSetfocus ()
{
    SendSetFocusNativeMessage (m_hWnd, m_pEventListener);
}

void CVWnd_CheckListBox::OnSelchange ()
{
    if (m_pEventListener != NULL)
        m_pEventListener->OnSelchange ();
}

void CVWnd_CheckListBox::OnDblclk ()
{
    if (m_pEventListener != NULL)
        m_pEventListener->OnDblClk ();
}

BOOL CVWnd_CheckListBox::CreateControl (CVolNativeWndCreateParam* pCreateParam)
{
    pCreateParam->m_dwStyle |= (LBS_USETABSTOPS | LBS_NOINTEGRALHEIGHT | LBS_NOTIFY | LBS_OWNERDRAWFIXED | LBS_HASSTRINGS);

    if (CWnd::CreateEx (pCreateParam->m_dwExStyle, _T ("LISTBOX"), pCreateParam->m_szCaption, pCreateParam->m_dwStyle,
            pCreateParam->m_nLeft, pCreateParam->m_nTop, pCreateParam->m_nWidth, pCreateParam->m_nHeight,
            pCreateParam->m_pParentWnd->GetSafeHwnd (), (HMENU)pCreateParam->m_nID))
    {
        //   组件相关GUI资源载入必须放在此处而不能放在OnConstruct中,因为本组件的
        // 实例对象有可能伴随启动类的实例对象创建而创建,而此时MFC尚未初始化.
        if ((HBITMAP)m_bmpRadios != NULL || m_bmpRadios.LoadBitmap (_IDB_VOL_RADIOS))
        {
            BITMAP bm;
            m_bmpRadios.GetObject (sizeof (BITMAP), &bm);
            m_sizeRadio.cx = bm.bmWidth / 3;
            m_sizeRadio.cy = bm.bmHeight;
        }

        // 提前设置组件字体以支持在后面设置组件行间距
        ASSERT (pCreateParam->m_hFont != NULL);
        SendMessage (WM_SETFONT, (WPARAM)pCreateParam->m_hFont, MAKELPARAM (TRUE, 0));
        pCreateParam->m_hFont = NULL;

        SetItemHeight (0, GetItemHeight (0) + (INT)m_npRowExtra);  // 设置组件行间距

        // 设置初始列表内容
        ASSERT (m_hWnd != NULL);
        ResetListItems (m_strItems.GetText ());
        m_strItems.Empty ();  // 将其清除掉,避免内存空间占用.

        return TRUE;
    }

    return FALSE;
}

void CVWnd_CheckListBox::ResetListItems (const TCHAR* szListItems)
{
    sResetListItems (this, m_strItems, szListItems);
}

void CVWnd_CheckListBox::SetOnlyCheckOne (const BOOL_P blpOnlyCheckOne)
{
    if (blpOnlyCheckOne == m_blpOnlyCheckOne)
        return;

    m_blpOnlyCheckOne = blpOnlyCheckOne;

    if (m_hWnd != NULL)
    {
        Invalidate ();

        if (m_blpOnlyCheckOne)
        {
            BOOL_P blpIsFirst = TRUE;

            INT_P npCount = GetCount ();
            for (INT_P npIndex = 0; npIndex < npCount; npIndex++)
            {
                if (GetCheck ((INT)npIndex) == 1)
                {
                    if (blpIsFirst == FALSE)
                        SetCheck ((INT)npIndex, 0);
                    blpIsFirst = FALSE;
                }
            }
        }
    }
}

void CVWnd_CheckListBox::SaveCurrentStates (INT_P* pnpCurSel, INT_P* pnpNumItems, CMIntPArray& aryCheckState)
{
    ASSERT (pnpCurSel != NULL && pnpNumItems != NULL);

    const INT_P npNumItems = GetCount ();

    *pnpCurSel = -1;
    *pnpNumItems = npNumItems;
    aryCheckState.RemoveAll ();

    for (INT_P npIndex = 0; npIndex < npNumItems; npIndex++)
    {
        const INT_P npCheckState = GetCheck ((INT)npIndex);

        if (m_pEventListener != NULL)
            aryCheckState.Add (npCheckState);

        if (*pnpCurSel == -1 && npCheckState == 1)
            *pnpCurSel = npIndex;
    }
}

void CVWnd_CheckListBox::CheckCurrentStates (const INT_P npOldSel, const INT_P npOldNumItems, const CMIntPArray& aryOldCheckState)
{
    if (m_blpOnlyCheckOne && npOldSel >= 0)
    {
        const INT_P npNumItems = GetCount ();

        if (npNumItems >= 0)
        {
            INT_P npIndex = 0;
            for (; npIndex < npNumItems; npIndex++)
            {
                if (GetCheck ((INT)npIndex) == 1)
                    break;
            }

            if (npIndex >= npNumItems)
            {
                SetCheck ((INT)npOldSel, 1);
            }
            else
            {
                for (npIndex = 0; npIndex < npNumItems; npIndex++)
                {
                    if (npIndex != npOldSel && GetCheck ((INT)npIndex) == 1)
                    {
                        SetCheck ((INT)npOldSel, 0);
                        break;
                    }
                }
            }
        }
    }

    if (m_pEventListener != NULL)
    {
        if (npOldNumItems == GetCount ())
        {
            for (INT npIndex = 0; npIndex < npOldNumItems; npIndex++)
            {
                if (GetCheck ((INT)npIndex) != aryOldCheckState [npIndex])
                {
                    m_pEventListener->OnCheckStateChanged ();
                    break;
                }
            }
        }
        else
        {
            m_pEventListener->OnCheckStateChanged ();
        }
    }
}

void CVWnd_CheckListBox::OnLButtonDown (UINT nFlags, CPoint point)
{
    INT_P npOldSel, npOldNumItems;
    CMIntPArray aryOldCheckState;
    SaveCurrentStates (&npOldSel, &npOldNumItems, aryOldCheckState);

    CCheckListBox::OnLButtonDown (nFlags, point);

    CheckCurrentStates (npOldSel, npOldNumItems, aryOldCheckState);
}

void CVWnd_CheckListBox::OnKeyDown (UINT nChar, UINT nRepCnt, UINT nFlags)
{
    INT_P npOldSel, npOldNumItems;
    CMIntPArray aryOldCheckState;
    SaveCurrentStates (&npOldSel, &npOldNumItems, aryOldCheckState);

    CCheckListBox::OnKeyDown (nChar, nRepCnt, nFlags);

    CheckCurrentStates (npOldSel, npOldNumItems, aryOldCheckState);
}

void CVWnd_CheckListBox::OnLButtonDblClk (UINT nFlags, CPoint point)
{
    INT_P npOldSel, npOldNumItems;
    CMIntPArray aryOldCheckState;
    SaveCurrentStates (&npOldSel, &npOldNumItems, aryOldCheckState);

    CCheckListBox::OnLButtonDblClk (nFlags, point);

    CheckCurrentStates (npOldSel, npOldNumItems, aryOldCheckState);
}

BOOL CVWnd_CheckListBox::OnChildNotify (UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
    if (message != WM_DRAWITEM)
        return CCheckListBox::OnChildNotify (message, wParam, lParam, pResult);

    ASSERT (pResult == NULL);  // no return value expected
    MyPreDrawItem ((LPDRAWITEMSTRUCT)lParam);
    return TRUE;
}

void CVWnd_CheckListBox::MyPreDrawItem (LPDRAWITEMSTRUCT lpDrawItemStruct)
{
    if (m_blpOnlyCheckOne == FALSE || (HBITMAP)m_bmpRadios == NULL)
    {
        PreDrawItem (lpDrawItemStruct);
        return;
    }

    DRAWITEMSTRUCT drawItem;
    COPY_MEM (&drawItem, lpDrawItemStruct, sizeof (DRAWITEMSTRUCT));

    if (((LONG)drawItem.itemID) >= 0 && (drawItem.itemAction & (ODA_DRAWENTIRE | ODA_SELECT)) != 0)
    {
        INT cyItem = GetItemHeight (drawItem.itemID);
        CDC* pDC = CDC::FromHandle (drawItem.hDC);
        COLORREF newBkColor = GetSysColor (COLOR_WINDOW);

        BOOL fDisabled = (IsWindowEnabled () == FALSE || IsEnabled (drawItem.itemID) == FALSE);
        if ((drawItem.itemState & ODS_SELECTED) != 0 && fDisabled == FALSE)
            newBkColor = GetSysColor (COLOR_HIGHLIGHT);

        COLORREF oldBkColor = pDC->SetBkColor (newBkColor);

        CDC dcb;
        if (dcb.CreateCompatibleDC (pDC))
        {
            int nCheck = GetCheck (drawItem.itemID);
            HBITMAP hOldBitmap = (HBITMAP)::SelectObject (dcb.m_hDC, (HBITMAP)m_bmpRadios);

            CRect rectCheck = drawItem.rcItem;
            rectCheck.left += 1;
            rectCheck.top += 1 + MAX (0, (cyItem - m_sizeRadio.cy) / 2);
            rectCheck.right = rectCheck.left + m_sizeRadio.cx;
            rectCheck.bottom = rectCheck.top + m_sizeRadio.cy;

            CRect rectItem = drawItem.rcItem;
            rectItem.right = rectItem.left + m_sizeRadio.cx + 2;

            CRect rectCheckBox (OnGetCheckPosition (rectItem, rectCheck));

            ASSERT (rectCheck.IntersectRect (rectItem, rectCheckBox));
            ASSERT ((rectCheck == rectCheckBox) && (rectCheckBox.Size () == m_sizeRadio));

            CBrush brush (newBkColor);
            pDC->FillRect (rectItem, &brush);

            pDC->BitBlt (rectCheckBox.left, rectCheckBox.top, m_sizeRadio.cx,
                    m_sizeRadio.cy, &dcb, m_sizeRadio.cx * nCheck, 0, SRCCOPY);

            ::SelectObject (dcb.m_hDC, hOldBitmap);
        }
        pDC->SetBkColor (oldBkColor);
    }

    drawItem.rcItem.left = drawItem.rcItem.left + m_sizeRadio.cx + 2;
    DrawItem (&drawItem);
}

//-----------------------------------------------------------------------------------------

BEGIN_MESSAGE_MAP (CVWnd_ShapeBox, CWnd)
    //{{AFX_MSG_MAP(CVWnd_ShapeBox)
    ON_WM_PAINT ()
    ON_WM_GETDLGCODE ()
    //}}AFX_MSG_MAP
END_MESSAGE_MAP ()

void CVWnd_ShapeBox::OnConstruct ()
{
    m_clrBack = CLR_DEFAULT;
    m_npShapeType = 0;
    m_npLineEffect = 0;
    m_npLineStyle = 1;
    m_npLineWidth = 1;
    m_clrLine = RGB (0, 0, 0);
    m_clrFill = CLR_NONE;
}

BOOL CVWnd_ShapeBox::CreateControl (CVolNativeWndCreateParam* pCreateParam)
{
    pCreateParam->m_dwStyle &= ~WS_TABSTOP;

    return CWnd::CreateEx (pCreateParam->m_dwExStyle, VolRegisterWndClass (_T ("VolShapeBox")),
            pCreateParam->m_szCaption, pCreateParam->m_dwStyle,
            pCreateParam->m_nLeft, pCreateParam->m_nTop, pCreateParam->m_nWidth, pCreateParam->m_nHeight,
            pCreateParam->m_pParentWnd->GetSafeHwnd (), (HMENU)pCreateParam->m_nID);
}

UINT CVWnd_ShapeBox::OnGetDlgCode ()
{
    return DLGC_STATIC;
}

void CVWnd_ShapeBox::OnPaint ()
{
    CPaintDC dc (this);

    CRect rtClipBox;
    dc.GetClipBox (&rtClipBox);
    if (rtClipBox.IsRectEmpty ())
        return;

    CBrush* pOldBrush = dc.GetCurrentBrush ();
    CPen* pOldPen = dc.GetCurrentPen ();

    const INT_P npPenStyle = (m_npLineStyle == 0 ? PS_NULL :
            m_npLineStyle == 1 ? PS_INSIDEFRAME :
            m_npLineStyle == 2 ? PS_DASH :
            m_npLineStyle == 3 ? PS_DOT :
            m_npLineStyle == 4 ? PS_DASHDOT :
            PS_DASHDOTDOT);
    const INT_P npLineWidth = (m_npLineEffect == 0 && m_npLineStyle == 1 ? m_npLineWidth : 1);

    CPen pen1, pen2;
    CRect rt;
    GetClientRect (&rt);

    if (m_npLineEffect == 0)
    {
        pen1.CreatePen ((INT)npPenStyle, (INT)(npLineWidth == 1 ? 0 : npLineWidth), m_clrLine);
    }
    else
    {
        if (m_npLineEffect == 1)
        {
            pen1.CreatePen ((INT)npPenStyle, 0, RGB (255, 255, 255));
            pen2.CreatePen ((INT)npPenStyle, 0, RGB (0, 0, 0));
        }
        else
        {
            ASSERT (m_npLineEffect == 2);
            pen1.CreatePen ((INT)npPenStyle, 0, RGB (0, 0, 0));
            pen2.CreatePen ((INT)npPenStyle, 0, RGB (255, 255, 255));
        }

        if (m_npShapeType == 6)
        {
            rt.top++;
        }
        else if (m_npShapeType == 7)
        {
            rt.left++;
        }
        else
        {
            rt.left++;
            rt.top++;
        }
    }

    CBrush brhBack (GetRealBackColor (m_clrBack));
    dc.SelectObject (&brhBack);
    dc.PatBlt (rtClipBox.left, rtClipBox.top, rtClipBox.Width (), rtClipBox.Height (), PATCOPY);

    INT nWidth = rt.Width ();
    INT nHeight = rt.Height ();
    if (m_npShapeType == 1 || m_npShapeType == 3 || m_npShapeType == 5)
    {
        if (nWidth > nHeight)
        {
            rt.left += (nWidth - nHeight) / 2;
            rt.right = rt.left + nHeight;
        }
        else if (nHeight > nWidth)
        {
            rt.top += (nHeight - nWidth) / 2;
            rt.bottom = rt.top + nWidth;
        }
    }

    CBrush brhFill;
    dc.SetBkMode (TRANSPARENT);

    for (INT_P npIndex = 0; npIndex <= 1; npIndex++)
    {
        if (npIndex != 0 && m_npLineEffect == 0)
            break;

        dc.SelectObject (npIndex == 0 ? &pen1 : &pen2);

        if (npIndex == 1 || m_clrFill == CLR_NONE)
        {
            dc.SelectStockObject (NULL_BRUSH);
        }
        else
        {
            brhFill.CreateSolidBrush (m_clrFill);
            dc.SelectObject (&brhFill);
        }

        switch (m_npShapeType)
        {
        case 0:
        case 1:
            dc.Rectangle (&rt);
            rt.OffsetRect (-1, -1);
            break;
        case 2:
        case 3:
            dc.Ellipse (&rt);
            rt.OffsetRect (-1, -1);
            break;
        case 4:
        case 5:
            dc.RoundRect (&rt, CPoint (nWidth / 5, nHeight / 5));
            rt.OffsetRect (-1, -1);
            break;
        case 6:
            dc.MoveTo (rt.left, rt.top);
            dc.LineTo (rt.right, rt.top);
            rt.top--;
            break;
        case 7:
            dc.MoveTo (rt.left, rt.top);
            dc.LineTo (rt.left, rt.bottom);
            rt.left--;
            break;
        DEFAULT_FAIL
        }
    }

    dc.SelectObject (pOldPen);
    dc.SelectObject (pOldBrush);
}

void CVWnd_ShapeBox::SetBackColor (const COLORREF clrBack)
{
    if (m_clrBack != clrBack)
    {
        m_clrBack = clrBack;
        CheckRedraw ();
    }
}

void CVWnd_ShapeBox::SetLineColor (const COLORREF clrLine)
{
    if (m_clrLine != clrLine)
    {
        m_clrLine = clrLine;
        CheckRedraw ();
    }
}

void CVWnd_ShapeBox::SetFillColor (const COLORREF clrFill)
{
    if (m_clrFill != clrFill)
    {
        m_clrFill = clrFill;
        CheckRedraw ();
    }
}

void CVWnd_ShapeBox::SetShapeType (INT_P npShapeType)
{
    npShapeType = CLIP (npShapeType, 0, 7);

    if (m_npShapeType != npShapeType)
    {
        m_npShapeType = npShapeType;
        CheckRedraw ();
    }
}

void CVWnd_ShapeBox::SetLineEffect (INT_P npLineEffect)
{
    npLineEffect = CLIP (npLineEffect, 0, 2);

    if (m_npLineEffect != npLineEffect)
    {
        m_npLineEffect = npLineEffect;
        CheckRedraw ();
    }
}

void CVWnd_ShapeBox::SetLineStyle (INT_P npLineStyle)
{
    npLineStyle = CLIP (npLineStyle, 0, 5);

    if (m_npLineStyle != npLineStyle)
    {
        m_npLineStyle = npLineStyle;
        CheckRedraw ();
    }
}

void CVWnd_ShapeBox::SetLineWidth (INT_P npLineWidth)
{
    npLineWidth = MAX (npLineWidth, 0);

    if (m_npLineWidth != npLineWidth)
    {
        m_npLineWidth = npLineWidth;
        CheckRedraw ();
    }
}

//-----------------------------------------------------------------------------------------

BEGIN_MESSAGE_MAP (CVWnd_ScrollBar, CScrollBar)
    //{{AFX_MSG_MAP(CVWnd_ScrollBar)
    ON_WM_HSCROLL_REFLECT ()
    ON_WM_VSCROLL_REFLECT ()
    //}}AFX_MSG_MAP
END_MESSAGE_MAP ()

void CVWnd_ScrollBar::OnConstruct ()
{
    m_npInitialMin = 1;
    m_npInitialMax = 100;
    m_npInitialPos = 1;
    m_npPageStep = 10;
    m_npLineStep = 1;
    m_blpAllowTrack = TRUE;
}

BOOL CVWnd_ScrollBar::CreateControl (CVolNativeWndCreateParam* pCreateParam)
{
    if (CWnd::CreateEx (pCreateParam->m_dwExStyle, _T ("SCROLLBAR"),
            pCreateParam->m_szCaption, pCreateParam->m_dwStyle,
            pCreateParam->m_nLeft, pCreateParam->m_nTop, pCreateParam->m_nWidth, pCreateParam->m_nHeight,
            pCreateParam->m_pParentWnd->GetSafeHwnd (), (HMENU)pCreateParam->m_nID))
    {
        const INT nInitialMin = (INT)MIN (m_npInitialMin, m_npInitialMax);
        const INT nInitialMax = (INT)MAX (m_npInitialMin, m_npInitialMax);

        SetScrollRange (nInitialMin, nInitialMax);
        SetScrollPos (CLIP ((INT)m_npInitialPos, nInitialMin, nInitialMax));
        
        return TRUE;
    }

    return FALSE;
}

void CVWnd_ScrollBar::HScroll (UINT nSBCode, UINT nPos) 
{
    OnScroll (nSBCode, nPos);
}

void CVWnd_ScrollBar::VScroll (UINT nSBCode, UINT nPos) 
{
    OnScroll (nSBCode, nPos);
}

void CVWnd_ScrollBar::OnScroll (UINT nSBCode, UINT nPos) 
{
    if (nSBCode == SB_ENDSCROLL)
        return;

    INT nOldPos = GetScrollPos ();

    INT nMinPos = 0, nMaxPos = 0;
    GetScrollRange (&nMinPos, &nMaxPos);

    INT nNewPos = nOldPos;
    switch (nSBCode)
    {
    case SB_LEFT:
    // case SB_TOP:
        COMPILE_TIME_ASSERT (SB_LEFT == SB_TOP);
        nNewPos = nMinPos;
        break;

    case SB_RIGHT:
    // case SB_BOTTOM:
        COMPILE_TIME_ASSERT (SB_RIGHT == SB_BOTTOM);
        nNewPos = nMaxPos;
        break;

    case SB_LINELEFT:
    // case SB_LINEUP:
        COMPILE_TIME_ASSERT (SB_LINELEFT == SB_LINEUP);
        nNewPos -= (INT)m_npLineStep;
        break;

    case SB_LINERIGHT:
    // case SB_LINEDOWN:
        COMPILE_TIME_ASSERT (SB_LINERIGHT == SB_LINEDOWN);
        nNewPos += (INT)m_npLineStep;
        break;

    case SB_PAGELEFT:
    // case SB_PAGEUP:
        COMPILE_TIME_ASSERT (SB_PAGELEFT == SB_PAGEUP);
        nNewPos -= (INT)m_npPageStep;
        break;

    case SB_PAGERIGHT:
    // case SB_PAGEDOWN:
        COMPILE_TIME_ASSERT (SB_PAGERIGHT == SB_PAGEDOWN);
        nNewPos += (INT)m_npPageStep;
        break;

    case SB_THUMBTRACK:
        if (m_blpAllowTrack == FALSE)
            return;
        nNewPos = nPos;
        break;

    case SB_THUMBPOSITION:
        nNewPos = nPos;
        break;
    }

    nNewPos = CLIP (nNewPos, nMinPos, nMaxPos);

    if (nNewPos != nOldPos)
    {
        SetScrollPos (nNewPos);
        
        if (m_pEventListener != NULL)
            m_pEventListener->OnScroll ();
    }
}

//-----------------------------------------------------------------------------------------

void CVWnd_ProgressBar::OnConstruct ()
{
    m_npInitialMin = 0;
    m_npInitialMax = 100;
    m_npInitialPos = 0;
}

BOOL CVWnd_ProgressBar::CreateControl (CVolNativeWndCreateParam* pCreateParam)
{
    if (CProgressCtrl::Create (pCreateParam->m_dwStyle,
            CMRect (pCreateParam->m_nLeft, pCreateParam->m_nTop,
                pCreateParam->m_nLeft + pCreateParam->m_nWidth, pCreateParam->m_nTop + pCreateParam->m_nHeight),
            pCreateParam->m_pParentWnd, (UINT)pCreateParam->m_nID))
    {
        ModifyWndStyleEx (this, 0, pCreateParam->m_dwExStyle);

        const INT nInitialMin = (INT)MIN (m_npInitialMin, m_npInitialMax);
        const INT nInitialMax = (INT)MAX (m_npInitialMin, m_npInitialMax);

        SetRange (nInitialMin, nInitialMax);
        SetPos (CLIP ((INT)m_npInitialPos, nInitialMin, nInitialMax));
        
        return TRUE;
    }

    return FALSE;
}

//-----------------------------------------------------------------------------------------

BEGIN_MESSAGE_MAP (CVWnd_SliderBar, CSliderCtrl)
    //{{AFX_MSG_MAP(CVWnd_SliderBar)
    ON_WM_HSCROLL_REFLECT ()
    ON_WM_VSCROLL_REFLECT ()
    //}}AFX_MSG_MAP
END_MESSAGE_MAP ()

void CVWnd_SliderBar::OnConstruct ()
{
    m_npTickFreq = 1;
    m_npInitialSelStart = m_npInitialSelLength = 0;

    m_npInitialMin = 1;
    m_npInitialMax = 10;
    m_npInitialPageStep = 5;
    m_npInitialLineStep = 1;
    m_npInitialPos = 1;
}

BOOL CVWnd_SliderBar::CreateControl (CVolNativeWndCreateParam* pCreateParam)
{
    if (CSliderCtrl::Create (pCreateParam->m_dwStyle,
            CMRect (pCreateParam->m_nLeft, pCreateParam->m_nTop,
                pCreateParam->m_nLeft + pCreateParam->m_nWidth, pCreateParam->m_nTop + pCreateParam->m_nHeight),
            pCreateParam->m_pParentWnd, (UINT)pCreateParam->m_nID))
    {
        ModifyWndStyleEx (this, 0, pCreateParam->m_dwExStyle);

        const INT nInitialMin = (INT)MIN (m_npInitialMin, m_npInitialMax);
        const INT nInitialMax = (INT)MAX (m_npInitialMin, m_npInitialMax);

        SetRange (nInitialMin, nInitialMax, TRUE);

        const INT nRange = nInitialMax - nInitialMin;
        SetLineSize ((INT)CLIP (m_npInitialLineStep, 1, nRange));
        SetPageSize ((INT)CLIP (m_npInitialPageStep, 1, nRange));
        SetTicFreq ((INT)CLIP (m_npTickFreq, 1, nRange));

        SetPos ((INT)CLIP (m_npInitialPos, nInitialMin, nInitialMax));

        if ((pCreateParam->m_dwStyle & TBS_ENABLESELRANGE) != 0)
        {
            SetSelection ((INT)CLIP (m_npInitialSelStart, nInitialMin, nInitialMax),
                    (INT)CLIP (m_npInitialSelStart + m_npInitialSelLength, nInitialMin, nInitialMax));
        }

        return TRUE;
    }

    return FALSE;
}

void CVWnd_SliderBar::HScroll (UINT nSBCode, UINT nPos) 
{
    if (nSBCode != SB_ENDSCROLL && m_pEventListener != NULL)
        m_pEventListener->OnScroll ();
}

void CVWnd_SliderBar::VScroll (UINT nSBCode, UINT nPos) 
{
    if (nSBCode != SB_ENDSCROLL && m_pEventListener != NULL)
        m_pEventListener->OnScroll ();
}

//-----------------------------------------------------------------------------------------

BEGIN_MESSAGE_MAP (CVWnd_Tab, CTabCtrl)
    //{{AFX_MSG_MAP(CVWnd_Tab)
    ON_NOTIFY_REFLECT (TCN_SELCHANGE, OnSelchange)
    ON_NOTIFY_REFLECT (NM_CLICK, OnClick)
    ON_NOTIFY_REFLECT (TCN_SELCHANGING, OnSelchanging)
#ifdef _VOL_FOR_UI_DESIGNER  // 为编译布局设计器专用版本?
    ON_MESSAGE (MWM_IS_ON_CHILD_TAB_SWITCHER, IsOnChildTabSwitcher)
#endif
    ON_MESSAGE (MWM_GET_CURRENT_CHILD_TAB_INDEX, OnGetCurrentChildTabIndex)
    ON_WM_SIZE ()
    //}}AFX_MSG_MAP
END_MESSAGE_MAP ()

#ifdef _VOL_FOR_UI_DESIGNER  // 为编译布局设计器专用版本?

LRESULT CVWnd_Tab::IsOnChildTabSwitcher (WPARAM wParam, LPARAM lParam)
{
    if (lParam == _VOL_MSG_ACK && wParam != NULL)
    {
        if ((INT_P)::SendMessage (m_hWnd, TCM_GETITEMCOUNT, 0, 0L) > 1)
		{
			TCHITTESTINFO info;
			info.pt.x = GET_X_LPARAM (wParam);
			info.pt.y = GET_Y_LPARAM (wParam);
			info.flags = 0;

			if ((INT_P)::SendMessage (m_hWnd, TCM_HITTEST, 0, (LPARAM)&info) != -1 &&
                    (info.flags & TCHT_ONITEM) != 0)
            {
				return _VOL_MSG_ACK;
            }
		}
    }

    return 0;
}

#endif

LRESULT CVWnd_Tab::OnGetCurrentChildTabIndex (WPARAM wParam, LPARAM lParam)
{
    if (lParam == _VOL_MSG_ACK && wParam != NULL)
    {
        *(INT*)wParam = (INT)GetCurSel ();
        return _VOL_MSG_ACK;
    }

    return 0;
}

void CVWnd_Tab::OnSelchange (NMHDR* pNMHDR, LRESULT* pResult) 
{
    ::NotifyParentCurrentChildTabChanged (m_hWnd, FALSE);
    ShowCurrentContentControl (TRUE);

    if (m_pEventListener != NULL)
        m_pEventListener->OnSelChange ();

    *pResult = 0;
}

void CVWnd_Tab::OnConstruct ()
{
    m_npInitialSel = 0;
    m_npTabOrientation = 0;  // 上
    m_blpMutiLineHeader = FALSE;
    m_lpCurrentFreeID = 0;

    m_memControlInfos.SetMemAlignSize (sizeof (TAB_CONTENT_CONTROL_INFO) * 4);
}

BOOL CVWnd_Tab::CreateControl (CVolNativeWndCreateParam* pCreateParam)
{
    switch (m_npTabOrientation)
    {
    case 1:  // 下
        pCreateParam->m_dwStyle |= TCS_BOTTOM;
        break;
    case 2:  // 左
        pCreateParam->m_dwStyle |= TCS_VERTICAL;
        break;
    case 3:  // 右
        pCreateParam->m_dwStyle |= TCS_BOTTOM | TCS_VERTICAL;
        break;
    }

    if (m_blpMutiLineHeader)
        pCreateParam->m_dwStyle |= TCS_MULTILINE;

    if (CTabCtrl::Create (pCreateParam->m_dwStyle,
            CMRect (pCreateParam->m_nLeft, pCreateParam->m_nTop,
                pCreateParam->m_nLeft + pCreateParam->m_nWidth, pCreateParam->m_nTop + pCreateParam->m_nHeight),
            pCreateParam->m_pParentWnd, (UINT)pCreateParam->m_nID))
    {
        ModifyWndStyleEx (this, 0, pCreateParam->m_dwExStyle);

        if (m_lstImages.m_hImageList != NULL)  // 设置有图片组?
            SetImageList (&m_lstImages);

        // 设置初始子夹内容
        ASSERT (m_hWnd != NULL);
        ResetChildTabs (m_strItems.GetText ());
        m_strItems.Empty ();  // 将其清除掉,避免内存空间占用.

        const INT_P npNumItems = GetItemCount ();
        if (npNumItems > 0)
            SetCurSel ((INT)CLIP (m_npInitialSel, 0, npNumItems - 1));

        return TRUE;
    }

    return FALSE;
}

BOOL_P CVWnd_Tab::IsControlWindowValid (HWND hWndControl) const
{
    if (hWndControl == NULL || ::IsWindow (hWndControl) == FALSE || ::GetParent (hWndControl) == NULL ||
            hWndControl == m_hWnd || ::IsChild (hWndControl, m_hWnd))
    {
        return FALSE;
    }

    const TAB_CONTENT_CONTROL_INFO* pInf = (const TAB_CONTENT_CONTROL_INFO*)m_memControlInfos.GetPtr ();
    const INT_P npCount = m_memControlInfos.GetSize () / sizeof (TAB_CONTENT_CONTROL_INFO);
    ASSERT (m_aryControls.GetCount () == npCount);

    for (INT_P npIndex = 0; npIndex < npCount; npIndex++, pInf++)
    {
        if (pInf->m_hWndControl != NULL)
        {
            if (pInf->m_hWndControl == hWndControl ||
                    ::IsChild (pInf->m_hWndControl, hWndControl) ||
                    ::IsChild (hWndControl, pInf->m_hWndControl))
            {
                return FALSE;
            }
        }
    }

    return TRUE;
}

void CVWnd_Tab::RemoveAllChildTabs ()
{
    INT_P npCount = m_aryControls.GetCount (), npIndex;
    ASSERT (m_memControlInfos.GetSize () / sizeof (TAB_CONTENT_CONTROL_INFO) == npCount);

    const TAB_CONTENT_CONTROL_INFO* pInf = (const TAB_CONTENT_CONTROL_INFO*)m_memControlInfos.GetPtr ();
    for (npIndex = 0; npIndex < npCount; npIndex++, pInf++)
        sDestroyChildControl (pInf);
    ASSERT (m_memControlInfos.IsAtEnd (pInf));

    m_aryControls.RemoveAll ();
    m_memControlInfos.Free ();
    DeleteAllItems ();

    //--------------------------------------------------------------------------

    CMUIntPArray aryDestroyWindows;
    for (HWND hWndChild = ::GetWindow (m_hWnd, GW_CHILD);
            hWndChild != NULL;
            hWndChild = ::GetWindow (hWndChild, GW_HWNDNEXT))
    {
        if (GetVolControlGroupNumber (hWndChild) >= 0)
            aryDestroyWindows.Add ((UINT_P)hWndChild);
    }

    npCount = aryDestroyWindows.GetCount ();
    for (npIndex = 0; npIndex < npCount; npIndex++)
        ::DestroyWindow ((HWND)aryDestroyWindows [npIndex]);
}

void CVWnd_Tab::RemoveChildTab (const INT nChildTabIndex)
{
    const INT_P npContentControlIndex = FindContentControlIndex (nChildTabIndex);
    if (npContentControlIndex != -1)
    {
        ASSERT (m_aryControls.IsIndexValid (npContentControlIndex) &&
                m_memControlInfos.GetSize () / sizeof (TAB_CONTENT_CONTROL_INFO) == m_aryControls.GetCount ());

        sDestroyChildControl ((TAB_CONTENT_CONTROL_INFO*)m_memControlInfos.GetPtr () + npContentControlIndex);

        m_aryControls.RemoveAt (npContentControlIndex);
        m_memControlInfos.Remove (npContentControlIndex * (INT_P)sizeof (TAB_CONTENT_CONTROL_INFO), (INT_P)sizeof (TAB_CONTENT_CONTROL_INFO));
    }

    const BOOL_P blpIsCurTab = (nChildTabIndex == GetCurSel ());
    if (DeleteItem (nChildTabIndex))
    {
        const INT nNumChildTabs = GetItemCount ();
        if (blpIsCurTab && nNumChildTabs > 0)
            SetCurSel (nChildTabIndex < nNumChildTabs ? nChildTabIndex : nNumChildTabs - 1);

        if (m_blpMutiLineHeader)
            AdjustTabContentPostion ();

        ShowCurrentContentControl (blpIsCurTab);
    }

    //--------------------------------------------------------------------------

    if (nChildTabIndex >= 0)
    {
        BOOL_P blpNeedUpdate = FALSE;

        CMUIntPArray aryDestroyWindows;
        for (HWND hWndChild = ::GetWindow (m_hWnd, GW_CHILD);
                hWndChild != NULL;
                hWndChild = ::GetWindow (hWndChild, GW_HWNDNEXT))
        {
            const INT_P npGroupNumber = GetVolControlGroupNumber (hWndChild);

            if (npGroupNumber == nChildTabIndex)
            {
                aryDestroyWindows.Add ((UINT_P)hWndChild);
            }
            else if (npGroupNumber > nChildTabIndex)
            {
                if (::SetVolControlGroupNumber (hWndChild, (INT)npGroupNumber - 1))
                    blpNeedUpdate = TRUE;
            }
        }

        INT_P npCount = aryDestroyWindows.GetCount ();
        for (INT_P npIndex = 0; npIndex < npCount; npIndex++)
            ::DestroyWindow ((HWND)aryDestroyWindows [npIndex]);

        if (blpNeedUpdate)
            ::NotifyParentCurrentChildTabChanged (m_hWnd, FALSE);
    }
}

void CVWnd_Tab::ShowCurrentContentControl (const BOOL_P blpSetFocus)
{
    const INT_P npCurrentContentControlIndex = FindContentControlIndex (GetCurSel ());
    const TAB_CONTENT_CONTROL_INFO* pInf = (const TAB_CONTENT_CONTROL_INFO*)m_memControlInfos.GetPtr ();

    const INT_P npCount = m_aryControls.GetCount ();
    ASSERT (m_memControlInfos.GetSize () / sizeof (TAB_CONTENT_CONTROL_INFO) == npCount);

    for (INT_P npIndex = 0; npIndex < npCount; npIndex++, pInf++)
    {
        const HWND hWndControl = pInf->m_hWndControl;

        if (hWndControl != NULL)
        {
            if (npIndex == npCurrentContentControlIndex)
            {
                ::ShowWindow (hWndControl, SW_SHOW);
                ::InvalidateRect (hWndControl, NULL, TRUE);

                if (blpSetFocus)
                    ::SetFocus (hWndControl);
            }
            else
                ::ShowWindow (hWndControl, SW_HIDE);
        }
    }
    ASSERT (m_memControlInfos.IsAtEnd (pInf));
}

INT_P CVWnd_Tab::FindContentControlIndex (const INT_P npChildTabIndex)
{
    do
    {
        if (npChildTabIndex < 0)
            break;

        TC_ITEM item;
        item.mask = TCIF_PARAM;
        item.lParam = 0;
        if (GetItem ((INT)npChildTabIndex, &item) == FALSE)
            break;

        const INT_P npCount = m_aryControls.GetCount ();
        for (INT_P npIndex = 0; npIndex < npCount; npIndex++)
        {
            if (m_aryControls [npIndex].m_npUserValue == (INT_P)item.lParam)
                return npIndex;
        }
    }
    while (FALSE);

    return -1;
}

void CVWnd_Tab::sDestroyChildControl (const TAB_CONTENT_CONTROL_INFO* pInf)
{
    ASSERT (pInf != NULL);

    if (pInf->m_hWndControl == NULL)
        return;

    if (pInf->m_blpIsWindowExisted)  // 为挂接进来时已经存在的窗口?
        ::ShowWindow (pInf->m_hWndControl, SW_HIDE);  // 将其隐藏
    else
        ::DestroyWindow (pInf->m_hWndControl);  // 否则将其销毁
}

// szListItems为使用'\n'字符分隔的数组,每个成员记录一个子夹的标题文本.
void CVWnd_Tab::ResetChildTabs (const TCHAR* szListItems)
{
    if (m_hWnd != NULL)
    {
        RemoveAllChildTabs ();

        if (IsEmptyStr (szListItems) == FALSE)
        {
            TC_ITEM item;
            ZERO_MEM (&item, sizeof (item));
            item.mask = (TCIF_TEXT | TCIF_PARAM);
            item.lParam = 0;

            if (m_lstImages.m_hImageList != NULL)  // 设置有图片组?
                item.mask |= TCIF_IMAGE;

            CMStringArray saryFields;
            const INT_P npNumItems = SplitStrings (szListItems, saryFields, '\n', FALSE, FALSE) / 2;

            for (INT_P npItemIndex = 0; npItemIndex < npNumItems; npItemIndex++)
            {
                item.pszText = (LPTSTR)saryFields [npItemIndex * 2];
                if (m_lstImages.m_hImageList != NULL)  // 设置有图片组?
                    item.iImage = _ttoi (saryFields [npItemIndex * 2 + 1]);

                InsertItem ((INT)npItemIndex, &item);
            }
        }

    #ifndef _VOL_FOR_UI_DESIGNER  // 不为编译布局设计器专用版本?
        // 向父窗口投递重新布局选择夹内容消息. 因为此时选择夹中的子组件可能尚未创建,所以必须使用投递消息.
        ::NotifyParentCurrentChildTabChanged (m_hWnd, TRUE);
    #endif
    }
    else
    {
        m_strItems.SetText (szListItems);
    }
}

void CVWnd_Tab::TakeOverImageList (const HIMAGELIST hImageList)
{
    m_lstImages.DeleteImageList ();
    m_lstImages.Attach (hImageList);

    if (m_hWnd != NULL)
    {
        SetImageList (&m_lstImages);
        Invalidate ();
    }
}

void CVWnd_Tab::OnClick (NMHDR* pNMHDR, LRESULT* pResult) 
{
    if (m_pEventListener != NULL)
        m_pEventListener->OnClick ();

    *pResult = 0;
}

void CVWnd_Tab::OnSelchanging (NMHDR* pNMHDR, LRESULT* pResult) 
{
    if (m_pEventListener != NULL &&
            m_pEventListener->OnSelChanging () == FALSE)
    {
        *pResult = TRUE;
    }
    else
        *pResult = FALSE;
}

void CVWnd_Tab::OnSize (UINT nType, int cx, int cy)
{
    CTabCtrl::OnSize (nType, cx, cy);

    if (cx > 0 && cy > 0)
        AdjustTabContentPostion ();
}

void CVWnd_Tab::AdjustTabContentPostion ()
{
    const INT_P npNumContentControls = m_memControlInfos.GetSize () / sizeof (TAB_CONTENT_CONTROL_INFO);
    ASSERT (npNumContentControls == m_aryControls.GetCount ());

    if (npNumContentControls > 0)
    {
        CMRect rtContent;
        GetContentRect (&rtContent);

        const TAB_CONTENT_CONTROL_INFO* pInf = (const TAB_CONTENT_CONTROL_INFO*)m_memControlInfos.GetPtr ();
        for (INT_P npIndex = 0; npIndex < npNumContentControls; npIndex++, pInf++)
        {
            if (pInf->m_hWndControl != NULL)
            {
                CMRect rtPos;
                CalcContentControlPos (rtContent, pInf->m_nDesignClientWidth, pInf->m_nDesignClientHeight, pInf->m_nContentPlace, &rtPos);

                ::MoveWindow (pInf->m_hWndControl, rtPos.left, rtPos.top, rtPos.Width (), rtPos.Height (), TRUE);
            }
        }
        ASSERT (m_memControlInfos.IsAtEnd (pInf));
    }
}

void CVWnd_Tab::GetContentRect (CMRect* prtContent)
{
    ASSERT (prtContent != NULL);

    if (m_hWnd == NULL)
    {
        FAIL;
        prtContent->SetEmpty ();
    }
    else
    {
        GetWindowRect (prtContent);
        ScreenToClient (prtContent);
        AdjustRect (FALSE, prtContent);
    }
}

void CVWnd_Tab::CalcContentControlPos (const INT nDesignClientWidth, const INT nDesignClientHeight, const INT_P npContentPlace, CMRect* prtPos)
{
    ASSERT (prtPos != NULL);

    CMRect rtContent;
    GetContentRect (&rtContent);

    CalcContentControlPos (rtContent, nDesignClientWidth, nDesignClientHeight, npContentPlace, prtPos);
}

void CVWnd_Tab::CalcContentControlPos (const CMRect& rtContent, const INT nDesignClientWidth, const INT nDesignClientHeight, const INT_P npContentPlace, CMRect* prtPos)
{
    ASSERT (prtPos != NULL);

    INT nControlWidth = nDesignClientWidth;
    INT nControlHeight = nDesignClientHeight;

    INT nLeft = rtContent.left, nTop = rtContent.top;
    switch (npContentPlace)
    {
    case 0:  // 填满
        nControlWidth = rtContent.Width ();
        nControlHeight = rtContent.Height ();
        break;

    case 2:  // 居中
        nLeft += (rtContent.Width () - nControlWidth) / 2;
        nTop += (rtContent.Height () - nControlHeight) / 2;
        break;

    case 3:  // 右下
        nLeft += rtContent.Width () - nControlWidth;
        nTop += rtContent.Height () - nControlHeight;
        break;
    }

    prtPos->Set (nLeft, nTop, nLeft + nControlWidth, nTop + nControlHeight);
}

//-----------------------------------------------------------------------------------------

#ifdef _VOL_FOR_UI_DESIGNER  // 为编译布局设计器专用版本?

BEGIN_MESSAGE_MAP (CVWnd_Animate, CAnimateCtrl)
    //{{AFX_MSG_MAP(CVWnd_Animate)
    ON_WM_PAINT ()
    //}}AFX_MSG_MAP
END_MESSAGE_MAP ()

#endif

void CVWnd_Animate::OnConstruct ()
{
    m_blpCenter = TRUE;
    m_blpTransparent = TRUE;
    m_blpPlay = TRUE;
    m_npPlayCount = -1;
}

BOOL CVWnd_Animate::CreateControl (CVolNativeWndCreateParam* pCreateParam)
{
    if (m_blpCenter)
        pCreateParam->m_dwStyle |= ACS_CENTER;
    if (m_blpTransparent)
        pCreateParam->m_dwStyle |= ACS_TRANSPARENT;

    if (CAnimateCtrl::Create (pCreateParam->m_dwStyle,
            CMRect (pCreateParam->m_nLeft, pCreateParam->m_nTop,
                pCreateParam->m_nLeft + pCreateParam->m_nWidth, pCreateParam->m_nTop + pCreateParam->m_nHeight),
            pCreateParam->m_pParentWnd, (UINT)pCreateParam->m_nID))
    {
        ModifyWndStyleEx (this, 0, pCreateParam->m_dwExStyle);

    #ifndef _VOL_FOR_UI_DESIGNER  // 不为编译布局设计器专用版本?
        if (m_strAnimFileName.IsEmpty () == FALSE)
        {
            if (Open (m_strAnimFileName.GetText ()) && m_blpPlay)
                Play (0, -1, (INT)m_npPlayCount);
        }
    #endif

        return TRUE;
    }

    return FALSE;
}

void CVWnd_Animate::OpenAnimFile (const TCHAR* szAnimFileName)
{
    m_strAnimFileName.SetText (szAnimFileName);

#ifndef _VOL_FOR_UI_DESIGNER  // 不为编译布局设计器专用版本?
    if (m_hWnd != NULL)
    {
        if (IsEmptyStr (szAnimFileName) == FALSE)
        {
            if (Open (szAnimFileName) && m_blpPlay)
                Play (0, -1, (INT)m_npPlayCount);
        }
        else
            Close ();
    }
#endif
}

void CVWnd_Animate::SetPlayState (const BOOL_P blpPlay)
{
    if (m_blpPlay != blpPlay)
    {
        m_blpPlay = blpPlay;

    #ifndef _VOL_FOR_UI_DESIGNER  // 不为编译布局设计器专用版本?
        if (m_hWnd != NULL)
        {
            if (m_blpPlay == FALSE)
            {
                Stop ();
            }
            else
            {
                if (m_strAnimFileName.IsEmpty () == FALSE)
                    Play (0, -1, (INT)m_npPlayCount);
            }
        }
    #endif
    }
}

void CVWnd_Animate::SetPlayCount (const INT_P npPlayCount)
{
    m_npPlayCount = MAX (-1, npPlayCount);

#ifndef _VOL_FOR_UI_DESIGNER  // 不为编译布局设计器专用版本?
    if (m_hWnd != NULL)
    {
        if (m_blpPlay && m_strAnimFileName.IsEmpty () == FALSE)
            Play (0, -1, (INT)m_npPlayCount);
    }
#endif
}

#ifdef _VOL_FOR_UI_DESIGNER  // 为编译布局设计器专用版本?

void CVWnd_Animate::OnPaint ()
{
    CPaintDC dcPaint (this);

    CRect rtClipBox;
    dcPaint.GetClipBox (&rtClipBox);
    if (rtClipBox.IsRectEmpty ())
        return;

    CBrush brh (::GetSysColor (COLOR_BTNFACE));
    CBrush* pOldBrush = dcPaint.SelectObject (&brh);
    dcPaint.PatBlt (rtClipBox.left, rtClipBox.top, rtClipBox.Width (), rtClipBox.Height (), PATCOPY);
    dcPaint.SelectObject (pOldBrush);
}

#endif

//-----------------------------------------------------------------------------------------

BEGIN_MESSAGE_MAP (CVWnd_DatePicker, CDateTimeCtrl)
    //{{AFX_MSG_MAP(CVWnd_DatePicker)
    ON_NOTIFY_REFLECT (DTN_DATETIMECHANGE, OnDatetimechange)
    ON_NOTIFY_REFLECT (NM_KILLFOCUS, OnKillfocus)
    ON_NOTIFY_REFLECT (NM_SETFOCUS, OnSetfocus)
    ON_MESSAGE (MWM_DATE_TIME_CHANGE, OnMyDateTimeChange)
    //}}AFX_MSG_MAP
END_MESSAGE_MAP ()

void CVWnd_DatePicker::OnConstruct ()
{
    m_dbCurrentDate = m_dbMinDate = m_dbMaxDate = 0;
    m_dtLastNotify = 0;
    m_blpNotifying = FALSE;
}

BOOL CVWnd_DatePicker::CreateControl (CVolNativeWndCreateParam* pCreateParam)
{
    m_dtLastNotify = 0;

    if (CDateTimeCtrl::Create (pCreateParam->m_dwStyle,
            CMRect (pCreateParam->m_nLeft, pCreateParam->m_nTop,
                pCreateParam->m_nLeft + pCreateParam->m_nWidth, pCreateParam->m_nTop + pCreateParam->m_nHeight),
            pCreateParam->m_pParentWnd, (UINT)pCreateParam->m_nID))
    {
        ModifyWndStyleEx (this, 0, pCreateParam->m_dwExStyle);

        UpdateRange ();
        SetTime (IsDoubleEqualZero (m_dbCurrentDate) ? COleDateTime::GetCurrentTime () : COleDateTime (m_dbCurrentDate));

        return TRUE;
    }

    return FALSE;
}

void CVWnd_DatePicker::UpdateRange ()
{
    if (m_hWnd != NULL)
    {
        SetRange ((IsDoubleEqualZero (m_dbMinDate) ? NULL : &COleDateTime (m_dbMinDate)),
                (IsDoubleEqualZero (m_dbMaxDate) ? NULL : &COleDateTime (m_dbMaxDate)));
    }
}

LRESULT CVWnd_DatePicker::OnMyDateTimeChange (WPARAM, LPARAM)
{
    if (m_blpNotifying == FALSE)
    {
        COleDateTime time;
        GetTime (time);
        const DATE dtCurrent = (DATE)time;

        if (IsDoubleEqualZero (m_dtLastNotify) || IsDoubleEqual (dtCurrent, m_dtLastNotify) == FALSE)
        {
            m_blpNotifying = TRUE;
            if (m_pEventListener != NULL)
                m_pEventListener->OnDateChanged ();
            m_blpNotifying = FALSE;

            m_dtLastNotify = dtCurrent;
        }
    }

    return 0;
}

void CVWnd_DatePicker::OnDatetimechange (NMHDR* pNMHDR, LRESULT* pResult)
{
    PostMessage (MWM_DATE_TIME_CHANGE, 0, 0);
    *pResult = 0;
}

void CVWnd_DatePicker::OnKillfocus (NMHDR* pNMHDR, LRESULT* pResult) 
{
    SendKillFocusNativeMessage (m_hWnd, m_pEventListener);
    *pResult = 0;
}

void CVWnd_DatePicker::OnSetfocus (NMHDR* pNMHDR, LRESULT* pResult) 
{
    SendSetFocusNativeMessage (m_hWnd, m_pEventListener);
    *pResult = 0;
}

//-----------------------------------------------------------------------------------------

BEGIN_MESSAGE_MAP (CVWnd_MonthCal, CMonthCalCtrl)
    //{{AFX_MSG_MAP(CVWnd_MonthCal)
    ON_NOTIFY_REFLECT (MCN_SELCHANGE, OnSelchange)
    //}}AFX_MSG_MAP
END_MESSAGE_MAP ()

void CVWnd_MonthCal::OnConstruct ()
{
    m_npFirstDayOfWeek = 0;
    m_npMonthDelta = 0;
    m_npMaxSelDayCount = 1;
    m_dtBeginSel = m_dtEndSel = 0;

    m_clrText = RGB (0, 0, 0);
    m_clrBack = CLR_DEFAULT;
    m_clrMonthBack = RGB (255, 255, 255);
    m_clrTitleText = RGB (0, 0, 0);
    m_clrTitleBack = CLR_DEFAULT;
    m_clrTrailingText = RGB (192, 192, 192);

    m_dbCurrentDate = m_dbMinDate = m_dbMaxDate = 0;
}

BOOL CVWnd_MonthCal::CreateControl (CVolNativeWndCreateParam* pCreateParam)
{
    if (CMonthCalCtrl::Create (pCreateParam->m_dwStyle,
            CMRect (pCreateParam->m_nLeft, pCreateParam->m_nTop,
                pCreateParam->m_nLeft + pCreateParam->m_nWidth, pCreateParam->m_nTop + pCreateParam->m_nHeight),
            pCreateParam->m_pParentWnd, (UINT)pCreateParam->m_nID))
    {
        ModifyWndStyleEx (this, 0, pCreateParam->m_dwExStyle);

        SetFirstDayOfWeek ((INT)m_npFirstDayOfWeek);
        SetMonthDelta ((INT)m_npMonthDelta);

        SetToday (IsDoubleEqualZero (m_dbCurrentDate) ? COleDateTime::GetCurrentTime () : COleDateTime (m_dbCurrentDate));
        UpdateDateRange ();

        SetMaxSelCount ((INT)m_npMaxSelDayCount);
        UpdateSelRange ();

        SetColor (MCSC_TEXT, m_clrText);
        SetColor (MCSC_BACKGROUND, GetRealBackColor (m_clrBack));
        SetColor (MCSC_MONTHBK, GetRealBackColor (m_clrMonthBack));
        SetColor (MCSC_TITLETEXT, m_clrTitleText);
        SetColor (MCSC_TITLEBK, GetRealBackColor (m_clrTitleBack));
        SetColor (MCSC_TRAILINGTEXT, m_clrTrailingText);

        return TRUE;
    }

    return FALSE;
}

void CVWnd_MonthCal::UpdateDateRange ()
{
    if (m_hWnd != NULL)
    {
        SetRange ((IsDoubleEqualZero (m_dbMinDate) ? NULL : &COleDateTime (m_dbMinDate)),
                (IsDoubleEqualZero (m_dbMaxDate) ? NULL : &COleDateTime (m_dbMaxDate)));
    }
}

void CVWnd_MonthCal::UpdateSelRange ()
{
    if (m_hWnd == NULL)
        return;

    COleDateTime timeBegin (IsDoubleEqualZero (m_dtBeginSel) ?
            COleDateTime::GetCurrentTime () : COleDateTime (m_dtBeginSel));

    if ((GetStyle () & MCS_MULTISELECT) == 0)  // 不允许选择多天?
    {
        SetCurSel (timeBegin);
    }
    else
    {
        COleDateTime timeEnd (IsDoubleEqualZero (m_dtEndSel) ? timeBegin : COleDateTime (m_dtEndSel));

        COleDateTimeSpan timeSpan (timeEnd - timeBegin);
        if (timeSpan.GetDays () > GetMaxSelCount ())
        {
            timeEnd = timeBegin + COleDateTimeSpan (MAX (0, GetMaxSelCount () - 1));

            if (IsDoubleEqualZero (m_dtEndSel) == FALSE)
                m_dtEndSel = (DATE)timeEnd;
        }

        SetSelRange (timeBegin, timeEnd);
    }
}

void CVWnd_MonthCal::OnSelchange (NMHDR* pNMHDR, LRESULT* pResult) 
{
    if (m_pEventListener != NULL)
        m_pEventListener->OnDateChanged ();

    *pResult = 0;
}

//-----------------------------------------------------------------------------------------

#include <direct.h>

BEGIN_MESSAGE_MAP (CVWnd_DriveBox, CComboBox)
    //{{AFX_MSG_MAP(CVWnd_DriveBox)
    ON_WM_CTLCOLOR_REFLECT ()
    ON_CONTROL_REFLECT (CBN_SELENDOK, OnSelendok)
    ON_CONTROL_REFLECT (CBN_KILLFOCUS, OnKillfocus)
    ON_CONTROL_REFLECT (CBN_SETFOCUS, OnSetfocus)
    //}}AFX_MSG_MAP
END_MESSAGE_MAP ()

void CVWnd_DriveBox::OnConstruct ()
{
    m_chCurrentDrive = '\0';
    m_npMatchDriveType = 0;
    m_clrText = RGB (0, 0, 0);
    m_clrBack = RGB (255, 255, 255);

    m_brhBack.CreateSolidBrush (GetRealBackColor (m_clrBack));
}

BOOL CVWnd_DriveBox::CreateControl (CVolNativeWndCreateParam* pCreateParam)
{
    pCreateParam->m_dwStyle |= CBS_DROPDOWNLIST | CBS_AUTOHSCROLL | WS_VSCROLL;

    if (CWnd::CreateEx (pCreateParam->m_dwExStyle, _T ("COMBOBOX"), pCreateParam->m_szCaption, pCreateParam->m_dwStyle,
            pCreateParam->m_nLeft, pCreateParam->m_nTop, pCreateParam->m_nWidth, pCreateParam->m_nHeight,
            pCreateParam->m_pParentWnd->GetSafeHwnd (), (HMENU)pCreateParam->m_nID))
    {
        init ();
        return TRUE;
    }

    return FALSE;
}

void CVWnd_DriveBox::OnKillfocus ()
{
    SendKillFocusNativeMessage (m_hWnd, m_pEventListener);
}

void CVWnd_DriveBox::OnSetfocus ()
{
    SendSetFocusNativeMessage (m_hWnd, m_pEventListener);
}

void CVWnd_DriveBox::OnSelendok ()
{
    const INT_P npCurSel = GetCurSel ();

    if (npCurSel == CB_ERR)
        m_chCurrentDrive = '\0';
    else
        m_chCurrentDrive = (TCHAR)GetItemData ((INT)npCurSel);

    if (m_pEventListener != NULL)
        m_pEventListener->OnDriveChanged ();
}

HBRUSH CVWnd_DriveBox::CtlColor (CDC* pDC, UINT nCtlColor) 
{
    pDC->SetTextColor (m_clrText);
    pDC->SetBkColor (GetRealBackColor (m_clrBack));
    return m_brhBack;
}

void CVWnd_DriveBox::SetBackColor (COLORREF clrBack)
{
    if (m_clrBack != clrBack)
    {
        m_clrBack = clrBack;
        m_brhBack.DeleteObject ();
        m_brhBack.CreateSolidBrush (GetRealBackColor (m_clrBack));

        if (m_hWnd != NULL)
            Invalidate ();
    }
}

TCHAR CVWnd_DriveBox::GetCurrentDriveChar () const
{
    return (m_chCurrentDrive == '\0' ? (_getdrive () + 'A' - 1) : m_chCurrentDrive);
}

CVolString CVWnd_DriveBox::GetCurrentDrive () const
{
    TCHAR cary [2];
    cary [0] = GetCurrentDriveChar ();
    cary [1] = '\0';

    return CVolString (cary);
}

void CVWnd_DriveBox::SetCurrentDrive (const TCHAR* szDrive)
{
    m_chCurrentDrive = ToUpperCase (szDrive [0]);

    if (m_hWnd == NULL)
        return;

    const TCHAR chDrive = GetCurrentDriveChar ();

    const INT_P npCount = GetCount ();
    INT_P npIndex = 0;
    for (; npIndex < npCount; npIndex++)
    {
        if ((TCHAR)GetItemData ((INT)npIndex) == chDrive)
            break;
    }

    if (npIndex < npCount)
    {
        SetCurSel ((INT)npIndex);
        m_chCurrentDrive = chDrive;
    }
    else
    {
        SetCurSel (-1);
        m_chCurrentDrive = '\0';
    }
}

void CVWnd_DriveBox::SetMatchDriveType (const INT_P npMatchDriveType)
{
    m_npMatchDriveType = CLIP (npMatchDriveType, 0, 5);
    init ();
}

void CVWnd_DriveBox::init ()
{
    if (m_hWnd == NULL)
        return;

    ResetContent ();

    TCHAR buf [MAX_PATH];
    buf [0] = '\0';
    GetLogicalDriveStrings (NUM_ELEMENTS_OF (buf) - 1, buf);

    TCHAR caryDrive [3];
    caryDrive [0] = ' ';
    caryDrive [1] = ':';
    caryDrive [2] = '\0';

    TCHAR caryDriveType [4];
    caryDriveType [0] = '\0';
    caryDriveType [1] = ':';
    caryDriveType [2] = OS_PATH_CHAR;
    caryDriveType [3] = '\0';

    const TCHAR chCurDrive = GetCurrentDriveChar ();
    INT_P npCurSel = -1;

    const TCHAR* ps = buf;
    while (*ps != '\0')
    {
        caryDriveType [0] = caryDrive [0] = ToUpperCase (*ps);
        
        const INT_P npDriveType = (INT_P)GetDriveType (caryDriveType);

        if (m_npMatchDriveType == 0 ||
                (m_npMatchDriveType == 1 && npDriveType == DRIVE_REMOVABLE) ||
                (m_npMatchDriveType == 2 && npDriveType == DRIVE_FIXED) ||
                (m_npMatchDriveType == 3 && npDriveType == DRIVE_CDROM) ||
                (m_npMatchDriveType == 4 && npDriveType == DRIVE_REMOTE) ||
                (m_npMatchDriveType == 5 && npDriveType == DRIVE_RAMDISK))
        {
            CVolString strCaption (caryDrive);

            switch (npDriveType)
            {
            case DRIVE_FIXED:
                strCaption.AddText (_T_NAME_DRIVE_FIXED);
                break;
            case DRIVE_CDROM:
                strCaption.AddText (_T_NAME_DRIVE_CDROM);
                break;
            case DRIVE_REMOTE:
                strCaption.AddText (_T_NAME_DRIVE_REMOTE);
                break;
            case DRIVE_RAMDISK:
                strCaption.AddText (_T_NAME_DRIVE_RAMDISK);
                break;
            }

            if (npDriveType == DRIVE_FIXED)
            {
                TCHAR caryVol [256];
                caryVol [0] = ' ';

                if (GetVolumeInformation (ps, &caryVol [1], NUM_ELEMENTS_OF (caryVol) - 2, NULL, NULL, NULL, NULL, NULL) &&
                        caryVol [1] != '\0')
                {
                    strCaption.AddText (caryVol);
                }
            }

            const INT_P npItemIndex = AddString (strCaption.GetText ());
            if (chCurDrive == caryDrive [0])
                npCurSel = npItemIndex;
            SetItemData ((INT)npItemIndex, (DWORD)caryDrive [0]);
        }

        ps += _tcslen (buf) + 1;
    }

    if (npCurSel == -1)
    {
        m_chCurrentDrive = '\0';
        SetCurSel (-1);
    }
    else
    {
        m_chCurrentDrive = chCurDrive;
        SetCurSel ((INT)npCurSel);
    }
}

//-----------------------------------------------------------------------------------------

BEGIN_MESSAGE_MAP (CVWnd_DirBox, CTreeCtrl)
    //{{AFX_MSG_MAP(CVWnd_DirBox)
    ON_NOTIFY_REFLECT (NM_DBLCLK, OnDblclk)
    ON_WM_CTLCOLOR_REFLECT ()
    ON_NOTIFY_REFLECT (NM_KILLFOCUS, OnKillfocus)
    ON_NOTIFY_REFLECT (NM_SETFOCUS, OnSetfocus)
    ON_MESSAGE (MWM_PRE_FILTER_INPUT_MSG, OnPreFilterInputMessage)
    //}}AFX_MSG_MAP
END_MESSAGE_MAP ()

void CVWnd_DirBox::OnConstruct ()
{
    m_clrBack = RGB (255, 255, 255);

    m_brhBack.CreateSolidBrush (GetRealBackColor (m_clrBack));
}

BOOL CVWnd_DirBox::CreateControl (CVolNativeWndCreateParam* pCreateParam)
{
    pCreateParam->m_dwStyle |= TVS_SHOWSELALWAYS | TVS_DISABLEDRAGDROP;

    if (CTreeCtrl::Create (pCreateParam->m_dwStyle,
            CMRect (pCreateParam->m_nLeft, pCreateParam->m_nTop,
                pCreateParam->m_nLeft + pCreateParam->m_nWidth, pCreateParam->m_nTop + pCreateParam->m_nHeight),
            pCreateParam->m_pParentWnd, (UINT)pCreateParam->m_nID))
    {
        CVolAppInstance::sSetWndNeedPreFilterInputMessage (m_hWnd);  // 标志本窗口需要过滤其自身及其下属子窗口的输入信息
        ModifyWndStyleEx (this, 0, pCreateParam->m_dwExStyle);

        if ((HIMAGELIST)m_objImgList == NULL)
            m_objImgList.Create (_IDB_VOL_DIRSTATE, 16, 0, RGB (255, 0, 255));
        SetImageList (&m_objImgList, TVSIL_NORMAL);

        init ();
        return TRUE;
    }

    return FALSE;
}

LRESULT CVWnd_DirBox::OnPreFilterInputMessage (WPARAM wParam, LPARAM lParam)
{
#ifndef _VOL_FOR_UI_DESIGNER  // 不为编译布局设计器专用版本?
    if (lParam == _VOL_MSG_ACK &&  // 调用格式正确?
            GetKeyCodeFromMessage ((const MSG*)wParam) == KYV_ENTER)
    {
        OnDblclk (NULL, NULL);
        return _VOL_MSG_ACK;
    }
#endif

    return 0;
}

void CVWnd_DirBox::OnKillfocus (NMHDR* pNMHDR, LRESULT* pResult) 
{
    SendKillFocusNativeMessage (m_hWnd, m_pEventListener);
    *pResult = 0;
}

void CVWnd_DirBox::OnSetfocus (NMHDR* pNMHDR, LRESULT* pResult) 
{
    SendSetFocusNativeMessage (m_hWnd, m_pEventListener);
    *pResult = 0;
}

HBRUSH CVWnd_DirBox::CtlColor (CDC* pDC, UINT nCtlColor) 
{
    pDC->SetBkColor (GetRealBackColor (m_clrBack));
    return m_brhBack;
}

void CVWnd_DirBox::SetBackColor (COLORREF clrBack)
{
    if (m_clrBack != clrBack)
    {
        m_clrBack = clrBack;
        m_brhBack.DeleteObject ();
        m_brhBack.CreateSolidBrush (GetRealBackColor (m_clrBack));

        if (m_hWnd != NULL)
            Invalidate ();
    }
}

void CVWnd_DirBox::SetCurrentPath (const TCHAR* szPath)
{
    if (IsEmptyStr (szPath))
    {
        TCHAR path [MAX_PATH];
        if (GetCurrentDirectory (MAX_PATH - 1, path) == 0)
            return;

        m_strPath.SetText (path);
    }
    else
    {
        m_strPath.SetText (szPath);

        const UINT_P ch = (UINT_P)szPath [0];
        if (szPath [1] == '\0' && IS_ALPHA (ch))  // 仅有一个驱动器符?
            m_strPath.AddText (_T (":") OS_PATH_CHAR_TEXT);
    }

    m_strPath.RemoveEndPathChar ();  // 删除多余的尾部路径字符
    init ();
}

void CVWnd_DirBox::init ()
{
    if (m_hWnd == NULL)
        return;

    DeleteAllItems ();

    CMStringArray sary;
    const INT_P npNumNodes = SplitStrings (m_strPath.GetText (), sary, OS_PATH_CHAR, TRUE, TRUE);
    if (npNumNodes == 0)
        return;

    TV_INSERTSTRUCT tvstruct;
    tvstruct.hParent = (HTREEITEM)TVI_ROOT;
    tvstruct.hInsertAfter = (HTREEITEM)TVI_LAST;
    tvstruct.item.iImage = 2;
    tvstruct.item.iSelectedImage = 3;
    tvstruct.item.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_TEXT;

    INT_P npIndex;
    for (npIndex = 0; npIndex < npNumNodes; npIndex++)
    {
        tvstruct.item.pszText = (TCHAR*)sary [npIndex];
        tvstruct.hParent = InsertItem (&tvstruct);
    }
    const HTREEITEM hLastItem = tvstruct.hParent;

    HTREEITEM hTreeItem = GetRootItem ();
    for (npIndex = 0; npIndex < npNumNodes; npIndex++)
    {
        Expand (hTreeItem, TVE_EXPAND);
        hTreeItem = GetChildItem (hTreeItem);
    }

    SelectItem (hLastItem);
    AddDirsToTree (hLastItem);
}

void CVWnd_DirBox::AddDirsToTree (HTREEITEM htParent)
{
    CString strStartDir (m_strPath.GetText ());
    if (strStartDir.IsEmpty () == FALSE && strStartDir.Right (1) != OS_PATH_CHAR_TEXT)
        strStartDir += OS_PATH_CHAR_TEXT;
    strStartDir += _T ("*.*");

    WIN32_FIND_DATA stFindData;
    HANDLE hFile = FindFirstFile ((const TCHAR*)strStartDir, &stFindData);
    if (hFile == INVALID_HANDLE_VALUE)
        return;

    TV_INSERTSTRUCT tvstruct;
    tvstruct.hParent = htParent;
    tvstruct.hInsertAfter = (HTREEITEM)TVI_SORT;
    tvstruct.item.iImage = 0;
    tvstruct.item.iSelectedImage = 1;
    tvstruct.item.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_TEXT;

    do
    {
        if ((stFindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0 &&
                _tcscmp (stFindData.cFileName, _T (".")) != 0 && 
                _tcscmp (stFindData.cFileName, _T ("..")) != 0)
        {
            tvstruct.item.pszText = stFindData.cFileName;
            InsertItem (&tvstruct);
        }
    }
    while (FindNextFile (hFile, &stFindData));

    FindClose (hFile);
    Expand (htParent, TVE_EXPAND);
}

void CVWnd_DirBox::BuildPathFromTree (HTREEITEM htItem)
{
    if (htItem == NULL)
        return;

    m_strPath.SetText (GetItemText (htItem));
    
    while (TRUE)
    {
        htItem = GetParentItem (htItem);
        if (htItem == NULL)
            break;

        m_strPath.SetText (CVolString (GetItemText (htItem)) + OS_PATH_CHAR_TEXT + m_strPath);
    }

    if (m_pEventListener != NULL)
        m_pEventListener->OnDirChanged ();
}

void CVWnd_DirBox::DeleteChildren (HTREEITEM htItem)
{
    HTREEITEM hTreeChild1 = GetChildItem (htItem);

    if (hTreeChild1 != NULL)
    {
        HTREEITEM hTreeChild2 = GetChildItem (hTreeChild1);

        while (hTreeChild2 != NULL)
        {
            DeleteItem (hTreeChild2);
            hTreeChild2 = GetChildItem (hTreeChild1);
        }

        DeleteItem (hTreeChild1);
    }
}

void CVWnd_DirBox::DeletePeers (HTREEITEM htItem)
{
    HTREEITEM hTreePeer = GetPrevSiblingItem (htItem);
    while (hTreePeer)
    {
        DeleteItem (hTreePeer);
        hTreePeer = GetPrevSiblingItem (htItem);
    }

    hTreePeer = GetNextSiblingItem (htItem);
    while (hTreePeer)
    {
        DeleteItem (hTreePeer);
        hTreePeer = GetNextSiblingItem (htItem);
    }
}

void CVWnd_DirBox::OnDblclk (NMHDR*, LRESULT* pResult) 
{
    HTREEITEM hTreeChild;
    INT nImage, nSelImage;

    HTREEITEM hTreeItem = GetSelectedItem ();
    if (hTreeItem == NULL)
        return;
    UINT uItemState = GetItemState (hTreeItem, TVIF_STATE);

    if ((uItemState & TVIS_EXPANDED) != 0)
    {
        if (ItemHasChildren (hTreeItem))
        {
            hTreeChild = GetChildItem (hTreeItem);
            if (hTreeChild)
            {
                GetItemImage (hTreeChild, nImage, nSelImage);
                if (nImage == 0 || nSelImage == 1)
                {
                    if (pResult != NULL)
                        *pResult = 1;
                    return;
                }
            }
        }

        DeleteChildren (hTreeItem);
    }
    else
    {
        SetItemImage (hTreeItem, 2, 3);

        DeletePeers (hTreeItem);
    }

    BuildPathFromTree (hTreeItem);
    AddDirsToTree (hTreeItem);

    if (pResult != NULL)
        *pResult = 1;
}

//-----------------------------------------------------------------------------------------

BEGIN_MESSAGE_MAP (CVWnd_FileBox, CListBox)
    //{{AFX_MSG_MAP(CVWnd_FileBox)
    ON_WM_CTLCOLOR_REFLECT()
    ON_CONTROL_REFLECT (LBN_SELCHANGE, OnSelchange)
    ON_CONTROL_REFLECT (LBN_KILLFOCUS, OnKillfocus)
    ON_CONTROL_REFLECT (LBN_SETFOCUS, OnSetfocus)
    ON_CONTROL_REFLECT (LBN_DBLCLK, OnDblclk)
    //}}AFX_MSG_MAP
END_MESSAGE_MAP ()

void CVWnd_FileBox::OnConstruct ()
{
    m_blpAllowSelMutilFile = FALSE;
    m_blpAllowNormalFile = TRUE;
    m_blpAllowArcFile = TRUE;
    m_blpAllowReadOnlyFile = TRUE;
    m_blpAllowSysFile = FALSE;
    m_blpAllowHiddenFile = FALSE;
    m_dbMinDate = m_dbMaxDate = 0;

    m_clrText = RGB (0, 0, 0);
    m_clrBack = RGB (255, 255, 255);

    m_brhBack.CreateSolidBrush (GetRealBackColor (m_clrBack));
}

BOOL CVWnd_FileBox::CreateControl (CVolNativeWndCreateParam* pCreateParam)
{
    if (m_blpAllowSelMutilFile)
        pCreateParam->m_dwStyle |= LBS_EXTENDEDSEL;
    pCreateParam->m_dwStyle |= (LBS_SORT | LBS_NOINTEGRALHEIGHT | LBS_NOTIFY | WS_VSCROLL);

    if (CWnd::CreateEx (pCreateParam->m_dwExStyle, _T ("LISTBOX"), pCreateParam->m_szCaption, pCreateParam->m_dwStyle,
            pCreateParam->m_nLeft, pCreateParam->m_nTop, pCreateParam->m_nWidth, pCreateParam->m_nHeight,
            pCreateParam->m_pParentWnd->GetSafeHwnd (), (HMENU)pCreateParam->m_nID))
    {
        // 提前设置组件字体以支持在后面设置组件行间距
        ASSERT (pCreateParam->m_hFont != NULL);
        SendMessage (WM_SETFONT, (WPARAM)pCreateParam->m_hFont, MAKELPARAM (TRUE, 0));
        pCreateParam->m_hFont = NULL;

        SetItemHeight (0, GetItemHeight (0) + 2);  // 设置组件行间距

        Update ();
        return TRUE;
    }

    return FALSE;
}

void CVWnd_FileBox::OnKillfocus ()
{
    SendKillFocusNativeMessage (m_hWnd, m_pEventListener);
}

void CVWnd_FileBox::OnSetfocus ()
{
    SendSetFocusNativeMessage (m_hWnd, m_pEventListener);
}

void CVWnd_FileBox::OnDblclk ()
{
    if (m_pEventListener != NULL)
        m_pEventListener->OnDblClickItem ();
}

HBRUSH CVWnd_FileBox::CtlColor (CDC* pDC, UINT nCtlColor) 
{
    pDC->SetTextColor (m_clrText);
    pDC->SetBkColor (GetRealBackColor (m_clrBack));
    return m_brhBack;
}

void CVWnd_FileBox::SetBackColor (COLORREF clrBack)
{
    if (m_clrBack != clrBack)
    {
        m_clrBack = clrBack;
        m_brhBack.DeleteObject ();
        m_brhBack.CreateSolidBrush (GetRealBackColor (m_clrBack));

        if (m_hWnd != NULL)
            RedrawWindow (NULL, NULL, RDW_ERASE | RDW_FRAME | RDW_INVALIDATE);
    }
}

void CVWnd_FileBox::OnSelchange ()
{
    m_strCurSelFiles.Empty ();

    const INT_P npCount = GetCount ();
    if (npCount > 0)
    {
        if (m_blpAllowSelMutilFile)
        {
            CString str;

            for (INT_P npIndex = 0; npIndex < npCount; npIndex++)
            {
                if (GetSel ((INT)npIndex) != 0)
                {
                    GetText ((INT)npIndex, str);

                    if (m_strCurSelFiles.IsEmpty () == FALSE)
                        m_strCurSelFiles.AddChar (';');
                    m_strCurSelFiles.AddText (str);
                }
            }
        }
        else
        {
            const INT_P npCurSel = GetCurSel ();

            if (npCurSel != LB_ERR)
            {
                CString str;
                GetText ((INT)npCurSel, str);
                m_strCurSelFiles.SetText (str);
            }
        }

        if (m_pEventListener != NULL)
            m_pEventListener->OnSelFileChanged ();
    }
}

void CVWnd_FileBox::Update ()
{
    if (m_hWnd == NULL)
        return;

    ResetContent ();

    m_strPath.TrimAll ();
    if (m_strPath.IsEmpty ())
    {
        TCHAR path [MAX_PATH];
        if (GetCurrentDirectory (MAX_PATH, path) == 0)
            return;
        m_strPath.SetText (path);
    }

    CVolString strFindPath (m_strPath);
    strFindPath.CheckAddPathChar ();

    CMStringArray saryCurSelFiles;
    SplitStrings (m_strCurSelFiles.GetText (), saryCurSelFiles, ';', TRUE, TRUE);

    CMStringArray saryPatterns;
    INT_P npNumPatterns = SplitStrings (m_strPatterns.GetText (), saryPatterns, ';', TRUE, TRUE);
    if (npNumPatterns == 0)
    {
        saryPatterns.Add (_T ("*.*"));
        npNumPatterns++;
    }

    CIUniqueStringArray usaryExistFileNames;
    for (INT_P npPatternIndex = 0; npPatternIndex < npNumPatterns; npPatternIndex++)
    {
        CVolString strFind (strFindPath);
        strFind.AddText (saryPatterns [npPatternIndex]);

        WIN32_FIND_DATA fnddata;
        const HANDLE pos = FindFirstFile (strFind.GetText (), &fnddata);

        if (pos != INVALID_HANDLE_VALUE)
        {
            do
            {
                if ((fnddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0 &&
                        (m_blpAllowNormalFile || (fnddata.dwFileAttributes & FILE_ATTRIBUTE_NORMAL) == 0) &&
                        (m_blpAllowArcFile || (fnddata.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE) == 0) &&
                        (m_blpAllowReadOnlyFile || (fnddata.dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0) &&
                        (m_blpAllowSysFile || (fnddata.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) == 0) &&
                        (m_blpAllowHiddenFile || (fnddata.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) == 0) &&
                        (IsDoubleEqualZero (m_dbMinDate) || COleDateTime (fnddata.ftLastWriteTime) >= COleDateTime (m_dbMinDate)) &&
                        (IsDoubleEqualZero (m_dbMaxDate) || COleDateTime (fnddata.ftLastWriteTime) <= COleDateTime (m_dbMaxDate)))
                {
                    if (usaryExistFileNames.Add (fnddata.cFileName) != -1)
                        SetItemData (AddString (fnddata.cFileName), (saryCurSelFiles.IIsElementExist (fnddata.cFileName) ? 1 : 0));
                }
            }
            while (FindNextFile (pos, &fnddata));

            FindClose (pos);
        }
    }

    m_strCurSelFiles.Empty ();

    const INT_P npNumItems = GetCount ();
    for (INT_P npIndex = 0; npIndex < npNumItems; npIndex++)
    {
        if (GetItemData ((INT)npIndex) != 0)
        {
            CString strItem;
            GetText ((INT)npIndex, strItem);

            if (m_blpAllowSelMutilFile)
            {
                SetSel ((INT)npIndex, TRUE);

                if (m_strCurSelFiles.IsEmpty () == FALSE)
                    m_strCurSelFiles.AddChar (';');
                m_strCurSelFiles.AddText (strItem);
            }
            else
            {
                SetCurSel ((INT)npIndex);
                m_strCurSelFiles.SetText (strItem);
                break;
            }
        }
    }
}

//-----------------------------------------------------------------------------------------

BEGIN_MESSAGE_MAP (CVWnd_Spin, CSpinButtonCtrl)
    //{{AFX_MSG_MAP(CVWnd_Spin)
    ON_NOTIFY_REFLECT(UDN_DELTAPOS, OnDeltapos)
    //}}AFX_MSG_MAP
END_MESSAGE_MAP ()

BOOL CVWnd_Spin::CreateControl (CVolNativeWndCreateParam* pCreateParam)
{
    const CMRect rt (pCreateParam->m_nLeft, pCreateParam->m_nTop,
            pCreateParam->m_nLeft + pCreateParam->m_nWidth, pCreateParam->m_nTop + pCreateParam->m_nHeight);

    if (CSpinButtonCtrl::Create (pCreateParam->m_dwStyle, rt, pCreateParam->m_pParentWnd, (UINT)pCreateParam->m_nID))
    {
        ModifyWndStyleEx (this, 0, pCreateParam->m_dwExStyle);

        MoveWindow (&rt);
        return TRUE;
    }

    return FALSE;
}

void CVWnd_Spin::OnDeltapos (NMHDR* pNMHDR, LRESULT* pResult) 
{
    NM_UPDOWN* pNMUpDown = (NM_UPDOWN*)pNMHDR;

    if (m_pEventListener != NULL)
        m_pEventListener->OnDeltapos (pNMUpDown->iDelta < 0 ? 1 : -1);

    *pResult = 0;
}

//-----------------------------------------------------------------------------------------

BEGIN_MESSAGE_MAP (CVWnd_Dummy, CWnd)
    //{{AFX_MSG_MAP(CVWnd_Dummy)
#ifdef _VOL_FOR_UI_DESIGNER  // 为编译布局设计器专用版本?
    ON_WM_PAINT ()
#endif
    //}}AFX_MSG_MAP
END_MESSAGE_MAP ()

#ifdef _VOL_FOR_UI_DESIGNER  // 为编译布局设计器专用版本?

void CVWnd_Dummy::OnPaint ()
{
    CPaintDC dc (this);

    CRect rtClipBox;
    dc.GetClipBox (&rtClipBox);
    if (rtClipBox.IsRectEmpty () == TRUE)
        return;

    CBrush brh (::GetSysColor (COLOR_BTNFACE));
    CBrush* pOldBrush = dc.SelectObject (&brh);
    dc.PatBlt (rtClipBox.left, rtClipBox.top, rtClipBox.Width (), rtClipBox.Height (), PATCOPY);
    dc.SelectObject (pOldBrush);

    CRect rtClient;
    GetClientRect (&rtClient);

    CBitmap bmp;
    if (m_uIDMarkBitmap == 0 || bmp.LoadBitmap (m_uIDMarkBitmap) == FALSE)
        return;

    BITMAP infBitmap;
    bmp.GetObject (sizeof (infBitmap), &infBitmap);

    CDC dcBitmap;
    dcBitmap.CreateCompatibleDC (&dc);
    CBitmap* pOldBmp = dcBitmap.SelectObject (&bmp);

    dc.BitBlt (MAX (0, (rtClient.Width () - infBitmap.bmWidth) / 2),
            MAX (0, (rtClient.Height () - infBitmap.bmHeight) / 2),
            infBitmap.bmWidth, infBitmap.bmHeight, &dcBitmap, 0, 0, SRCCOPY);

    dcBitmap.SelectObject (pOldBmp);
}

#endif

BOOL CVWnd_Dummy::CreateDummyControl (CVolNativeWndCreateParam* pCreateParam)
{
#ifdef _VOL_FOR_UI_DESIGNER  // 为编译布局设计器专用版本?
    pCreateParam->m_dwStyle |= WS_BORDER;
#else
    pCreateParam->m_dwStyle &= ~WS_VISIBLE;
#endif

    return CWnd::CreateEx (pCreateParam->m_dwExStyle, VolRegisterWndClass (_T ("VolDummyWnd")),
            pCreateParam->m_szCaption, pCreateParam->m_dwStyle,
            pCreateParam->m_nLeft, pCreateParam->m_nTop, pCreateParam->m_nWidth, pCreateParam->m_nHeight,
            pCreateParam->m_pParentWnd->GetSafeHwnd (), (HMENU)pCreateParam->m_nID);
}

//-----------------------------------------------------------------------------------------

BEGIN_MESSAGE_MAP (CVWnd_Timer, CVWnd_Dummy)
    //{{AFX_MSG_MAP (CVWnd_Timer)
#ifndef _VOL_FOR_UI_DESIGNER  // 不为编译布局设计器专用版本?
    ON_WM_TIMER ()
    ON_WM_DESTROY ()
#endif
    //}}AFX_MSG_MAP
END_MESSAGE_MAP ()

#define _VOL_TIMER_ID  1000

void CVWnd_Timer::OnConstruct ()
{
    m_blpTimerCreated = FALSE;
    m_npElapse = 0;

#ifdef _VOL_FOR_UI_DESIGNER  // 为编译布局设计器专用版本?
    m_uIDMarkBitmap = _IDB_VOL_TIMER;
#endif
}

BOOL CVWnd_Timer::CreateControl (CVolNativeWndCreateParam* pCreateParam)
{
    if (CreateDummyControl (pCreateParam) == FALSE)
        return FALSE;

#ifndef _VOL_FOR_UI_DESIGNER  // 不为编译布局设计器专用版本?
    ASSERT (m_hWnd != NULL);
    VolResetTimer (m_npElapse);
#endif
    return TRUE;
}

#ifndef _VOL_FOR_UI_DESIGNER  // 不为编译布局设计器专用版本?

void CVWnd_Timer::OnTimer (UINT_PTR nIDEvent) 
{
    CWnd::OnTimer (nIDEvent);

    if (m_pEventListener != NULL)
        m_pEventListener->OnTimer ();
}

void CVWnd_Timer::OnDestroy() 
{
    if (m_blpTimerCreated)
        KillTimer (_VOL_TIMER_ID);

    CVWnd_Dummy::OnDestroy();
}

#endif

void CVWnd_Timer::VolResetTimer (const INT_P npElapse)
{
    m_npElapse = MAX (0, npElapse);

#ifndef _VOL_FOR_UI_DESIGNER  // 不为编译布局设计器专用版本?
    if (m_hWnd != NULL)
    {
        if (m_blpTimerCreated)
        {
            KillTimer (_VOL_TIMER_ID);
            m_blpTimerCreated = FALSE;
        }

        if (m_npElapse > 0)
        {
            SetTimer (_VOL_TIMER_ID, (UINT)m_npElapse, NULL);
            m_blpTimerCreated = TRUE;
        }
    }
#endif
}

//-----------------------------------------------------------------------------------------

void CVWnd_PicButton::OnConstruct ()
{
    m_npButtonType = 0;
    m_blpPressed = FALSE;
    m_clrTransColor = m_clrBack = CLR_DEFAULT;
    m_npCurrentBitmapIndex = VWPB_NORMAL_BITMAP;
}

BEGIN_MESSAGE_MAP (CVWnd_PicButton, CWnd)
    //{{AFX_MSG_MAP(CVWnd_PicButton)
    ON_WM_LBUTTONDOWN ()
    ON_WM_LBUTTONUP ()
    ON_WM_PAINT ()
    ON_WM_MOUSEMOVE ()
    ON_WM_ENABLE ()
    //}}AFX_MSG_MAP
END_MESSAGE_MAP ()

BOOL CVWnd_PicButton::CreateControl (CVolNativeWndCreateParam* pCreateParam)
{
    if (CWnd::CreateEx (pCreateParam->m_dwExStyle, VolRegisterWndClass (_T ("VolPicButton")),
            pCreateParam->m_szCaption, pCreateParam->m_dwStyle,
            pCreateParam->m_nLeft, pCreateParam->m_nTop, pCreateParam->m_nWidth, pCreateParam->m_nHeight,
            pCreateParam->m_pParentWnd->GetSafeHwnd (), (HMENU)pCreateParam->m_nID))
    {
        UpdateButtonShape (FALSE);
        SetPressState (m_blpPressed);
        return TRUE;
    }

    return FALSE;
}

void CVWnd_PicButton::OnLButtonDown (UINT nFlags, CPoint point) 
{
    CRect rtClient;
    GetClientRect (&rtClient);

    if (rtClient.PtInRect (point) && IsWindowEnabled () && m_npButtonType == 0)
    {
        Draw (VWPB_PRESSED_BITMAP);

        if (::GetCapture () != m_hWnd)
            SetCapture ();
    }
}

void CVWnd_PicButton::OnLButtonUp (UINT nFlags, CPoint point) 
{
    ::ReleaseCapture ();

    CRect rtClient;
    GetClientRect (&rtClient);

    if (rtClient.PtInRect (point) && IsWindowEnabled ())
    {
        if (m_npButtonType == 1)
            SetPressState (!m_blpPressed);
        else
            Draw (VWPB_NORMAL_BITMAP);

        if (m_pEventListener != NULL)
            m_pEventListener->OnClick ();
    }
}

void CVWnd_PicButton::SetPressState (const BOOL_P blpPressed)
{
    if (m_npButtonType == 1)
    {
        m_blpPressed = blpPressed;
        Draw (m_blpPressed ? VWPB_PRESSED_BITMAP : VWPB_NORMAL_BITMAP);
    }
}

void CVWnd_PicButton::OnMouseMove (UINT nFlags, CPoint point) 
{
    if (m_npButtonType == 1 || IsWindowEnabled () == FALSE)
    {
        ::ReleaseCapture ();
        return;
    }

    CRect rtClient;
    GetClientRect (&rtClient);
    const BOOL_P blpInWndRect = rtClient.PtInRect (point);

    ClientToScreen (&point);
    HWND hWnd = ::WindowFromPoint (point);

    if (hWnd == m_hWnd || (hWnd == NULL && blpInWndRect))
    {
        if (::GetCapture () != m_hWnd)
            SetCapture ();

        Draw ((nFlags & MK_LBUTTON) != 0 ? VWPB_PRESSED_BITMAP : VWPB_HIGHLIGHT_BITMAP);
    }
    else
    {
        if ((nFlags & MK_LBUTTON) == 0)
            ::ReleaseCapture ();
        Draw (VWPB_NORMAL_BITMAP);
    }
}

void CVWnd_PicButton::OnPaint() 
{
    CPaintDC dc (this);

    CRect rtClient;
    GetClientRect (&rtClient);
    const INT nWidth = rtClient.Width ();
    const INT nHeight = rtClient.Height ();

    CBitmap bitmap;
    bitmap.CreateCompatibleBitmap (&dc, nWidth, nHeight);

    CDC dcMem;
    dcMem.CreateCompatibleDC (&dc);
    CBitmap* pOldBitmap = dcMem.SelectObject (&bitmap);

    CBrush brhBack (GetRealBackColor (m_clrBack));
    CBrush* pOldBrush = dcMem.SelectObject (&brhBack);
    dcMem.PatBlt (0, 0, nWidth, nHeight, PATCOPY);
    dcMem.SelectObject (pOldBrush);

    const HBITMAP hBitmap = GetHBitmap ();
    if (hBitmap != NULL)
    {
        BITMAP infBitmap;
        ::GetObject ((HGDIOBJ)hBitmap, sizeof (infBitmap), &infBitmap);

        HDC hMemDC = ::CreateCompatibleDC (dc.m_hDC);
        HBITMAP hOldBitmap = (HBITMAP)::SelectObject (hMemDC, hBitmap);
        ::SetStretchBltMode (dcMem.m_hDC, COLORONCOLOR);
        ::AlphaStretchBitmap (dcMem.m_hDC, 0, 0, nWidth, nHeight, hMemDC, infBitmap);
        ::SelectObject (hMemDC, hOldBitmap);
        ::DeleteDC (hMemDC);
    }

    dc.BitBlt (0, 0, nWidth, nHeight, &dcMem, 0, 0, SRCCOPY);
    dcMem.SelectObject (pOldBitmap);
}

HBITMAP CVWnd_PicButton::GetHBitmap ()
{
    ASSERT (m_npCurrentBitmapIndex >= 0 && m_npCurrentBitmapIndex < NUM_ELEMENTS_OF (m_aobjBitmaps));

    HBITMAP hBitmap = (HBITMAP)m_aobjBitmaps [m_npCurrentBitmapIndex];

    if (IsWindowEnabled () == FALSE && (HBITMAP)m_aobjBitmaps [VWPB_DISABLED_BITMAP] != NULL)
        hBitmap = (HBITMAP)m_aobjBitmaps [VWPB_DISABLED_BITMAP];

    if (hBitmap == NULL)
        hBitmap = (HBITMAP)m_aobjBitmaps [VWPB_NORMAL_BITMAP];

    return hBitmap;
}

void CVWnd_PicButton::Draw (const INT_P npBitmapIndex)
{
    ASSERT (npBitmapIndex >= 0 && npBitmapIndex < NUM_ELEMENTS_OF (m_aobjBitmaps));

    if (m_npCurrentBitmapIndex != npBitmapIndex)
    {
        m_npCurrentBitmapIndex = npBitmapIndex;
        UpdateButtonShape (TRUE);
    }
}

void CVWnd_PicButton::OnEnable (BOOL bEnable) 
{
    CWnd::OnEnable (bEnable);
    UpdateButtonShape (TRUE);
}

void CVWnd_PicButton::UpdateButtonShape (const BOOL_P blpUpdateAtonce)
{
    if (m_hWnd == NULL)
        return;

    m_rgnShape.DeleteObject ();

    if (m_clrTransColor != CLR_DEFAULT)
    {
        HBITMAP hBitmap = GetHBitmap ();
        if (hBitmap != NULL)
        {
            HRGN hRgn = BitmapToRegion (hBitmap, m_clrTransColor);
            if (hRgn != NULL)
            {
                m_rgnShape.Attach (hRgn);
                ::SetWindowRgn (m_hWnd, hRgn, TRUE);
            }
        }
    }

    if ((HRGN)m_rgnShape == NULL)
        ::SetWindowRgn (m_hWnd, NULL, TRUE);

    Invalidate ();
    if (blpUpdateAtonce)
        UpdateWindow ();
}

void CVWnd_PicButton::TakeOverBitmap (const INT_P npBitmapIndex, const HBITMAP hBitmap)
{
    ASSERT (npBitmapIndex >= 0 && npBitmapIndex < NUM_ELEMENTS_OF (m_aobjBitmaps));

    m_aobjBitmaps [npBitmapIndex].DeleteObject ();
    m_aobjBitmaps [npBitmapIndex].Attach (hBitmap);
    UpdateButtonShape (TRUE);
}

void CVWnd_PicButton::SetTransColor (const COLORREF clrTransColor)
{
    if (m_clrTransColor != clrTransColor)
    {
        m_clrTransColor = clrTransColor;
        UpdateButtonShape (TRUE);
    }
}

//-----------------------------------------------------------------------------------------

BEGIN_MESSAGE_MAP (CVEdt_Input, CEdit)
    //{{AFX_MSG_MAP(CVEdt_Input)
    ON_WM_CHAR ()
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

void CVEdt_Input::OnChar (UINT nChar, UINT nRepCnt, UINT nFlags) 
{
    if (nChar >= ' ')
    {
        if (m_npInputMode == 2)  // 整数输入
        {
            if (nChar != '-' && (nChar < '0' || nChar > '9'))
            {
                MessageBeep (MB_ICONASTERISK);
                return;
            }
        }
        else if (m_npInputMode == 3)  // 小数输入
        {
            if (nChar != '-' && nChar != '.' && (nChar < '0' || nChar > '9'))
            {
                MessageBeep (MB_ICONASTERISK);
                return;
            }
        }
    }

    CEdit::OnChar (nChar, nRepCnt, nFlags);
}

void CVDlg_Input::DoDataExchange (CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
    //{{AFX_DATA_MAP(CVDlg_Input)
    DDX_Control (pDX, _IDC_VOL_EDIT1, m_edit1);
    DDX_Control (pDX, _IDC_VOL_EDIT2, m_edit2);
    //}}AFX_DATA_MAP
}

BOOL CVDlg_Input::OnInitDialog () 
{
    CDialog::OnInitDialog();

    if (m_npInputMode == 4)  // 输入密码?
    {
        m_pEdit = &m_edit2;
        m_edit1.ShowWindow (SW_HIDE);
    }
    else
    {
        m_pEdit = &m_edit1;
        m_edit2.ShowWindow (SW_HIDE);
    }

    m_pEdit->m_npInputMode = m_npInputMode;

    if (m_strCaption.IsEmpty () == FALSE)
        SetWindowText (m_strCaption.GetText ());

    CWnd* pLbl = GetDlgItem (_IDC_VOL_LBL);
    if (pLbl != NULL)
        pLbl->SetWindowText (m_strTip);
    m_pEdit->SetWindowText (m_strInitialText.GetText ());
    m_pEdit->SetSel (0, -1);

    return TRUE;
}

void CVDlg_Input::OnOK ()
{
    m_pEdit->GetWindowText (m_strResult);
    CDialog::OnOK ();
}

//-----------------------------------------------------------------------------------------

#ifndef _USE_MFC_OCX_CONTROL_VER2

BEGIN_MESSAGE_MAP (CVWnd_OcxControl, CWnd)
	//{{AFX_MSG_MAP(CVWnd_OcxControl)
	ON_WM_SETFOCUS()
    ON_WM_SIZE()
    ON_WM_DESTROY()
    ON_WM_PAINT()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

void CVWnd_OcxControl::OnPaint ()
{
	CPaintDC dc (this);
	if (PaintWindowlessControls (&dc))
        return;

	CRect rt;
	dc.GetClipBox (&rt);
    dc.SelectStockObject (WHITE_BRUSH);
    dc.PatBlt (rt.left, rt.top, rt.Width (), rt.Height (), PATCOPY);
}

void CVWnd_OcxControl::OnSetFocus (CWnd* pOldWnd) 
{
	CWnd::OnSetFocus (pOldWnd);

	if (m_wndOcxCtl.m_hWnd != NULL)
		m_wndOcxCtl.SetFocus ();
}

void CVWnd_OcxControl::OnSize (UINT nType, int cx, int cy)
{
    CWnd::OnSize (nType, cx, cy);

	if (m_wndOcxCtl.m_hWnd != NULL)
	{
		CRect rect;
		GetClientRect (rect);

        m_wndOcxCtl.SetWindowPos (NULL, rect.left, rect.top, rect.Width (), rect.Height (), (SWP_NOACTIVATE | SWP_NOZORDER));
	}
}

void CVWnd_OcxControl::OnDestroy ()
{
    if (m_wndOcxCtl.m_hWnd != NULL)
        m_wndOcxCtl.DestroyWindow ();

    CWnd::OnDestroy ();
}

BOOL CVWnd_OcxControl::CreateControl (CVolNativeWndCreateParam* pCreateParam)
{
    if (m_strOCXFileName.IsEmpty () == FALSE)  // 提供了需要注册的OCX文件名?
    {
        const TCHAR* szOCXFileName = m_strOCXFileName.GetText ();

        // 如果为相对路径,则基于程序可执行文件当前所处目录将其转换为绝对路径.
        CVolString strInstancePath;
        CVolMem memBuf;
        if (IsAbsPath (szOCXFileName) == FALSE && GetInstancePath (g_objVolApp.GetInstanceHandle (), strInstancePath))
            szOCXFileName = LinkOSPath (strInstancePath.GetText (), szOCXFileName, memBuf, OS_PATH_CHAR);

        CVolComObject::sRegOcx (szOCXFileName, TRUE);
    }

    if (CWnd::CreateEx (pCreateParam->m_dwExStyle,
            VolRegisterWndClass (_T ("VolOcxSiteWnd"), (HBRUSH)::GetStockObject (NULL_BRUSH)),
            pCreateParam->m_szCaption, (pCreateParam->m_dwStyle | WS_CLIPCHILDREN),
            pCreateParam->m_nLeft, pCreateParam->m_nTop, pCreateParam->m_nWidth, pCreateParam->m_nHeight,
            pCreateParam->m_pParentWnd->GetSafeHwnd (), (HMENU)pCreateParam->m_nID) == FALSE)
    {
        return FALSE;
    }

    if (m_strOcxTypeName.IsEmpty () == FALSE)
    {
        BSTR bstrLicKey = (m_strLicKey.IsEmpty () ? NULL : ::SysAllocString (m_strLicKey.GetText ()));

        CRect rtClient;
	    GetClientRect (&rtClient);

        if (m_wndOcxCtl.CreateControl (m_strOcxTypeName.GetText (), NULL, (WS_CHILD | WS_VISIBLE), rtClient,
                this, (UINT)AFX_IDW_PANE_FIRST, NULL, FALSE, bstrLicKey))
        {
            if (m_wndOcxCtl.m_hWnd != NULL)
                m_wndOcxCtl.SetWindowPos (NULL, rtClient.left, rtClient.top, rtClient.Width (), rtClient.Height (), (SWP_NOACTIVATE | SWP_NOZORDER));

        #ifdef _VOL_FOR_UI_DESIGNER  // 为编译布局设计器专用版本?
            LPUNKNOWN lpUnk = m_wndOcxCtl.GetControlUnknown ();
            if (lpUnk != NULL)
            {
                IOleObject* pObject;
                if (SUCCEEDED (lpUnk->QueryInterface (IID_IOleObject, (void**)&pObject)))
                {
	                LPOLECONTROL pOleCtl;
	                if (SUCCEEDED (pObject->QueryInterface (IID_IOleControl, (void**)&pOleCtl)))
	                {
		                pOleCtl->FreezeEvents (TRUE);
		                pOleCtl->Release ();
	                }

                    pObject->Release ();
                }
            }
        #endif
        }

        if (bstrLicKey != NULL)
            ::SysFreeString (bstrLicKey);
    }

    return TRUE;
}

CVolComObject& CVWnd_OcxControl::GetComObject (CVolComObject& objResult)
{
    objResult.SetUnknownObject (m_wndOcxCtl.GetControlUnknown ());
    return objResult;
}

#else

BOOL CVWnd_OcxControl::CreateControl (CVolNativeWndCreateParam* pCreateParam)
{
    if (m_strOCXFileName.IsEmpty () == FALSE)  // 提供了需要注册的OCX文件名?
    {
        const TCHAR* szOCXFileName = m_strOCXFileName.GetText ();

        // 如果为相对路径,则基于程序可执行文件当前所处目录将其转换为绝对路径.
        CVolString strInstancePath;
        CVolMem memBuf;
        if (IsAbsPath (szOCXFileName) == FALSE && GetInstancePath (g_objVolApp.GetInstanceHandle (), strInstancePath))
            szOCXFileName = LinkOSPath (strInstancePath.GetText (), szOCXFileName, memBuf, OS_PATH_CHAR);

        CVolComObject::sRegOcx (szOCXFileName, TRUE);
    }

    if (m_strOcxTypeName.IsEmpty () == FALSE)
    {
        BSTR bstrLicKey = (m_strLicKey.IsEmpty () ? NULL : ::SysAllocString (m_strLicKey.GetText ()));

        const BOOL blSucceeded = CWnd::CreateControl (m_strOcxTypeName.GetText (), NULL, (pCreateParam->m_dwStyle | WS_CLIPCHILDREN),
                CRect (pCreateParam->m_nLeft, pCreateParam->m_nTop, pCreateParam->m_nLeft + pCreateParam->m_nWidth, pCreateParam->m_nTop + pCreateParam->m_nHeight),
                pCreateParam->m_pParentWnd, (UINT)pCreateParam->m_nID, NULL, FALSE, bstrLicKey);

        if (bstrLicKey != NULL)
            ::SysFreeString (bstrLicKey);

        if (blSucceeded)
        {
        #ifdef _VOL_FOR_UI_DESIGNER  // 为编译布局设计器专用版本?
            LPUNKNOWN lpUnk = GetControlUnknown ();
            if (lpUnk != NULL)
            {
                IOleObject* pObject;
                if (SUCCEEDED (lpUnk->QueryInterface (IID_IOleObject, (void**)&pObject)))
                {
	                LPOLECONTROL pOleCtl;
	                if (SUCCEEDED (pObject->QueryInterface (IID_IOleControl, (void**)&pOleCtl)))
	                {
		                pOleCtl->FreezeEvents (TRUE);
		                pOleCtl->Release ();
	                }

                    pObject->Release ();
                }
            }
        #endif

            if (IsEmptyStr (pCreateParam->m_szCaption) == FALSE)
                SetWindowText (pCreateParam->m_szCaption);

            ModifyWndStyleEx (this, 0, pCreateParam->m_dwExStyle);
            return TRUE;
        }
    }

    return CWnd::CreateEx (pCreateParam->m_dwExStyle,
            VolRegisterWndClass (_T ("VolOcxSiteWnd"), (HBRUSH)::GetStockObject (NULL_BRUSH)),
            pCreateParam->m_szCaption, (pCreateParam->m_dwStyle | WS_CLIPCHILDREN),
            pCreateParam->m_nLeft, pCreateParam->m_nTop, pCreateParam->m_nWidth, pCreateParam->m_nHeight,
            pCreateParam->m_pParentWnd->GetSafeHwnd (), (HMENU)pCreateParam->m_nID);
}

CVolComObject& CVWnd_OcxControl::GetComObject (CVolComObject& objResult)
{
    objResult.SetUnknownObject (GetControlUnknown ());
    return objResult;
}

#endif

IMPLEMENT_DYNAMIC (CVWnd_OcxControl, CWnd)

void CVWnd_OcxControl::OnConstruct ()
{
    ZERO_MEM (m_ablNeedSpecKeys, sizeof (m_ablNeedSpecKeys));
}

BOOL CVWnd_OcxControl::OnCmdMsg (UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo)
{
#ifndef _VOL_FOR_UI_DESIGNER  // 不为编译布局设计器专用版本?
    if (m_pEventListener != NULL && nCode == CN_EVENT && pHandlerInfo == NULL)
    {
        const AFX_EVENT* pEvent = (AFX_EVENT*)pExtra;

        if (pEvent != NULL && pEvent->m_eventKind == AFX_EVENT::event && pEvent->m_dispid != DISPID_UNKNOWN)
        {
            CVolObjectArray aryParamVariants;

            if (pEvent->m_pDispParams != NULL && pEvent->m_pDispParams->cArgs > 0)
            {
                aryParamVariants.InitCount (VOL_RUNTIME_CLASS (CVolComVariant), 0, (INT_P)pEvent->m_pDispParams->cArgs);

                INT_P npParamIndex = 0;
                for (INT_P npIndex = (INT_P)pEvent->m_pDispParams->cArgs - 1; npIndex >= 0; npIndex--, npParamIndex++)
                {
                    ::VariantCopy (&((CVolComVariant&)aryParamVariants [npParamIndex]).m_var, &pEvent->m_pDispParams->rgvarg [npIndex]);
                }
                ASSERT (npParamIndex == aryParamVariants.GetCount ());
            }

            if (m_pEventListener->OnOcxEvent ((INT)pEvent->m_dispid, aryParamVariants))
                return TRUE;
        }
    }
#endif

    return CWnd::OnCmdMsg (nID, nCode, pExtra, pHandlerInfo);
}

/* BOOL CVWnd_OcxControl::OnAmbientProperty (COleControlSite* pSite, DISPID dispid, VARIANT* pvar)
{
    switch (dispid)
    {
    case DISPID_AMBIENT_USERMODE:
        V_VT(pvar) = VT_BOOL;
    #ifdef _VOL_FOR_UI_DESIGNER  // 为编译布局设计器专用版本?
        V_BOOL(pvar) = VARIANT_FALSE;
    #else
        V_BOOL(pvar) = VARIANT_TRUE;
    #endif
        break;

    case DISPID_AMBIENT_DISPLAYASDEFAULT:
		V_VT(pvar) = VT_BOOL;
		V_BOOL(pvar) = VARIANT_FALSE;
        break;

    case DISPID_AMBIENT_BACKCOLOR:
		V_VT(pvar) = VT_COLOR;
		V_I4(pvar) = (0x80000000 | COLOR_BTNFACE);
        break;

    default:
        return CWnd::OnAmbientProperty (pSite, dispid, pvar);
    }

    return TRUE;
} */

//-----------------------------------------------------------------------------------------

BOOL CVWnd_HotKey::CreateControl (CVolNativeWndCreateParam* pCreateParam)
{
    return CHotKeyCtrl::CreateEx (pCreateParam->m_dwExStyle, pCreateParam->m_dwStyle,
            CMRect (pCreateParam->m_nLeft, pCreateParam->m_nTop,
                pCreateParam->m_nLeft + pCreateParam->m_nWidth, pCreateParam->m_nTop + pCreateParam->m_nHeight),
            pCreateParam->m_pParentWnd, (UINT)pCreateParam->m_nID);
}

UINT_P CVWnd_HotKey::sVolKeyModifiers2HotCtrl (UINT_P upVolKeyModifier)
{
    UINT_P upModifiers = 0;
    if ((upVolKeyModifier & KYS_CONTROL) != 0)  upModifiers |= HOTKEYF_CONTROL;
    if ((upVolKeyModifier & KYS_SHIFT) != 0)  upModifiers |= HOTKEYF_SHIFT;
    if ((upVolKeyModifier & KYS_ALT) != 0)  upModifiers |= HOTKEYF_ALT;

    return upModifiers;
}

UINT_P CVWnd_HotKey::sHotCtrlKeyModifiers2Vol (UINT_P upHotCtrlKeyModifier)
{
    UINT_P upModifiers = 0;
    if ((upHotCtrlKeyModifier & HOTKEYF_CONTROL) != 0)  upModifiers |= KYS_CONTROL;
    if ((upHotCtrlKeyModifier & HOTKEYF_SHIFT) != 0)  upModifiers |= KYS_SHIFT;
    if ((upHotCtrlKeyModifier & HOTKEYF_ALT) != 0)  upModifiers |= KYS_ALT;

    return upModifiers;
}

INT CVWnd_HotKey::GetCurrentHotKey ()
{
    if (m_hWnd == NULL)
        return KYV_NULL;

    WORD wVirtualKeyCode = 0, wModifiers = 0;
    GetHotKey (wVirtualKeyCode, wModifiers);

    UINT_P upKeyCode = GetKeyCodeFromVirtualKey ((UINT_P)(DWORD)wVirtualKeyCode);
    if (upKeyCode != KYV_NULL)
        upKeyCode |= sHotCtrlKeyModifiers2Vol ((UINT_P)(DWORD)wModifiers);

    return (INT)upKeyCode;
}

void CVWnd_HotKey::SetCurrentHotKey (INT nKeyCode)
{
    if (m_hWnd != NULL)
        SetHotKey ((WORD)(nKeyCode & KYM_MASK), (WORD)sVolKeyModifiers2HotCtrl (nKeyCode));
}

void CVWnd_HotKey::SetInvalidHotKeyModifiers (INT nInvalidModifiers, INT nReplaceModifiers)
{
    nInvalidModifiers &= (KYS_CONTROL | KYS_SHIFT | KYS_ALT);

    WORD wInvalidComb;
    switch (nInvalidModifiers)
    {
    case KYS_CONTROL:                         wInvalidComb = HKCOMB_C;    break;
    case KYS_SHIFT:                           wInvalidComb = HKCOMB_S;    break;
    case KYS_ALT:                             wInvalidComb = HKCOMB_A;    break;
    case (KYS_SHIFT | KYS_CONTROL):           wInvalidComb = HKCOMB_SC;   break;
    case (KYS_CONTROL | KYS_ALT):             wInvalidComb = HKCOMB_CA;   break;
    case (KYS_SHIFT | KYS_ALT):               wInvalidComb = HKCOMB_SA;   break;
    case (KYS_SHIFT | KYS_CONTROL | KYS_ALT): wInvalidComb = HKCOMB_SCA;  break;
    default:                                  wInvalidComb = HKCOMB_NONE; break;
    }

    SetRules (wInvalidComb, (WORD)sVolKeyModifiers2HotCtrl ((UINT_P)nReplaceModifiers));
}
