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

创建通用 ProfUIS 对话框和 ProfUIS 消息框

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.90/5 (15投票s)

2007 年 6 月 14 日

3分钟阅读

viewsIcon

80593

downloadIcon

801

创建通用 ProfUIS 对话框和 ProfUIS 消息框

引言

本文针对那些在项目中使用 ProfUIS 库的 MFC 程序员;同时,它还包含一些关于如何使用自定义 UI 库的一般技巧。我认为大多数在对话框中使用自定义 UI 控件类的程序员不喜欢用简单的方法——创建所需控件类的新成员并执行类似 DDX_Control() 的操作。你在程序中拥有的对话框越多,你就会越受这种“简单方法”的困扰。因此,我解决了这个问题,现在我可以给出一个简单而“正确”的方法来使其工作。此外,我将给出两个有趣的技巧,关于如何用换肤的控件替换标准 MessageBox 和标准打印预览栏。

背景

如果您想在实际工作中检查此示例,我建议您安装由 FOSS 软件提供的专业用户界面解决方案的免费版本,只需从公司网站下载即可:http://www.prof-uis.com/download.aspx

那么,让我们开始吧。

一般概念

本文将提供三个类的示例以及一些关于外部代码的提示。 主要类是:CGeneralSubclassersContainerCProfUISDialogCProuisDialogBar

一般概念是动态创建和子类化所有常用的换肤控件。由于我不仅要处理对话框,还要处理对话框栏,因此我创建了一个独立的类来操作所有动态控件——CGeneralSubclassersContainer 类。CProfUISDialog 替换了标准的 CDialog 类,而 CProuisDialogBar 类替换了标准的 CDialogBar 类,因此要使用它们 - 只需将派生类中的所有 CDialog (CDialogBar) 条目替换为 CProfUISDialog (CProuisDialogBar),然后享受乐趣。

类代码

头文件

#pragma once
#include < vector >
BOOL CALLBACK EnumContainerChildProc( HWND hwnd, LPARAM lParam );


struct CGeneralSubclasserContainer {
    CGeneralSubclasserContainer()
    {
        m_ActLikeMessageBox = false;
    }
    virtual  ~CGeneralSubclasserContainer()
    {
        for( long i = 0; i < m_GeneralSubclassers.size(); ++i ) 
    {
            delete m_GeneralSubclassers[i];
        }
    }
    bool m_ActLikeMessageBox;
    std::vector< CWnd* > m_GeneralSubclassers;
    long addWindow(CWnd* newWnd)
    {
        m_GeneralSubclassers.push_back( newWnd );
        return m_GeneralSubclassers.size() - 1;
    }
};

class CProuisDialogBar : public CExtWA < CExtWS < CDialogBar > >, 
                    public CGeneralSubclasserContainer
{
public:
    CProuisDialogBar() {}
    virtual ~CProuisDialogBar() {};
    void StandardPrepare();
};
class CExtProgressCtrl : public CProgressCtrl {
public:
    CExtProgressCtrl(){}
    virtual ~CExtProgressCtrl(){}

public:
    DECLARE_MESSAGE_MAP()
public:
    afx_msg void OnPaint();
};


// CQSOFilterDialog dialog

class CProfUISDlg : public CExtResizableDialog, 
    public CGeneralSubclasserContainer //CExtWA < CExtWS < CDialog > >
{
    DECLARE_DYNAMIC(CProfUISDlg)

public:
    CProfUISDlg() {}
    CProfUISDlg(UINT nID, CWnd* pParent = NULL);   // standard constructor
    CProfUISDlg( __EXT_MFC_SAFE_LPCTSTR lpszTemplateName, 
                    CWnd * pParentWnd = NULL );
    virtual ~CProfUISDlg();

    void StandardPrepare();

protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support

    DECLARE_MESSAGE_MAP()
public:
    virtual BOOL OnInitDialog();
    afx_msg void OnShowWindow(BOOL bShow, UINT nStatus);
};

ProfUIS 库需要声明以下类,这样您也可以为非客户区获得换肤外观。

template < >
class CExtNCW < CProfUISDlg >
    : public CProfUISDlg
    , public CExtNcFrameImpl
{
public:
    CExtNCW()
    {
    }
    CExtNCW(
        UINT nIDTemplate,
        CWnd * pParentWnd = NULL
        )
        : CProfUISDlg( nIDTemplate, pParentWnd )
    {
    }
    CExtNCW(
        __EXT_MFC_SAFE_LPCTSTR lpszTemplateName,
        CWnd * pParentWnd = NULL
        )
        : CProfUISDlg( lpszTemplateName, pParentWnd )
    {
    }
    virtual ~CExtNCW()
    {
    }
    virtual HWND NcFrameImpl_OnQueryHWND()
    {
        return GetSafeHwnd();
    }
protected:
    virtual void PreSubclassWindow()
    {
        CProfUISDlg::PreSubclassWindow();
        CExtNcFrameImpl::PreSubclassWindow();
    }
    virtual void PostNcDestroy()
    {
        CExtNcFrameImpl::PostNcDestroy();
        CProfUISDlg::PostNcDestroy();
    }
    virtual LRESULT WindowProc( UINT message, WPARAM wParam, LPARAM lParam )
    {
        if( ! NcFrameImpl_IsSupported() )
            return CProfUISDlg::WindowProc( message, wParam, lParam );
        HWND hWndOwn = m_hWnd;
        LRESULT lResult = 0;
        if( NcFrameImpl_PreWindowProc( lResult, message, wParam, lParam ) )
            return lResult;
        lResult = CProfUISDlg::WindowProc( message, wParam, lParam );
        if( ! ::IsWindow( hWndOwn ) )
            return lResult;
        if( CWnd::FromHandlePermanent(hWndOwn) == NULL )
            return lResult;
        NcFrameImpl_PostWindowProc( lResult, message, wParam, lParam );
        return lResult;
    }
}; // class CExtNCW

实现文件

#include < stdafx.h >
#pragma hdrstop
#include "ProfUISDlgApp.h"
#include "ProfuisDlg.h"

// CQSOFilterDialog dialog

IMPLEMENT_DYNAMIC(CProfUISDlg, CExtResizableDialog)
                // CExtWA < CExtWS < CDialog > >)

CProfUISDlg::CProfUISDlg(UINT nID, CWnd* pParent /*=NULL*/)
    : CExtResizableDialog( nID, pParent)
{
}
CProfUISDlg::CProfUISDlg( __EXT_MFC_SAFE_LPCTSTR lpszTemplateName, 
                        CWnd * pParentWnd )
    : CExtResizableDialog( lpszTemplateName, pParentWnd)
{
}

CProfUISDlg::~CProfUISDlg()
{
}

void CProfUISDlg::DoDataExchange(CDataExchange* pDX)
{
    CExtResizableDialog::DoDataExchange(pDX);
}


BEGIN_MESSAGE_MAP(CProfUISDlg, CExtResizableDialog )
    ON_WM_SHOWWINDOW()
END_MESSAGE_MAP()

// CQSOFilterDialog message handlers
BOOL CALLBACK EnumContainerChildProc( HWND hwnd, LPARAM lParam )
{
     CGeneralSubclasserContainer* dialogPtr = 
                (CGeneralSubclasserContainer*)lParam;
    if( CWnd::FromHandlePermanent( hwnd ) == 0 ) {
        CWnd* tmp = CWnd::FromHandle( hwnd );
        CString className;
        GetClassName( hwnd, className.GetBufferSetLength( 200 ), 200 );
        className.ReleaseBuffer();
        if( className.CompareNoCase( _T("edit") ) == 0 ) 
    {
            dialogPtr->m_GeneralSubclassers[dialogPtr->addWindow
                        ( new CExtEdit )]->
                SubclassWindow( hwnd );;
        } 
    else if( className.CompareNoCase( _T("Static") ) == 0 ) 
    {
            dialogPtr->m_GeneralSubclassers[dialogPtr->addWindow(
                new CExtLabel )]->SubclassWindow( hwnd );
        }  
    else if( className.CompareNoCase( _T("BUTTON") ) == 0 ) 
    {
            CButton* tmpButton = (CButton*)CWnd::FromHandle( hwnd );
            if( tmpButton->GetButtonStyle() == BS_CHECKBOX ||
                tmpButton->GetButtonStyle() == BS_AUTOCHECKBOX )
            {
                dialogPtr->m_GeneralSubclassers[dialogPtr->addWindow
            ( new CExtCheckBox )]->
                    SubclassWindow( hwnd );
            } 
        else if( tmpButton->GetButtonStyle() == BS_GROUPBOX ) 
        {
                dialogPtr->m_GeneralSubclassers[dialogPtr->addWindow
            ( new CExtGroupBox )]->
                    SubclassWindow( hwnd );
                ((CExtGroupBox*)dialogPtr->m_GeneralSubclassers[dialogPtr->
                    m_GeneralSubclassers.size() - 1])->SetStyle
                ( CExtGroupBox::STYLE_ROUNDED );
                ((CExtGroupBox*)dialogPtr->m_GeneralSubclassers[dialogPtr->
                    m_GeneralSubclassers.size() - 1])->SetWindowPos
            ( &CWnd::wndBottom, 0,0,0,0, SWP_NOSIZE| SWP_NOMOVE );

            } 
        else if( tmpButton->GetButtonStyle() == BS_AUTORADIOBUTTON ||
                tmpButton->GetButtonStyle() == BS_RADIOBUTTON )
            {
                dialogPtr->m_GeneralSubclassers[dialogPtr->addWindow
            ( new CExtRadioButton )]->
                    SubclassWindow( hwnd );
            } 
        else 
        {
                dialogPtr->m_GeneralSubclassers[dialogPtr->addWindow
            ( new CExtButton )]->
                    SubclassWindow( hwnd );
            }
        }  
    else if( className.CompareNoCase( _T("msctls_progress32") ) == 0 ) 
    {
            dialogPtr->m_GeneralSubclassers[dialogPtr->
                addWindow( new CExtProgressCtrl )]->SubclassWindow( hwnd );
        } 
    else if( className.CompareNoCase( _T("ComboBox") ) == 0 ) 
    {
            dialogPtr->m_GeneralSubclassers[dialogPtr->
                addWindow( new CExtComboBox )]->SubclassWindow( hwnd );
        }
    }
    return TRUE;
}
CProuisDialogBar::StandardPrepare()
{
    EnumChildWindows( GetSafeHwnd(), EnumContainerChildProc, LPARAM(
        static_cast< CGeneralSubclasserContainer* >( this ) ) );
}
void CProfUISDlg::StandardPrepare()
{
    EnumChildWindows( GetSafeHwnd(), EnumContainerChildProc, LPARAM(
        static_cast< CGeneralSubclasserContainer*>( this ) ) );
    ShowSizeGrip( FALSE );
}
BOOL CProfUISDlg::OnInitDialog()
{
    CExtResizableDialog::OnInitDialog();
    StandardPrepare();
    return TRUE;
}

BEGIN_MESSAGE_MAP(CExtProgressCtrl, CProgressCtrl)
    ON_WM_PAINT()
END_MESSAGE_MAP()

void CExtProgressCtrl::OnPaint()
{
    CRect rcClient;
    GetClientRect( &rcClient );
    CPaintDC dcPaint( this );
    CExtMemoryDC dc( &dcPaint, &rcClient );
    if( g_PaintManager->GetCb2DbTransparentMode(this) )
    {
        CExtPaintManager::stat_ExcludeChildAreas(
            dc,
            GetSafeHwnd(),
            CExtPaintManager::stat_DefExcludeChildAreaCallback
            );
        g_PaintManager->PaintDockerBkgnd( true, dc, this );
    } // if( g_PaintManager->GetCb2DbTransparentMode(this) )
    else
        dc.FillSolidRect( &rcClient, g_PaintManager->
    GetColor( CExtPaintManager::CLR_3DFACE_OUT, this ) );
    DefWindowProc( WM_PAINT, WPARAM(dc.GetSafeHdc()), 0L );
    g_PaintManager->OnPaintSessionComplete( this );
}

void CProfUISDlg::OnShowWindow(BOOL bShow, UINT nStatus)
{
    CExtResizableDialog::OnShowWindow(bShow, nStatus);
    if( m_ActLikeMessageBox && bShow ) {
        EnumChildWindows( GetSafeHwnd(), EnumContainerChildProc,
            LPARAM( static_cast< CGeneralSubclasserContainer*>( this ) ) );
        ShowSizeGrip( FALSE );
    }
}

使用代码

要在您的应用程序中使用这些类,首先您必须将声明复制到您的头文件,并将实现复制到实现文件。在将此代码包含到您的项目中后,像使用 CDialogCDialogBar 一样使用所需的类 CProfUISDlgCProuisDialogBar

示例

// CExtNCW - used in profuis for customising non-client area
class CImportDialog : public CExtNCW< CProfUISDlg >
{
    ....
    public:
        virtual BOOL OnInitDialog();
}

重要提示:如果想以不寻常的方式子类化某个控件,请在调用父 CProfUISDlg::OnInitDialog 之前对其进行子类化!!

BOOL CImportDialog::OnInitDialog()
{
    // some real custom subclass code
    BOOL result = CProfUISDlg::OnInitDialog();
    // post subclass code
    return result;
}

按照给定的说明,您可以获得类似这样的东西

红色/绿色指示器是我为大型输入表单设计的一个简单的静态派生控件。它可以通过鼠标点击来打开/关闭它之后的下一个控件,并且还指示控件状态。也许在我的下一篇短文中,我将把它放在讨论的顶部。

有用的提示

现在,一些有用的代码提示。

第一个提示:如何用全新的控件替换标准的 MessageBox。

您必须用您自己的 CMyApp::DoMessageBox 替换标准的 CWinApp::DoMessageBox。新版本将如下所示

int CMyApp::DoMessageBox(LPCTSTR lpszPrompt, UINT nType, UINT nIDPrompt)
{
    CExtNCW<CProfuisDlg> dlg;
    dlg.m_ActLikeMessageBox = true;
    AfxHookWindowCreate( &dlg );
    return CWinApp::DoMessageBox(lpszPrompt, nType, nIDPrompt);
}
//.............Somewhere in your code.........
AfxMessageBox( _T("Hi! Now you'll see the result!");
 //.............Somewhere in your code.........

这是非常漂亮的 MessageBox

第二个提示:如何用新的控件替换标准的打印预览栏。

如果您已经知道如何使用自定义打印预览,那么将给定的代码添加到您的自定义 DoPrintPreview

BOOL CWrapperView::DoPrintPreview(UINT nIDResource, CView* pPrintView,
    CRuntimeClass* pPreviewViewClass, CPrintPreviewState* pState)
{
// Some standard preparing job

// Create the preview view object
    CMyPreviewView* pView = (CMyPreviewView*)pPreviewViewClass->CreateObject();
    if (pView == NULL)
    {
        TRACE0("Error: Failed to create preview view.\n");
        return FALSE;
    }
    pView->m_pPreviewState = pState;        // save pointer

    // It's fun, but the real change is in the only one line!!!:
    pView->m_pToolBar = new CProuisDialogBar;

    if (!pView->m_pToolBar->Create(pParent, MAKEINTRESOURCE(nIDResource),
        CBRS_TOP, AFX_IDW_PREVIEW_BAR))
    {
        // some clearing and failure code
        return FALSE;
    }
    ((CProuisDialogBar*) pView->m_pToolBar)->StandardPrepare();
    pView->m_pToolBar->m_bAutoDelete = TRUE;    // automatic cleanup

// you can already manipulate with your new nice-looking printpreview bar,
// for example place custom button on it...
    addOptionsButtonToPreviewBar( pView->m_pToolBar );

// your standard code continues
}

最后,我们得到了我们想要的

结论

您可以在一个工作示例项目中测试该代码。您可以在本文的顶部下载该项目。

希望您觉得这篇文章真的很有用,再见。

特别感谢

特别感谢我的朋友 SloNN 提供的消息框提示。

历史

  • 2007 年 6 月 20 日 - 添加了示例项目,并更改了代码示例以使其稳定并经过测试。

© . All rights reserved.