Twelve steps to create a component with a window

Component Development Kit for THRSim11

© Alex van Rooijen, Bart Grootendorst, and Harry Broeders.


Previous page Next page Index

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

There are twelve steps you have to take to create such a component, these are:

  1. Derive from ImpComponent
  2. Adding connectionpins
  3. Expose the connectionpins
  4. Defining component behavior
  5. Give the component "eyes"
  6. Give the component a name
  7. Making a window
  8. Linking the window to the component
  9. Generate a GUID
  10. Register Classfactory
  11. Compiling
  12. Adding the component to the registry

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.

7. Making a window

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:

Led component.This gives us the following window class declaration for our LED example:

Using OWL:

class LEDWin: public TWindow {
public:
	LEDWin(TWindow* parent, ImpComponent* pcomp, BoolConnection& pin);
	...
private:
	void pinChanged();
	CallOnChange<BoolConnection::BaseType, LEDWin> coc;
	ImpComponent* pComp;
	...
};

Using VCL:

The VCL example is described on a separate page.

Using MFC:

class LEDWin : public CWnd {
public:
	LEDWin(ImpComponent* pcomp, BoolConnection& pin);
	...
private:
	void pinChanged();
	CallOnChange<BoolConnection::BaseType, LEDWin> coc;
	ImpComponent* pComp;
	...
};

Using Win32 API:

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:

Using OWL:

void LEDWin::pinChanged() {
	pComp->SetCompWinIcon(GetModule()->LoadIcon(coc->get() ? "iconLedOn" : "iconLedOff"));
	pComp->SetCompWinTitle(coc->get() ? "Led is ON" : "Led is OFF");
	Invalidate(false);
}

Using VCL:

The VCL example is described on a separate page.

Using MFC:

void LEDWin::pinChanged() {
	pComp->SetCompWinIcon(coc->get() ? hIconOn : hIconOff);
	pComp->SetCompWinTitle(coc->get() ? "Led is ON" : "Led is OFF");
	Invalidate(false);
}

Using Win32 API:

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:

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:

Using OWL:

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);
}

Using VCL:

The VCL example is described on a separate page.

Using MFC:

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);
}

Using Win32 API:

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);
}

8. Linking the window to the component.

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:

Using OWL:

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();
}

Using VCL:

The VCL example is described on a separate page.

Using MFC:

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;
}

Using Win32 API:

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:


For comments and/or suggestions: cdk@hc11.demon.nl