Creating Windows XP Style ActiveX Button

Environment: VC6 SP4, NT4 SP3

Creating an ActiveX control is very simple if you use MFC wizard. Here I explain how to create a simple ActiveX button control in VC++ that looks like a Windows XP buttons.

Create a new project by selecting the MFC ActiveX control wizard. Give it the name "XpButtonEx." There are two steps in the ActiveX control wizard. In the first dialog box select one control, no runtime licensing, source-file comments, and no Help files. In the second dialog box select Activates When Visible, Available in "Insert Object" Dialog, and has an "About Box." Select "BUTTON" in the combo-box where the wizard asks: "Which window class, if any, should this control subclass?" Click Finish. AppWizard will create some 19 files. There are 3 classes—CxpButtonExApp, CxpButtonExCtrl, and CxpButtonExPropPage.

Now open the class wizard. Make sure that the selected class name is "CXpButtonExCtrl." Add the message map entries for WM_CREATE, WM_LBUTTONDOWN, WM_LBUTTONUP, and WM_MOUSEMOVE. Right-click "CXpButtonExCtrl" in the Class view tab and add the virtual function PreSubclassWindow. Add the line:

ModifyStyle(0, BS_OWNERDRAW|BS_NOTIFY)

after

COleControl::PreSubclassWindow()
in the PreSubclassWindow function. Now open XpButtonEx.h and add the following member variables and functions.

public:
CPen *pBoundryPen;
CPen *pInsideBoundryPenLeft;
CPen *pInsideBoundryPenTop;
CPen *pInsideBoundryPenRight;
CPen *pInsideBoundryPenBottom;
CPen *pOldPen;
CBrush *pFillActive;
CBrush *pFillInactive;
CBrush *pOldBrush;
BOOL m_bOverControl;
void DoGradientFill(CDC *pDC, CRect rect);
void DrawInsideBorder(CDC *pDC, CRect rect);

Open XpButtonEx.cpp. In the constructor function CXpButtonExCtrl(), add the following:

  m_bOverControl = FALSE;
  pBoundryPen = new CPen(PS_INSIDEFRAME|PS_SOLID,1,RGB(0,0,0));
  pInsideBoundryPenLeft = new CPen(PS_INSIDEFRAME|
      PS_SOLID,3,RGB(250,196,88));
  pInsideBoundryPenRight = new CPen(PS_INSIDEFRAME|
      PS_SOLID,3,RGB(251,202,106));
  pInsideBoundryPenTop = new CPen(PS_INSIDEFRAME|
      PS_SOLID,2,RGB(252,210,121));
  pInsideBoundryPenBottom = new CPen(PS_INSIDEFRAME|
      PS_SOLID,2,RGB(229,151,0));
  pFillActive = new CBrush(RGB(222,223,236));
  pFillInactive = new CBrush(RGB(222,223,236));

And in the destructor ~CXpButtonExCtrl(), delete the objects:

pBoundryPen->DeleteObject();
  pFillActive->DeleteObject();
  pFillInactive->DeleteObject();
  pOldPen->DeleteObject();
  pOldBrush->DeleteObject();
  pInsideBoundryPenLeft->DeleteObject();
  pInsideBoundryPenRight->DeleteObject();
  pInsideBoundryPenBottom->DeleteObject();
  pInsideBoundryPenTop->DeleteObject();

Add the following functions in XpButtonCtl.cpp:

void CXpButtonExCtrl::DoGradientFill(CDC *pDC, CRect rect)
{
    CBrush* pBrush[64];
    for (int i=0; i<64; i++)
      pBrush[i] = new CBrush(RGB(253-(i/2),
                             253-(i/3), 
                             253-(i/4)));
    int nWidth = (rect.right) - (rect.left);
    int nHeight = (rect.bottom) - (rect.top);
    CRect rct;

    for (i=rect.top; i < nHeight+2; i++)
  {
        rct.SetRect (rect.left, i, nWidth+2, i + 1);
        pDC->FillRect (&rct, pBrush[(i * 63) / nHeight]);
    }

    for (i=0; i<64; i++)
        delete pBrush[i];

}
void CXpButtonExCtrl::DrawInsideBorder(CDC *pDC,CRect rect)
{
  pOldPen = pDC->SelectObject(pInsideBoundryPenLeft);
  pDC->MoveTo(rect.left,rect.bottom-3);
  pDC->LineTo(rect.left,rect.top+2);
  pDC->SelectObject(pInsideBoundryPenRight);
  pDC->MoveTo(rect.right-1,rect.bottom-3);
  pDC->LineTo(rect.right-1,rect.top+2);
  pDC->SelectObject(pInsideBoundryPenTop);
  pDC->MoveTo(rect.left+2,rect.top);
  pDC->LineTo(rect.right-2,rect.top);
  pDC->SelectObject(pInsideBoundryPenBottom);
  pDC->MoveTo(rect.left+2,rect.bottom);
  pDC->LineTo(rect.right-2,rect.bottom);
  pDC->SelectObject(pOldPen);
}

Now go to the OnOcmCommand() and add the following switch statement before return 0.

...
switch(wNotifyCode)
{
  case BN_CLICKED:  // The click event should be fired
                    // when the button is clicked.
          FireClick();
   break;
}

We are using the Boolean variable m_bOverControll for tracking the mouse position. The button will receive WM_MOUSEMOVE when the mouse is over the button. Change the code of OnMouseMove to the following:

void CXpButtonExCtrl::OnMouseMove(UINT nFlags, CPoint point)
{
  // TODO: Add your message handler code
  // here and/or call default.

  COleControl::OnMouseMove(nFlags, point);
  if(!m_bOverControl)
  {
   m_bOverControl = TRUE;
   Invalidate(FALSE);
   TRACKMOUSEEVENT tm;
   tm.cbSize = sizeof(tm);
   tm.dwFlags = TME_LEAVE;
   tm.hwndTrack = this->m_hWnd;
   ::_TrackMouseEvent(&tm);
  }
}

Now, for detecting when the mouse leaves the button, we should manually add the following message handlers. Add

LRESULT OnMouseLeave(WPARAM, LPARAM);

in XpButtonExCtl.h and add

ON_MESSAGE(WM_MOUSELEAVE, OnMouseLeave)

in XpButtonExCtl.cpp. Add the function

LRESULT CXpButtonExCtrl::OnMouseLeave(WPARAM, LPARAM)
{
   m_bOverControl = FALSE;
  Invalidate(FALSE);
  return 0;
}

in XpButtonExCtl.cpp. Now, to draw the button, we should manually add the handler for the OCM_DRAWITEM message. Add LRESULT OnOcmDrawItem(WPARAM wParam, LPARAM lParam); in XpButtonExCtl.h and add ON_MESSAGE(OCM_DRAWITEM, OnOcmDrawItem) in XpButtonExCtl.cpp. Add the function OnOcmDrawItem in XpButtonEx.cpp.

LRESULT CXpButtonExCtrl::OnOcmDrawItem(WPARAM wParam,
                                       LPARAM lParam)
{
  UINT nIDCtl = (UINT) wParam;
  LPDRAWITEMSTRUCT lpDrawItemStruct = 
                            (LPDRAWITEMSTRUCT) lParam;
  CDC* pDC   = CDC::FromHandle(lpDrawItemStruct->hDC);
  CRect rect = lpDrawItemStruct->rcItem;
  UINT state = lpDrawItemStruct->itemState;
 
    // draw the control edges
  CPoint pt;
  pt.x = 10;
  pt.y = 10;
  
    pOldPen = pDC->SelectObject(pBoundryPen);
    if (state & ODS_SELECTED)
       pDC->RoundRect(rect,pt);
    else
       pDC->RoundRect(rect,pt);
    pDC->SelectObject(pOldPen);
    // Deflate the drawing rect by the size of the button's edges
    rect.DeflateRect( CSize(GetSystemMetrics(SM_CXEDGE),
                      GetSystemMetrics(SM_CYEDGE)));
    // Fill the interior color if necessary

   if (m_bOverControl)
   {
      pOldBrush = pDC->SelectObject(pFillActive);
      DoGradientFill(pDC,rect);
      DrawInsideBorder(pDC,rect);
   }
   else
   {
      pOldBrush = pDC->SelectObject(pFillInactive);
      DoGradientFill(pDC,rect);
   }
   pDC->SelectObject(pOldBrush);
    
      // Draw the text
    if (!m_title.IsEmpty())
    {
        CSize Extent = pDC->GetTextExtent(m_title/*strText*/);
        CPoint pt( rect.CenterPoint().x - Extent.cx/2, 
        rect.CenterPoint().y - Extent.cy/2 );

        if (state & ODS_SELECTED) 
            pt.Offset(1,1);

        int nMode = pDC->SetBkMode(TRANSPARENT);
        CFont *pOldFont = SelectStockFont( pDC );

        if (state & ODS_DISABLED)
            pDC->DrawState( pt,
                            Extent,
                            m_title,
                            DSS_DISABLED,
                            TRUE,
                            0, 
                            (HBRUSH)NULL);
        else
            pDC->TextOut(pt.x, pt.y, m_title);
pDC->SelectObject(pOldFont);
        pDC->SetBkMode(nMode);
    }
  return 0;
}

Now we should add two properties to our ActiveX Control. One is Tile and the other is Font. Open Class Wizard and select the Automation tab. Make sure that the selected class name is CxpButtonEx. Click on the "Add Property" button. In the Add Property dialog box, give it an external name such as "title" and select the type as Cstring. Accept the default names for the variable and function. Click OK. Close the class wizard by clicking OK. Add the following line in the DoPropExchange function in XpButtonExCtl.cpp after the TODO: comment:

PX_String( pPX, _T("title"), m_title, _T("Caption"));

Now go to the Resourses view tab and open the dialog template IDD_PROPPAGE_XPBUTTONEX. Remove the "TODO:..." statement. Add one edit box, IDC_TITLE. Now open the Class wizard and select the Member Variables tab. Select the class CxpButtonExPropPage. Select IDC_TITLE and click Add Variable. In the Add Member variable dialog box, give Member Variable a name such as m_title, Category - Value, Variable Type - CString, and give the Optional Property a name such as "title." Click OK. Close the class wizard by clicking OK.

Now we can add the stock property Font. Open Class Wizard and select the Automation tab. Make sure that the selected class name is CxpButtonEx. Click on the "Add Property" button. In the Add Property dialog box, select the external name as Font in the combo box. Click OK. Close the class wizard by clicking OK. Now we can set up a property page for the font. This is really simple because we can use a prewritten property page. Open XpButtonExCtl.cpp and find the following code.

BEGIN_PROPPAGEIDS(CDierollCtrl, 1)
  PROPPAGEID(CDierollPropPage::guid)
END_PROPPAGEIDS(CDierollCtrl)

In this, change the count to 2, and add another PROPPAGEID. The new code will be like this:

BEGIN_PROPPAGEIDS(CDierollCtrl, 2)
  PROPPAGEID(CDierollPropPage::guid)
  PROPPAGEID(CLSID_CFontPropPage)
END_PROPPAGEIDS(CDierollCtrl)

Now our control is ready. Test it in an ActiveX test container.

Downloads

Download demo project - 96.9 Kb
Download source - 5.48 Kb


Comments

  • Hvad ville livet til at komme vil være mere farverigt Studio

    Posted by wanzixiao on 06/04/2013 09:21am

    [url=http://www.beatsbydrdredanmark.350.com/]beats by dre danmark[/url] Beats “Supreme Sound” Headsets forskydninger, der er valgt i form af vej erklæring – selv om det faktum, at faktisk syntes at tale om! “Jeg er en god overdreven person i hans teenageår” The hovedtelefon anker var oprindeligt strålende sammen med betydelig med hensyn til neon . Men i tidligere tider flere aldre, har kunderne allerede blevet anvendt som vil high-end konkurrerende virksomheder anvender personlig bank kan synes, sammen med Beats indgået et system var oprindeligt det meste af tydeligt. Artiklen udformet en slags in-house industri team – tidligere var næsten hele pakken jobbet oprindeligt dyrket ud – at drage fordel af luftstrøm sammen med forbedre enhver mp3. Den populære sti i disse dage har en god imprintede Great Tone. [url=http://www.beatsbydrdredanmark.webgarden.com/]beats by dre tilbud[/url] Et emne BIII booo kunne udrette ved hjælp af din adeptness sammen med aktivitet indenfor en ny og mere omfattende midler sammen med komplet inkluderet tilsætning i fald rigdom elektrisk strøm. Dit hygge del i de sande hovedtelefoner er dybest set vidunderligt. Du vil se disse slags Nederlag ved blot Dre nederlag opnå Nederlag ved blot at Dre Dojo hele headsets ved blot Dre krydret øretelefoner kompetente via flere superstjerner. Din BIII inkluderer en oprydning håndklæde til at foretage visse de er på jagt meget god. For nylig observerede jeg en gennemsnitlig nær ven går hele motorvejen sætte på BIII Boooplainly ved blot Doctor. Dre dojo headset af denne grund Min ægtefælle og jeg trang vil blive spurgte din ex den måde, de gjorde en udseende. [url=http://www.beatsbydrdredanmark.blinkweb.com/]beats by dre danmark[/url] Hestekræfter samt reggae hele verden har kombineret såvel som resultatet er den nye Doctor Dre laptop. Med ferier her og også Dr. Dre laptop annoncer viser konstant på tv, er alle spekulerer på, om de skal købe det faktiske H. s.. Misundelse (den bestemte specialiserede navn med den personlige computer).Over-the-ear hovedtelefoner er fantastisk for ejendom bliver opmærksomme samt vejledning i at lukke ud lyde som du sover. On-ear typer bør også være behageligt, sammen med delikat ekstra polstring og robust konstruktion. Bare om de mest væsentlige ting sammen med on-ear design er at sikre, at de er installeret til brug sammen med stikkene i dine gadgets.

    Reply
  • A good ActiveX lesson

    Posted by Jay Tautges on 12/07/2004 02:30am

    I've been trying to get into ActiveX programming for a while, and this article was a great help. Thanks

    Reply
  • buttons in winxp

    Posted by Legacy on 01/22/2004 12:00am

    Originally posted by: rana

    how can i make the style of buttons in Win Xp in my project in vb6


    thanks

    Reply
  • full source please ;0

    Posted by Legacy on 10/25/2003 12:00am

    Originally posted by: george

    can you please post teh full source. i followed your instructions word by word and all i ever get is a grey label and no button where am i goign wrong.

    Reply
  • Very Good Code with small bug.

    Posted by Legacy on 10/03/2003 12:00am

    Originally posted by: Stephen Murphy

    This is some great code and offers a nice result. Thank you for posting it! I intend on using it in a future project that needs some cool buttons.

    One big problem I found was placing the new button control on a form in Access 2000, and then assigning some code to it which _closes_ the form. This will result in a fatal error which kills Access on the spot. I have traced the problem to the destructor lines:

    pOldPen->DeleteObject();
    pOldBrush->DeleteObject();

    This makes sense because in some situations, those objects may not even exist any more. They are pointers to objects in the device context which were there already. The button control held on to them only briefly while using its own brushes (which should be deleted). This control should not try to delete things it doesn't own.

    Having commented out these lines, the button runs wonderfully. They look great with some 256 colour icons drawn off to one side with the text on the other. I thank you again for posting some great material for others to benefit from.

    Reply
  • This is good work you boneheads!

    Posted by Legacy on 07/12/2003 12:00am

    Originally posted by: rep

    Those that think this article is outdated, worthless, whatever, what are you talking about?

    I like this article, especially for doing XP style buttons
    on *Win9x/2k*, and for projects w/o MFC! The manifest trick only works on XP w/ MFC. This code works on whatever platform I want!

    Good job on a well written article.

    Reply
  • Nice

    Posted by Legacy on 03/16/2003 12:00am

    Originally posted by: Rama Krishna

    Codeing is very nice and a very new idea.Congatulations.

    Reply
  • Memory Leak ?

    Posted by Legacy on 03/11/2003 12:00am

    Originally posted by: Benjamin Wang

    When the app is first run, it uses 2500k memory, after i do some opration(just click it and mouse over), the memory useage it up to 3076k. is it a memory leak?

    Reply
  • Very good!I like it.

    Posted by Legacy on 10/13/2002 12:00am

    Originally posted by: mjc0513

    it is very good,I am doing it.
    

    Reply
  • Very Nice! It's OK although have some small bug..

    Posted by Legacy on 09/24/2002 12:00am

    Originally posted by: qfly

    I test it,and it's very nice.Thanks anyway.(need to modify your article to correct some words that will lead someone have errors in their projects like some class's name)

    Reply
  • Loading, Please Wait ...

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: December 11, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT Market pressures to move more quickly and develop innovative applications are forcing organizations to rethink how they develop and release applications. The combination of public clouds and physical back-end infrastructures are a means to get applications out faster. However, these hybrid solutions complicate DevOps adoption, with application delivery pipelines that span across complex hybrid cloud and non-cloud environments. Check out this …

  • On-demand Event Event Date: October 29, 2014 It's well understood how critical version control is for code. However, its importance to DevOps isn't always recognized. The 2014 DevOps Survey of Practice shows that one of the key predictors of DevOps success is putting all production environment artifacts into version control. In this webcast, Gene Kim discusses these survey findings and shares woeful tales of artifact management gone wrong! Gene also shares examples of how high-performing DevOps …

Most Popular Programming Stories

More for Developers

RSS Feeds