- Projects
- Downloads
- Documents
- Forum
- Contact
- About me


|
 |
| Platform: |
Windows |
| License: |
Freeware |
| Release: |
1.00 |
| Last update: |
25 April 2002 |
| Description: |
porting of CxShadeButton
to an activex control |

Introduction |
Around there are many owner-draw button written
using C++ and the MFC classes. There are also many activex
controls, mostly written in VisualBasic.
While with C++ you can easily use both the MFC and the OCX
controls, with VisualBasic you need to convert the MFC objects
to an activex control.
Unluckily, the class wizard doesn't cover all the messages
and the events sent to an activex control, and some messages
are different, so most of the magic must be hand written.
Because sometime a piece of code is more clear
than the article itself, I included the porting of CxShadeButton
to an activex control, but it's just an example, in the article
I will speak about a generic AxButtonCtrl.
|

The MFC Activex Control Wizard & the Class Wizard
|
With a couple of clicks, the Activex Control
Wizard writes for us about 600 lines of commented code, that
probably we will never read. Just remember to select "BUTTON"
in the combo-box where the wizard ask: "Which window
class, if any, should this control subclass?", and the
framework is ready.
Using the Class Wizard, you can add the member
functions to process the basic messages:
WM_CREATE
WM_ERASEBKGND
WM_KEYDOWN
WM_LBUTTONDOWN
WM_LBUTTONUP
WM_MOUSEMOVE
WM_SIZE
PreSubclassWindow
Some of these messages are optional, but I'm
thinking about a custom control, so there are good chances
to handle more messages than a normal button.
The WM_DRAWITEM message is not in the list, it's
not a mistake, and I will explain this later.
|

PreSubclassWindow
|
void AxButtonCtrl::PreSubclassWindow()
{
... //custom style initiaization code
COleControl::PreSubclassWindow();
ModifyStyle(0, BS_OWNERDRAW|BS_NOTIFY);
}
In this method you can copy the same code used
in a MFC control; the difference is in the last 2 lines: the
button is now derived from COleControl (in place
of CButton). You must set the BS_OWNERDRAW
style to paint the button with your custom graphics, and the
BS_NOTIFY style if you need some special notification
messages, like BN_DISABLE, BN_KILLFOCUS,
...
|

WM_CREATE & WM_SIZE
|
These messages sometime are not used by MFC controls, but
in VisualBasic, the WYSIWYG philosophy requires that the developer
can see the appearance while she/he builds the GUI.
The control receives the WM_CREATE message and
creates the object with COleControl::OnCreate(lpCreateStruct),
after this call the button exists, and you can use all the windows
functions (CWnd members in MFC) to initialize the
graphic objects.
The WM_SIZE message is sent after WM_CREATE,
and when the button size has been changed; here you must build
(or rebuild) the position and/or the dimensions of the graphic
objects. |

Keyboard & mouse messages
(tooltips & hover functionality)
|
These messages are useful for the tooltips and the hover functionality.
The MSDN article Q141871 (HOWTO: Add Tooltips to ActiveX Controls)
describes the steps to implement tooltips. The resume: add the
RelayEvent method and a CToolTipCtrl m_ttip
member variable; create and activate the tooltip; in the handlers
for WM_LBUTTONDOWN, WM_LBUTTONUP, and WM_MOUSEMOVE call RelayEvent
to relay appropriate messages to the ToolTip control.
You must use a bool m_tracking member variable
to track the mouse position. When the mouse is over the button,
the WM_MOUSEMOVE messages are sent, and here activate the tracking.
...
if (!m_tracking) {
TRACKMOUSEEVENT t={sizeof(TRACKMOUSEEVENT),TME_LEAVE,m_hWnd,0};
if (::_TrackMouseEvent(&t)) {
m_tracking = true;
Invalidate();
}
}
To detect when the mouse leaves, you must add
this message handler in the control class .H file:
LRESULT OnMouseLeave(WPARAM, LPARAM);
and in the control class .CPP file:
ON_MESSAGE(WM_MOUSELEAVE, OnMouseLeave)
LRESULT AxButtonCtrl::OnMouseLeave(WPARAM, LPARAM)
{
ASSERT (m_tracking);
m_tracking = false;
Invalidate();
return 0;
}
|

Drawing the button - Reflected window messages
|
The Class Wizard provides an AxButtonCtrl::OnDraw
member for the drawing functions.
void AxButtonCtrl::OnDraw(CDC* pdc,
const CRect& rcBounds,
const CRect& rcInvalid)
{
DoSuperclassPaint(pdc, rcBounds);
}
You can leave it as it is. Our button waits the WM_DRAWITEM
message, but the mechanism with activex controls is slightly
different: COleControl creates an extra window
called "reflector", in the same position of the control
window. The reflector intercepts certain window messages and
sends them to the control with a different name. The reflected
messages are:
| Message sent by control |
Message reflected to control |
| WM_COMMAND |
OCM_COMMAND |
| WM_CTLCOLOR |
OCM_CTLCOLOR |
| WM_DRAWITEM |
OCM_DRAWITEM |
| WM_MEASUREITEM |
OCM_MEASUREITEM |
| WM_DELETEITEM |
OCM_DELETEITEM |
| WM_VKEYTOITEM |
OCM_VKEYTOITEM |
| WM_CHARTOITEM |
OCM_CHARTOITEM |
| WM_COMPAREITEM |
OCM_COMPAREITEM |
| WM_HSCROLL |
OCM_HSCROLL |
| WM_VSCROLL |
OCM_VSCROLL |
| WM_NOTIFY |
OCM_NOTIFY |
| WM_PARENTNOTIFY |
OCM_PARENTNOTIFY |
For these messages you must add the message handlers manually:
in the control class .H file, declare a handler function like
this:
class AxButtonCtrl : public COleControl
{
...
protected:
LRESULT OnOcmCommand(WPARAM wParam, LPARAM lParam);
LRESULT OnOcmDrawItem(WPARAM wParam, LPARAM lParam); ...
}
In the control class .CPP file, add the ON_MESSAGE
entries to the message map:
BEGIN_MESSAGE_MAP(AxButtonCtrl, COleControl)
//{{AFX_MSG_MAP(AxButtonCtrl)
...
ON_MESSAGE(OCM_COMMAND, OnOcmCommand)
ON_MESSAGE(OCM_DRAWITEM, OnOcmDrawItem)
...
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
And implement the member functions to process
the reflected messages. The wParam and lParam parameters are
the same as those of the original window message.
LRESULT AxButtonCtrl::OnOcmDrawItem(WPARAM wParam,
LPARAM lParam)
{
UINT nIDCtl = (UINT) wParam;
LPDRAWITEMSTRUCT lpDrawItemStruct=(LPDRAWITEMSTRUCT)lParam;
... //drawing code as used in WM_DRAWITEM
return 0;
}
|

Events
|
The Class Wizard gives some stock handlers for the standard
events, however the resulting functionality could be limited.
If you add the "Click" event with the stock handler
implementation, when someone clicks the button with the mouse,
COleButton automatically calls the FireClick
method, so you don't need to process the OCM_COMMAND
message. But if the button has the focus and someone presses
the space bar, the FireClick method is not called,
although the button receives the BN_CLICKED notification
through the OCM_COMMAND message.
In the end, use the custom event handler implementation, in
this way you can control exactly the behavior of the button,
without hidden calls or messages.
LRESULT AxButtonCtrl::OnOcmCommand(WPARAM wParam,
LPARAM lParam)
{
...
switch (wNotifyCode)
{
case BN_CLICKED:
// Fire click event when button is clicked
FireClick();
break;
case BN_KILLFOCUS:
...
}
return 0;
}
|

Conclusion
|
| This article tells how to convert an owner draw button in
an ActiveX control, this is just the beginning, to build a complete
control I should talk about automation, property sheet, and
so on ..., but these topics are out of the scope of the article.
For detailed informations about COM, please read these articles
|
|
 |
|
 |
|
 |
| 2003 - 2004 ©
Davide Pizzolato - www.xdp.it |
 |
 |
|