创建WINDOWS XP样式的ActiveX按钮

 

使用MFC向导来创建一个ActiveX控件是一件非常简单的事。这篇文章里我会向大家阐述了怎样在VC++里面创建一个类似WINDOWS XP样式的ActiveX按钮。

选择MFC ActiveX控件向导来创建一个新的工程,并命名为“XpButtonEx”。在ActiveX控件向导里有两步。在第一个对话框中选择一个控件,不要运行时间许可,源文件注释和帮助文件。在第二个对话框中选择“Activates When Visible, Insert Object对话框中选择“Available”,还有一个About对话框。当向导询问:“Which window class, if any, should this control subclass?”时选择“BUTTON”。点完成。向导会建立大约19个文件,有三个类:CxpButtonExApp, CxpButtonExCtrlCxpButtonExPropPage

现在打开类向导,并确认当前选定的是CxpButtonExCtrl。为WM_CREATE, WM_LBUTTONDOWN, WM_LBUTTONUP,WM_MOUSEMOVE添加消息映射。在Class观察表中右击CxpButtonExCtrl,然后添加虚函数:PreSubclassWindow。在PreSubclassWindow函数的COleControl::PreSubclassWindow()添加语句:ModifyStyle(0, BS_OWNERDRAW|BS_NOTIFY)。现在打开XpButtonEx.h,添加下列成员变量和函数:

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

打开XpButtonEx.cpp。在构造函数CXpButtonExCtrl()添加下列代码:

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

在析构函数~CXpButtonExCtrl()里删除对象:

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

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

现在回到OnOcmCommand(),并在返回0值以前添加下列switch

...
  
  
switch(wNotifyCode)
  
  
{
  
  
  case BN_CLICKED:  // // The click event should be fired
   
   
                    // when the button is clicked.
  
  
          click事件应该在按钮按下时被激活
  
  
          FireClick();
  
  
   break;
  
  
}
  
  

我们使用Boolean变量m_bOverControll来跟踪鼠标位置。当鼠标在按钮上时,按钮接收到WM_MOUSEMOVE。将OnMouseMove的代码作如下改动:

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);
  
  
  }
  
  
}
  
  
现在,为了检测鼠标什么时候离开按钮,我们必须手动添加下列消息句柄。在XpButtonExCtl.h中添加:LRESULT OnMouseLeave(WPARAM, LPARAM);XpButtonExCtl.cpp中添加:ON_MESSAGE(WM_MOUSELEAVE, OnMouseLeave)。在XpButtonExCtl.cpp中添加函数:LRESULT CXpButtonExCtrl::OnMouseLeave(WPARAM, LPARAM)
  
  
{
  
  
   m_bOverControl = FALSE;
  
  
  Invalidate(FALSE);
  
  
  return 0;
  
  
}
  
  
现在,为了画出按钮,我们需要手动为OCM_DRAWITEM消息添加句柄。在 XpButtonExCtl.h 里添加LRESULT OnOcmDrawItem(WPARAM wParam, LPARAM lParam)。在XpButtonExCtl.cpp里添加add ON_MESSAGE(OCM_DRAWITEM, OnOcmDrawItem)。在XpButtonEx.cpp里添加函数OnOcmDrawItem
  
  
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;
  
  
 
  
  
    // 画出控件的边缘 
  
  
  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);
  
  
    //按照按钮边缘的大小缩小画图区域    rect.DeflateRect( CSize(GetSystemMetrics(SM_CXEDGE),
  
  
                      GetSystemMetrics(SM_CYEDGE)));
  
  
    //如果必要填充内部颜色
  
  
   if (m_bOverControl)
  
  
   {
  
  
      pOldBrush = pDC->SelectObject(pFillActive);
  
  
      DoGradientFill(pDC,rect);
  
  
      DrawInsideBorder(pDC,rect);
  
  
   }
  
  
   else
  
  
   {
  
  
      pOldBrush = pDC->SelectObject(pFillInactive);
  
  
      DoGradientFill(pDC,rect);
  
  
   }
  
  
   pDC->SelectObject(pOldBrush);
  
  
    
  
  
      //画出文本 
  
  
    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;
  
  
}
  
  
现在我们将对我们的ActiveX Control添加两个属性。一个是Tile,另一个是Font。打开类向导,选择自动控制(Automation)标签。确认所选类是CxpButtonEx。点击“Add Property”按钮。在Add Property对话框中,给它一个外部名称(如“title”)并选择类型为Cstring。接受默认的变量和函数。点击OK关闭类向导对话框。。在XpButtonExCtl.cpp里的DoPropExchange函数TODO注释后面添加下列语句:
  
  
PX_String( pPX, _T("title"), m_title, _T("Caption"));
  
  
现在到Resourses观察标签下打开对话框模板IDD_PROPPAGE_XPBUTTONEX,删除TODO:...声明。增加一个编辑框IDC_TITLE,现在打开类向导,选择成员变量标签。选择类CxpButtonExPropPage,选择IDC_TITLE然后点“Add Variable”。在成员变量对话框中,给一个成员变量赋名(m_title), Category - Value, Variable Type  Cstring。给Optional Property一个名字(如title)。点击OK关闭类向导对话框。
  
  
现在我们可以添加Font的一般属性。打开类向导选择自动控制标签。确认所选类名是CxpButtonEx。点击“Add Property”按钮。在组合框中选择外部名称(如font)。点击OK关闭类向导对话框。现在我们为font设置一个属性页。这真的很简单,因为我们可以使用先前写好的属性页。打开XpButtonExCtl.cpp,找到如下代码:
  
  
BEGIN_PROPPAGEIDS(CDierollCtrl, 1)
  
  
  PROPPAGEID(CDierollPropPage::guid)
  
  
END_PROPPAGEIDS(CdierollCtrl
  
  
在这里改变计数到2,添加另一个PROPPAGEID。新写的代码象这样:
  
  
BEGIN_PROPPAGEIDS(CDierollCtrl, 2)
  
  
  PROPPAGEID(CDierollPropPage::guid)
  
  
  PROPPAGEID(CLSID_CFontPropPage)
  
  
END_PROPPAGEIDS(CDierollCtrl)
  
  
现在我们的控件终于完成了。到另一个ActiveX测试器中去测试它吧!
  
  

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值