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:
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:
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:
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);
} |
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.
Download the CCircle control class here >> Circle.h
Download the TestCircle project here >> TestCircle.zip
Related Links: |