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






4.90/5 (15投票s)
2007 年 6 月 14 日
3分钟阅读

80593

801
创建通用 ProfUIS 对话框和 ProfUIS 消息框
引言
本文针对那些在项目中使用 ProfUIS
库的 MFC 程序员;同时,它还包含一些关于如何使用自定义 UI 库的一般技巧。我认为大多数在对话框中使用自定义 UI 控件类的程序员不喜欢用简单的方法——创建所需控件类的新成员并执行类似 DDX_Control()
的操作。你在程序中拥有的对话框越多,你就会越受这种“简单方法”的困扰。因此,我解决了这个问题,现在我可以给出一个简单而“正确”的方法来使其工作。此外,我将给出两个有趣的技巧,关于如何用换肤的控件替换标准 MessageBox
和标准打印预览栏。
背景
如果您想在实际工作中检查此示例,我建议您安装由 FOSS 软件提供的专业用户界面解决方案的免费版本,只需从公司网站下载即可:http://www.prof-uis.com/download.aspx。
那么,让我们开始吧。
一般概念
本文将提供三个类的示例以及一些关于外部代码的提示。 主要类是:CGeneralSubclassersContainer
、CProfUISDialog
和 CProuisDialogBar
。
一般概念是动态创建和子类化所有常用的换肤控件。由于我不仅要处理对话框,还要处理对话框栏,因此我创建了一个独立的类来操作所有动态控件——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 );
}
}
使用代码
要在您的应用程序中使用这些类,首先您必须将声明复制到您的头文件,并将实现复制到实现文件。在将此代码包含到您的项目中后,像使用 CDialog
或 CDialogBar
一样使用所需的类 CProfUISDlg
或 CProuisDialogBar
!
示例
// 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 日 - 添加了示例项目,并更改了代码示例以使其稳定并经过测试。