CapitolSoft Banner
Features
Tutorials
F.A.Q.s
Downloads
Articles
Open Architecture
Control Development Kit
RAD Foundation Class
Enhanced IntelliSense
Samples
Migration Center
Links

Spice up Your C++ Controls with a Touch of RAD

Ever thought of writing a powerful RAD C++ control using the Windows NotePad? Thought it's only possible in those "modern" languages like Java and C#? Now you can add those cool RAD features to your traditional C++ controls with ease and take them to a new level.. even without using any tool at all.

The tutorial presented in this section shows you how to manually create and use a RAD C++ control in Visual C++ environment. This discussion does not use any of Visual C++ tools such as Class Wizard or RadVC's IDE related features. To know how RadVC can significantly automate the task of creating and using a RAD C++ control, you should visit RAD C++ Control Development Kit (CDK) page.

The control we will create is called "Circle". "Circle" is a typical Windows control that does some drawing in its client area and has a property called "CircleColor" and a method called "AboutBox". Also it responds to an event called "Click" when the user clicks on its client area. Once the control is created, we will show you how to use the control in a dialog based Visual C++ project

Step 1: Create the Circle Control
In Visual C++, create a blank text files called "Circle.h". In this file, we will declare the "CCircle" class  as follows:
Circle.h
#pragma once
#include "rfcCtrl.h"
class CCircle : public CRCtrl
{
public:
    CCircle(){}
};

As shown, "CCircle" class is derived from a control class called "CRCtrl". CRCtrl is a RFC class which is derived from MFC's generic Windows class "CWnd". CRCtrl lets you hook up Windows messages (e.g. paint, mouse clicks etc.) easily wthout having to use any advanced tools such as Class Wizard. In addition to this, CRCtrl also adds a lot of handy properties such as Windows management (position, size), fonts etc. CRCtrl is declared in RFC's "rfcCtrl.h" file, so you need to add an "include" statement on the top of the control declaration.

If you do not want to use RFC class library in your application, you can use another windows control class called "CCtrl". CCtrl class is very similar to CRCtrl class, the only difference is that CCtrl is not part of RFC library. It is declared and implemented in "ctrl.h" and "ctrl.cpp" respectively. Thus in order to derive "CCircle" class from CCtrl class instead of CRCtrl, you need to change the #include "rfcCtrl.h" statement to #include "Ctrl.h". Also don't forget to add the files to your project, otherwise you will get linker errors. You should find these file in the following RadVC installation directories: \\RadVC\Include and \\RadVC\Src.

Step 2: Drawing on the Control

To draw on the control, override "OnEraseBkgndMsg(CDC* pDC)" function, which is called whenever the control receives a WM_ERASEBKGND windows message.

Circle.h

#pragma once
#include "rfcCtrl.h"
class CCircle : public CRCtrl
{
public:
    CCircle(){}
    void OnEraseBkgndMsg(CDC* pDC)
    {
        CRect rect;
        GetClientRect(rect);
        pDC->FillSolidRect(rect, RGB(192, 192, 192));
        pDC->Ellipse(rect);
    }

};
The circle first draws the background with grey color [RGB(192, 192, 192)] and then draws a circle in its client area.
 
Step 3: Add a Method to the Circle Control
In C++, methods are nothing but function calls. For our "Circle" control, we will add a method called "AboutBox". This method displays a simple message box, as follows:

Circle.h

#pragma once
#include "rfcCtrl.h"
class CCircle : public CRCtrl
{
public:
    CCircle(){}
    void OnEraseBkgndMsg(CDC* pDC)
    {
        CRect rect;
        GetClientRect(rect);
        pDC->FillSolidRect(rect, RGB(192, 192, 192));
        pDC->Ellipse(rect);
    }

    void AboutBox()
    {
        AfxMessageBox(_T("Circle Control\nCopyright(R), 2001"));
    }

};
Step 4: Add a Property to the Circle Control
For our "Circle" control, we will add a property, called "CircleColor", which essentilly defines the background color of the circle.
Circle.h
#pragma once
#include "rad.h"
class CCircle : public CRCtrl
{
public:
    CCircle()
    {
        m_CircleColor = RGB(192, 192, 192);
    }
    Property(CircleColor, COLORREF)
    void OnEraseBkgndMsg(CDC* pDC)
    {
         CRect rect;
         GetClientRect(rect);
         pDC->FillSolidRect(rect,
m_CircleColor);
         pDC->Ellipse(rect);
    }
    void AboutBox()
    {
        AfxMessageBox(_T("Circle Control\nCopyright(R), 2000"));
    }
};
 
inline COLORREF CCircle::GetCircleColor()
{
    return m_CircleShape;
}
inline void CCircle::SetCircleColor(COLORREF clrCircleColor)
{
    m_CircleColor = clrCircleColor;
    Invalidate();
}
The property is declared through a macro called "Property", which is defined in "rad.h" [in \\RadVC\Include directory] as follows:
#define         Property(PropName, PropType)\
            __declspec(property(get=Get##PropName,put=Set##PropName)) PropType PropName;\
            PropType Get##PropName();\
            void Set##PropName(PropType);\
            PropType m_##PropName;

The macro takes two parameters, the first of which is the name of the property and the second being the property type. The macro uses a Microsoft compiler specific attribute called "property". This enables users to "set" and "get" control properties through Visual Basic - like function calls. For example, the user of our CCircle control can get and set the "CircleColor" property in the following manner:

// Get the control color
COLORREF clr = m_Circle.CircleColor;
// Set the control color to red
m_Circle.CircleColor = RGB(255, 0, 0);
Step 5: Add an Event to the Circle Control
For our "Circle" control, we will add an event called "Click", which is fired whenever the user clicks on the control's client area. To add the event, we will override "OnLButtonDownMsg" function, which is called in response to Windows WM_LBUTTONDOWN message.

Circle.h

#pragma once
#include "rfcCtrl.h"
#include "rad.h"
class CCircle : public CRCtrl
{
public:
    CCircle()
    {
         m_CircleColor = RGB(192, 192, 192);
    }
    Property(CircleColor, COLORREF)
    void OnEraseBkgndMsg(CDC* pDC)
    {
         CRect rect;
         GetClientRect(rect);
         pDC->FillSolidRect(rect, m_CircleColor);
         pDC->Ellipse(rect);
    }
    void AboutBox()
    {
        AfxMessageBox(_T("Circle Control\nCopyright(R), 2000"));
    }
    void OnLButtonDownMsg(UINT nFlags, CPoint point)
    {
        FireEvent(Click, 0);
    }
    enum eCircleEvents
    {
        Click = 1L,
    };
    IMPLEMENT_EVENT()
};
 
inline COLORREF CCircle::GetCircleColor()
{
    return m_CircleShape;
}
inline void CCircle::SetCircleColor(COLORREF clrCircleColor)
{
    m_CircleColor = clrCircleColor;
    Invalidate():
}

As shown above, the "OnLButtonDownMsg" calls another function, "FireEvent". "FireEvent" is declared and implemented in "rad.h" in IMPLEMENT_EVENT() macro.

Step 6: Testing the Control:
Open Visual C++ (ver 6.0) and create a skeleton dialog based application called "TestCircle". If you are not very much familiar with Visual C++ environment, a detailed tutorial on how to create a dialog-based application is described in this page.

In this project, you will find two files: "TestCircleDlg.h" and "TestCircleDlg.cpp". These two files declare and implement the dialog respectively. First, open the declaration file ("TestCircleDlg.h") and declare a CCircle object in the class declaration, as follows:

CCircle m_Circle;

 

Now, open the implementation file ("TestCircleDlg.cpp") and locate the function: BOOL CTestCircleDlg::OnInitDialog(). This function is called when the dialog is loaded (initialized). Add the following code to create the "Circle" control:

BOOL CTestCircleDlg::OnInitDialog()
{
    CDialog::OnInitDialog();
    ..
    ..
    // TODO: Add extra initialization here
    m_Circle.Create(this, 220, CRect(10, 10, 100, 100));

    return TRUE; // return TRUE unless you set the focus to a control
}

Now build and run the project. You will see the circle control on the top left corner of the dialog, as shown below:

SpiceUp_C++_RAD_Create.jpg (8766 bytes)

To test the Method "AboutBox", we will add a button with caption "Property" on the dialog and override its "BN_CLICK" event. Now add the following code in ::OnMethod() handler:

void CTestCircleDlg::OnMethod()
{
    m_Circle.AboutBox();   
}

 

 

Now build and run the project. Click on the button "Show Method", The circle control will now display a message box as shown below:

SpiceUp_C++_RAD_Method.jpg (15792 bytes)

To test the property "CircleColor", we will add a button with caption "Property" on the dialog and override its "BN_CLICK" event. Now add the following code in ::OnProperty() handler:

void CTestCircleDlg::OnProperty()
{
    m_Circle.CircleColor = RGB(255, 0, 0);
}

 

 

SpiceUp_C++_RAD_Property.jpg (13666 bytes)

Now build and run the project. Click on the button "Property", You will see the background color of the circle changes to red.

To test the event "Click", we will add two buttons: "Add Event" and "Remove Event" on the dialog We will use these two buttons to demonstrate the fact that the RAD C++ control events can be attached and detached dynamically as the program runs.

Now override "BN_CLICK" events of these buttons and add the following code inside their handler functions:

void CTestCircleDlg::OnAddEvent()
{
    AddHandler(m_Circle, Click, CTestCircleDlg, Circle_Click)
    m_AddEventBtn.EnableWindow(FALSE);
    m_RemoveEventBtn.EnableWindow();
}

void CTestCircleDlg::OnRemoveEvent()
{
    RemoveHandler(m_Circle, CTestCircleDlg, Circle_Click)
    m_AddEventBtn.EnableWindow();
    m_RemoveEventBtn.EnableWindow(FALSE);
}

LRESULT CTestCircleDlg::Circle_Click(LPARAM lParam)
{
    AfxMessageBox("Circle clicked.");
    return 0L;
}

 

 

 

 

 

 

 

As shown above, the "OnAddEvent" handler uses a macro called "AddHandler" to an event handler. This macro has the following prototype:

AddHandler(<Control>, <Event>, <Client_Class>, <Handler>)

The <Handler> in this case is the event handler for the "Click" event, called "Circle_Click". A RAD C++ control event handler has the following prototype:

LRESULT <Handler>(LPARAM lParam)

Similarly, the "OnRemoveEvent" handler uses another macro called "RemoveHandler" to remove an event handler. This macro has the following prototype:

AddHandler(<Control>, <Client_Class>, <Handler>)

Now build and run the application and test the followings:

(1) Click inside the circle control, since no event has been added, so you won't see the message box (with "Circle clicked" message) to appear.

(2) Click on the "Add Event" button now and click on the circle again. Now you will find that the event is fired and thus the message box "Circle clicked" appears.

(3) Click on the "Remove Event" button and then click on the circle. The "circle" has stopped responding to the event again.

SpiceUp_C++_RAD_Event.jpg (18868 bytes)

Download the CCircle control class here >>  Circle.h

Download the TestCircle project here >>  TestCircle.zip

 

Related Links:

Control Development Kit (CDK) Home

 

 

Microsoft Visual C++, Microsoft Visual Basic and Microsoft Visual Studio are registered trademarks of Microsoft Corporation.
Copyright 1998-2000 Capitolsoft Corporation. All rights reserved.
Disclaimer: Capitolsoft Corporation is an independent business organization incorporated with the State Commission of Virginia and hence has no connection with U.S. Capitol or any other federal agency of U.S. government. Opinions expressed by the individuals in this site are of their own alone and hence do not necessarily reflect the views of Capitolsoft Corporation.

 

This page was last updated on November 21, 2005