![]() |
Desktop Development »
Button Controls »
Owner-draw buttons
Intermediate
License: The Code Project Open License (CPOL)
Irregular shaped buttons – owner drawn buttons made easyBy Naren NeelamegamFreehand draw - make a button with irregular shape. A step by step beginner's guide. |
VC6Win2K, WinXP, Win2003, MFC, Dev
|
|
Advanced Search Add to IE Search |
|
|
|
||||||||||||||||

Button is the only control where we have to struggle or have to take more effort to change its shape and properties. But, it is not that much difficult to change its properties as it seems. The only tough thing is, we need to know how to draw well. A good artist can make a good-looking button. If you know well to play with �Device Caps� you can do more with button designs, that's all. This article is just about the basics of making your own owner drawn irregular shape buttons.
Here is what you have to do, if you want to develop your own button control. Subclass CButton (CMyButton in my sample), i.e.:

CButton.


DrawItem(), PreSubclassWindow() virtual functions to your class.

PreSubclassWindow() function, add the following code: ModifyStyle(0,BS_OWNERDRAW);
DrawItem() virtual function comes along with a parameter lpDrawItemStruct of type LPDRAWITEMSTRUCT. DrawItem() is where your button has been drawn. Refer MSDN for full details of the structure LPDRAWITEMSTRUCT. Here I am explaining the members needed just to make a simple button. lpDrawItemStruct ->itemAction:
Defines the drawing action required. This will be one or more of the following bits:
ODA_DRAWENTIRE - This bit is set when the entire control needs to be drawn.
ODA_FOCUS - This bit is set when the control gains or loses input focus. The itemState member should be checked to determine whether the control has focus.
ODA_SELECT - This bit is set when only the selection status has changed. The itemState member should be checked to determine the new selection state. lpDrawItemStruct ->itemState:
Specifies the visual state of the item after the current drawing action takes place. That is, if a menu item is to be dimmed, the state flag ODS_GRAYED will be set. The state flags are as follows:
ODS_CHECKED - This bit is set if the menu item is to be checked. This bit is used only in a menu.
ODS_DISABLED - This bit is set if the item is to be drawn as disabled.
ODS_FOCUS - This bit is set if the item has input focus.
ODS_GRAYED - This bit is set if the item is to be dimmed. This bit is used only in a menu.
ODS_SELECTED - This bit is set if the item�s status is selected.
ODS_COMBOBOXEDIT - The drawing takes place in the selection field (edit control) of an ownerdrawn combo box.
ODS_DEFAULT - The item is the default item. LpDrawItemSturct->hDC
Identifies a device context. This device context must be used when performing drawing operations on the control.
Here is where your drawing is being done. The following is the code snap of the sample:
void CMyButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) { SetWindowRgn(rgn,TRUE); // Construct your buttons region. This wont reflect in your view. // But you can sense it by clicking the region area. CDC* pDC=CDC::FromHandle(lpDrawItemStruct->hDC); // Get dc for the button switch(lpDrawItemStruct->itemAction) { case ODA_SELECT: { } // no break; for this case case ODA_DRAWENTIRE: { if(lpDrawItemStruct->itemState & ODS_SELECTED) { pDC->FillRgn(CRgn::FromHandle(trgn), CBrush::FromHandle((HBRUSH)GetStockObject(GRAY_BRUSH))); } // Draw button down state else { pDC->FillRgn(CRgn::FromHandle(trgn), CBrush::FromHandle((HBRUSH)GetStockObject(BLACK_BRUSH))); HRGN r= ::CreateRectRgn(0,0,0,0); CombineRgn(r,trgn,0,RGN_COPY); OffsetRgn(r,2,2); pDC->FillRgn(CRgn::FromHandle(r), CBrush::FromHandle((HBRUSH)GetStockObject(GRAY_BRUSH))); } break; } case ODA_FOCUS: { pDC->FillRgn(CRgn::FromHandle(trgn), CBrush::FromHandle((HBRUSH)GetStockObject(LTGRAY_BRUSH))); } break; } // Draw button caption. }
The sample application demonstrates owner drawn irregular shape buttons. You can draw your own button using your mouse, on the dialog. The button will be created in the drawn shape on left button up. Nothing special with this concept. Just the drawn coordinates are collected as path using BeginPath and EndPath of the CDC. Path is then converted into region. Using that region irregular buttons are created and drawn (FillRgn).
To do so, a DC of the dialog is created as a member function (usually no one will do) CDC *m_pdc;. This DC is used to create a path while you draw in the dialog. Here is the drawing and path making code:
void CButtonSubDlg::OnLButtonDown(UINT nFlags, CPoint point) { #ifdef _SAMPLE1 m_pdc->BeginPath(); m_pdc->MoveTo(point); m_spoint=point; SetCapture(); #endif CDialog::OnLButtonDown(nFlags, point); }
void CButtonSubDlg::OnMouseMove(UINT nFlags, CPoint point) { // Show mouse position CString mouse; mouse.Format("Irregular Buttons - X:%d Y:%d",point.x,point.y); SetWindowText(mouse); if(nFlags==MK_LBUTTON) { #ifdef _SAMPLE1 CClientDC dc(this); m_pdc->LineTo(point); dc.MoveTo(m_spoint); dc.LineTo(point); m_spoint=point; #endif } CDialog::OnMouseMove(nFlags, point); }
void CButtonSubDlg::OnLButtonUp(UINT nFlags, CPoint point) { #ifdef _SAMPLE1 m_pdc->EndPath(); cRgn.DeleteObject(); cRgn.CreateFromPath(m_pdc); HRGN rrgn; rrgn=::CreateRectRgn(0,0,0,0); int res=CombineRgn(rrgn,cRgn.operator HRGN(),0,RGN_COPY); if(NULLREGION ==res) MessageBox("Null Region::Cannot create region for button"); else if(ERROR==res) MessageBox("Error::Cannot create region"); else { CRect rect; GetClientRect(rect); m_but= new CMyButton(); m_but->SetRgn(CRgn::FromHandle(rrgn)); m_but->Create("",WS_CHILD|WS_VISIBLE| WS_TABSTOP|BS_PUSHBUTTON, rect,this,1000+idcount++); } ReleaseCapture(); #endif CDialog::OnLButtonUp(nFlags, point); }
Note: BeginPath() is called in OnLButtonDown() and EndPath() is called in OnLButtonUp(). This is to grab the path from the drawn freehand drawing.
Following is the code to sense the clicked dynamically created buttons:
BOOL CButtonSubDlg::OnCommand(WPARAM wParam, LPARAM lParam)
{
if(HIWORD(wParam)==BN_CLICKED)
{
for(int i=0;i<idcount;i++)
{
if(LOWORD(wParam)==1000+i)
{
CString smsg;smsg.Format("Button %d Clicked",i+1);
MessageBox(smsg);
break;
}
}
}
return CDialog::OnCommand(wParam, lParam);
}
Finally delete all dynamically created buttons and the device cap (used to generate the path):
void CButtonSubDlg::OnDestroy() { CDialog::OnDestroy(); for(int i=0;i<idcount;i++) { m_but=(CMyButton *)GetDlgItem(1000+i); if(m_but) delete m_but; } #ifdef _SAMPLE1 if(m_pdc) delete m_pdc; #endif }
The region generated has been set to the CMyButton class using the following function:
void CMyButton::SetRgn(CRgn *region) { rgn=::CreateRectRgn(0,0,0,0); CombineRgn(rgn,region->operator HRGN(),NULL,RGN_COPY); trgn=::CreateRectRgn(0,0,0,0); CombineRgn(trgn,region->operator HRGN(),NULL,RGN_COPY); }
Region is where this demo application stands at all. The freehand drawing drawn by you will be generated into a path and the path is converted into region, finally a button is created using that region.
SetWindowRgn() function will set the shape for a window as by the given region. In the case of an owner-drawn button SetWindowRgn() is used to set the region of the button. But, this is not enough to visually set an irregular shaped button. For visual effects, you need to draw the button as per the region.
CombineRgn in this demo is used to copy a region into variables. RGN_COPY option lets the CombineRgn function to copy a region into another.
This demo project is of two parts, _SAMPLE1, _SAMPLE2. _SAMPLE1 is the project part defined above. _SAMPLE2 is the code sample from MSDN to change a button's text color. For the _SAMPLE2 project, go to project settings, and change _SAMPLE1 to _SAMPLE2 in the preprocessor definition.
None of the articles can satisfy one's expectations. But, each article should be a seed for your technical growth. Thus, I believe that this would be a seed. Thank you all.
| You must Sign In to use this message board. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||
General
News
Question
Answer
Joke
Rant
Admin
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 21 Sep 2005 Editor: Smitha Vijayan |
Copyright 2005 by Naren Neelamegam Everything else Copyright © CodeProject, 1999-2009 Web22 | Advertise on the Code Project |