© Alex van Rooijen, Bart Grootendorst, and Harry Broeders.
In the following explanation an example of a LED component is used. Each compiler comes with its own windows framework. So the source code for the three variations of the led example are completely different.
There is a fourth variation called led_api that only uses the Win32 API to create the LED window. This last variation can be compilled with all three compilers mentioned above and also with the free gcc compiler. I use the free Dev-C++ IDE which includes the gcc compiler and MinGW Win32 API libaries.
The C++ Builder example is described on a separate page. All other examples are described on this page.
The source code of this LED component is included with the CDK
if you use Visual C++ 6.0 to compile this example you also need the module definition file:
if you use Dev-C++ 5 to compile this example you also need the module definition file:
There are twelve steps you have to take to create such a component, these are:
ImpComponent
As you can see only two additional steps, "Making a window" and "Linking the window to the component", are required to make a component with a window. If you are not familiar with the other ten steps we strongly advise that you read Ten steps to create a component. With "a component with a window" we mean a component that has a visual presentation of itself. This can be done by showing a static bitmap of the component or dynamically, depending on pin sates.
The window in which you can give a visual presentation of your component
can be made with any window framework, OWL, VCL, MFC or just straight with
Windows API calls. The window must be made in the same way as a window for
a normal program.
You can continue reading at 8. Linking the window to the
component, when you:
To make a dynamic component window some additions to the previously described method Ten steps to create a component are necessary. These are:
TWindow
when you use OWL (Borland C++).
TForm
when you use VCL (C++ Builder).
CWnd
when you use MFC (Visual C++).
CallOnxxxxx
objects must be used to get notification
when the visual representation should change. These
CallOnxxxxx
objects must be defined as private datamembers
of the window class.
ImpComponent
pointer must be added as private datamember
of the window class.
ImpComponent
pointer and parameters for every component pin
created in the derived class of ImpComponent
that influences
the visual representation of the component.
This
gives us the following window class declaration for our LED example:
class LEDWin: public TWindow { public: LEDWin(TWindow* parent, ImpComponent* pcomp, BoolConnection& pin); ... private: void pinChanged(); CallOnChange<BoolConnection::BaseType, LEDWin> coc; ImpComponent* pComp; ... };
The VCL example is described on a separate page.
class LEDWin : public CWnd { public: LEDWin(ImpComponent* pcomp, BoolConnection& pin); ... private: void pinChanged(); CallOnChange<BoolConnection::BaseType, LEDWin> coc; ImpComponent* pComp; ... };
class Window { public: Window(LPCTSTR lpClassName, int x, int y, int nWidth, int nHeight, HWND hWndParent, HINSTANCE _hInstance, ImpComponent* pcomp, BoolConnection& pin); ... private: void pinChanged(); CallOnChange<BoolConnection::BaseType, Window> coc; ImpComponent* pComp; };
The constructor's pcomp
parameter needs to be stored in the
ImpComponent
pointer called pComp
of the window
class. The connectionpin pin
is only used in the constructor
to initialize the CallOnxxxxx
object. You now have to
use the get
and set
functions of pin via the
CallOnxxxxx
object (which behaves like a pointer) because
the connection pins are not stored in the window class. This will give the
following component visual behavior function:
void LEDWin::pinChanged() { pComp->SetCompWinIcon(GetModule()->LoadIcon(coc->get() ? "iconLedOn" : "iconLedOff")); pComp->SetCompWinTitle(coc->get() ? "Led is ON" : "Led is OFF"); Invalidate(false); }
The VCL example is described on a separate page.
void LEDWin::pinChanged() { pComp->SetCompWinIcon(coc->get() ? hIconOn : hIconOff); pComp->SetCompWinTitle(coc->get() ? "Led is ON" : "Led is OFF"); Invalidate(false); }
void Window::pinChanged() { pComp->SetCompWinIcon(LoadIcon(hInstance, coc->get() ? "iconLedOn" : "iconLedOff")); pComp->SetCompWinTitle(coc->get() ? "Led is ON" : "Led is OFF"); InvalidateRect(hWnd, NULL, FALSE); }
Two new ImpComponent
functions have been used:
SetCompWinIcon
- changes the icon of the component window.
SetCompWintitle
- changes the title of the component window.
NOTE: These two functions may also be used for components without a window.
The Invalidate
call tells the operating system that the window
needs to be repainted. The operating system will send a WM_PAINT
to the window. This message this message must be handled by the led window
class. In the LED example the appropriate bitmap is copied to the screen
when the WM_PAINT
message is handled:
In OWL the WM_PAINT is handled by the base window TWindow
which
calls a virtual memberfunction called Paint. The only thing we have to do
is to override this memberfunction:
void LEDWin::Paint(TDC& dc, bool, TRect&) { TMemoryDC memDC(dc); TBitmap led(*GetModule(), coc->get() ? "ledOn" : "ledOff"); memDC.SelectObject(led); dc.BitBlt(0, 0, led.Width(), led.Height(), memDC, 0, 0, SRCCOPY); }
The VCL example is described on a separate page.
In MFC you can use the Class Wizard to handle the WM_PAINT message. To do this you can use the Class Wizard's Message Maps tab and select the WM_PAINT message and click the Add Function button. The IDE will generate the message map code and the first and the last line of the following memberfunction:
void LEDWin::OnPaint() { CPaintDC dc(this); CDC memDC; memDC.CreateCompatibleDC(&dc); BITMAP bm; if (coc->get()) { bitmapOn.GetBitmap(&bm); memDC.SelectObject(&bitmapOn); } else { bitmapOff.GetBitmap(&bm); memDC.SelectObject(&bitmapOff); } dc.BitBlt(0, 0, bm.bmWidth, bm.bmHeight, &memDC, 0, 0, SRCCOPY); }
When you use only the Win32 API you have to write a so called window procedure.
We assume that you are familiar with this. In this example the function
Paint()
is called when the window procedure receives a WM_PAINT
message:
void Window::Paint() { PAINTSTRUCT ps; BeginPaint(hWnd, &ps); HDC hdc(CreateCompatibleDC(ps.hdc)); HBITMAP hBitmap(LoadBitmap(hInstance, coc->get() ? "ledOn" : "ledOff")); SelectObject(hdc, hBitmap); RECT rect; GetClientRect(hWnd, &rect); BitBlt(ps.hdc, 0, 0, 104, 104, hdc, 0 , 0, SRCCOPY); DeleteObject(hBitmap); DeleteDC(hdc); EndPaint(hWnd, &ps); }
To link the component window to your component you have to override the
HWND CreateComponentWindow(HWND parent)
virtual memberfunction
of the ImpComponent
class. Within this function you need to
create your window. The HWND parent
parameter must be used as
parent window and you need to return the HWND
of your just created
window. You can see the CreateComponentWindow
function for our
LED example below:
HWND LED::CreateComponentWindow(HWND parent) { if (LEDWindow==0) { LEDWindow = new LEDWin(::GetWindowPtr(parent), this, pin); // LEDWindow will be deleted by its parent. So don't call delete yourself! LEDWindow->Create(); } return LEDWindow->GetHandle(); }
The VCL example is described on a separate page.
HWND LED::CreateComponentWindow(HWND parent) { if (LEDWindow==0) { LEDWindow = new LEDWin(this, pin); // LEDWindow will be deleted by its parent. So don't call delete yourself! RECT r={0, 0, 104, 104}; LEDWindow->Create("OWL_Window", "", WS_CHILD|WS_VISIBLE, r, LEDWindow->FromHandle(parent), 0); } return LEDWindow->m_hWnd; }
HWND LED::CreateComponentWindow(HWND parent) { if (LEDWindow==0) { HINSTANCE hInstance(GetModuleHandle("led.dll")); WNDCLASS wndclass; if (GetClassInfo(hInstance, "THRSim11_LED", &wndclass)==0) { wndclass.style=CS_HREDRAW|CS_VREDRAW; wndclass.lpfnWndProc=::WndProc; wndclass.cbClsExtra=0; wndclass.cbWndExtra=4; wndclass.hInstance=hInstance; wndclass.hIcon=LoadIcon(hInstance, "iconLedOff"); wndclass.hCursor=LoadCursor(NULL, IDC_ARROW); wndclass.hbrBackground=(HBRUSH)GetStockObject(LTGRAY_BRUSH); wndclass.lpszMenuName=NULL; wndclass.lpszClassName="THRSim11_LED"; RegisterClass(&wndclass); } LEDWindow = new Window("THRSim11_LED", 0, 0, 104, 104, parent, hInstance, this, pin); } return LEDWindow->GetHandle(); }
You can find the complete C++ sources here:
C:\Program Files\THRSim11\cdk\examples\BorlandC++5.02\led\led.ide
You can use the following file to register this component:
C:\Program Files\THRSim11\cdk\examples\C++Builder4.0\examples.bpg
You can use the following file to register this component:
C:\Program Files\THRSim11\cdk\examples\C++Builder5.0\examples.bpg
You can use the following file to register this component:
C:\Program Files\THRSim11\cdk\examples\C++Builder6.0\examples.bpg
You can use the following file to register this component:
C:\Program Files\THRSim11\cdk\examples\VisualC++6.0\examples.dsw
C:\Program
Files\THRSim11\cdk\examples\BorlandC++5.02\led_api\led.ide
You can use the following file to register this component:
C:\Program Files\THRSim11\cdk\examples\C++Builder5.0\examples.bpg
You can use the following file to register this component:
C:\Program Files\THRSim11\cdk\examples\C++Builder6.0\examples.bpg
You can use the following file to register this component:
C:\Program Files\THRSim11\cdk\examples\Dev-C++5\led_api\led.dev
You can use the following file to register this component:
C:\Program Files\THRSim11\cdk\examples\VisualC++6.0\examples.dsw
You can use the following file to register this component: