Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / containers / docker

Creating the General ProfUIS Dialog and ProfUIS MessageBox

4.90/5 (16 votes)
19 Jun 20073 min read 1   780  
Creating the General ProfUIS Dialog and ProfUIS MessageBox

Introduction

This article concerns those MFC programmers who use the ProfUIS library in their projects; also, it contains some general tips on how to use custom UI libraries. I think most programmers who use custom UI control classes in their dialogs don't like the simple way to do it - create new member of the desired control class and do something like DDX_Control(). The more dialogs you have in your program - the more you become bothered by this "simple way". So, I solved this problem, and now I can give a simple and "right" way to make it work. In addition, I will give two interesting tips on how to replace the standard MessageBox and standard Print Preview bar with skinned ones.

Background

If you want to check this sample in real work, I recommend you install the free version of the professional user interface solutions by FOSS software by simply downloading it from the company site: http://www.prof-uis.com/download.aspx.

So let's start.

General concept

The article will give an example of three classes and some hints on the external code. The main classes are: CGeneralSubclassersContainer, CProfUISDialog, and CProuisDialogBar.

The general concept is to dynamically create and subclass all usual skinned controls. Since I used not only the dialogs to deal with, but also the dialog bars, I created the standalone class to manipulate all dynamic controls - the CGeneralSubclassersContainer class. The CProfUISDialog replaces the standard CDialog class, and the CProuisDialogBar class replaces the standard CDialogBar class, so to use them - simply replace all entries of CDialog (CDialogBar) in your derived classes with CProfUISDialog (CProuisDialogBar), and have fun.

Class code

Header file

C++
#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);
};

The ProfUIS library needs the next class to be declared, so you will get a skinned look for the non-client area as well.

C++
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

Implementation file

C++
#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 );
    }
}

Using the code

To use these classes in your applications, first you must copy the declarations to your header file and the implementation to the implementation file. After including this code to your project, use the desired class CProfUISDlg or CProuisDialogBar as usual CDialog or CDialogBar!

Example

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

Important tip: if you want to subclass some control in an unusual way, subclass it before calling the parent CProfUISDlg::OnInitDialog!!

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

Following the given instructions, you can obtain something that looks like this:

Image 1

The red/green indicator is a simple static-derived control I coded for a nice look in large input forms. It can switch on/off the next control after it by mouse click, and also indicates the control state. Maybe in my next short article, I will put it on the top of the discussion.

Useful hints

Now, some useful code hints.

First hint: How to replace the standard MessageBox with a brand-new one.

You must replace the standard CWinApp::DoMessageBox with your own CMyApp::DoMessageBox. The new version will look like this:

C++
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.........

And here is the Mr. NiceLooking MessageBox:

Image 2

Second hint: How to replace the standard print preview bar with a new one.

If you already know how to work with custom print preview, then add to your custom DoPrintPreview the given code:

C++
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
}

After all, we get what we wanted to:

Image 3

Conclusion

You can test the code in a working sample project. You can download the project at the top of this article.

I hope you find this article really useful, see ya.

Special thanks

Special thanks to my friend SloNN for the message box hint.

History

  • 20.06.2007 - sample project added and code samples changed to be stable and tested.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here