使用时在编辑资源时要设置BS_OWNERDRAW属性,并且CCoolButton将忽略WS_TABSTOP属性 
(按钮不获得输入焦点)。 
 
// CCoolButton is a class derived from CButton. 
// Most codes are copied from CBitmapButton. 
// The usage is just like CBitmapButton: 
// You declare a CCoolButton variable, and use AutoLoad class 
// member to load the bitmaps. The only difference is that 
// the four bitmaps have postfixes "N"(normal), "U"(Up),  
// "D"(Down) and "X"(Disabled). 
// You need to set the style OWNERDRAW and had better 
// unset the style TABSTOP when you edit the button resource. 
//  
// Designed by Scott Zhong, March 1998 
 
// CoolButton.h : header file 
// 
 
///////////////////////////////////////////////////////////////////////////// 
// CCoolButton window 
 
#ifndef _COOLBUTTON_H 
#define _COOLBUTTON_H 
 
#define CBS_NORMAL 0x0 
#define CBS_POPUP 0x1 
#define CBS_PUSHDOWN 0x2 
#define CBS_DISABLED 0x4 
 
// CCoolButton - push-button with 1->4 bitmap images 
class CCoolButton : public CButton 

// Construction 
        DECLARE_DYNAMIC(CCoolButton) 
public: 
        CCoolButton(); 
 
        BOOL LoadBitmaps(LPCTSTR lpszBitmapResource, 
                        LPCTSTR lpszBitmapResourceUp = NULL, 
                        LPCTSTR lpszBitmapResourceDown = NULL, 
                        LPCTSTR lpszBitmapResourceDisabled = NULL); 
        BOOL AutoLoad(UINT nID, CWnd* pParent); 
 
// Attributes 
 
protected: 
        UINT m_state; 
        void SetBitmapMode(UINT action = 0, UINT mode = 0); 
 
        // all bitmaps must be the same size 
        CBitmap m_bitmap;          // normal image (REQUIRED) 
        CBitmap m_bitmapUp;        // pop up image (OPTIONAL) 
        CBitmap m_bitmapDown;      // push down image (OPTIONAL) 
        CBitmap m_bitmapDisabled;  // disabled bitmap (OPTIONAL) 
 
        CRect m_clientRect; 
 
public:  
 
// Operations 
        void SizeToContent(); 
 
public: 
 
// Overrides 
        // ClassWizard generated virtual function overrides 
        //{{AFX_VIRTUAL(CCoolButton) 
        public: 
        virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct); 
        //}}AFX_VIRTUAL 
 
// Implementation 
public: 
#ifdef _DEBUG 
        virtual void AssertValid() const; 
        virtual void Dump(CDumpContext& dc) const; 
#endif 
 
        virtual ~CCoolButton(); 
 
        // Generated message map functions 
protected: 
        //{{AFX_MSG(CCoolButton) 
        afx_msg void OnMouseMove(UINT nFlags, CPoint point); 
        afx_msg void OnLButtonUp(UINT nFlags, CPoint point); 
        afx_msg void OnLButtonDown(UINT nFlags, CPoint point); 
        afx_msg void OnCancelMode(); 
        //}}AFX_MSG 
 
        DECLARE_MESSAGE_MAP() 
}; 
 
#endif 
 
///////////////////////////////////////////////////////////////////////////// 
 
 
// CoolButton.cpp : implementation file 
// 
 
#include "stdafx.h" 
#include "CoolButton.h" 
 
#ifdef AFX_AUX_SEG 
#pragma code_seg(AFX_AUX_SEG) 
#endif 
 
#ifdef _DEBUG 
#define new DEBUG_NEW 
#undef THIS_FILE 
static char THIS_FILE[] = __FILE__; 
#endif 
 
///////////////////////////////////////////////////////////////////////////// 
// CCoolButton 
 
CCoolButton::CCoolButton() 

        m_state = CBS_NORMAL; 

 
CCoolButton::~CCoolButton() 


 
// LoadBitmaps will load in one, two, three or all four bitmaps 
// returns TRUE if all specified images are loaded 
BOOL CCoolButton::LoadBitmaps(LPCTSTR lpszBitmapResource,  
        LPCTSTR lpszBitmapResourceUp, 
        LPCTSTR lpszBitmapResourceDown, 
        LPCTSTR lpszBitmapResourceDisabled) 

        // delete old bitmaps (if present) 
        m_bitmap.DeleteObject(); 
        m_bitmapUp.DeleteObject(); 
        m_bitmapDown.DeleteObject(); 
        m_bitmapDisabled.DeleteObject(); 
 
        if (!m_bitmap.LoadBitmap(lpszBitmapResource)) 
        { 
                TRACE0("Failed to load bitmap for normal image.\n"); 
                return FALSE;   // need this one image 
        } 
        BOOL bAllLoaded = TRUE; 
        if (lpszBitmapResourceUp != NULL) 
        { 
                if (!m_bitmapUp.LoadBitmap(lpszBitmapResourceUp)) 
                { 
                        TRACE0("Failed to load bitmap for pop-up image.\n"); 
                        bAllLoaded = FALSE; 
                } 
        } 
        if (lpszBitmapResourceDown != NULL) 
        { 
                if (!m_bitmapDown.LoadBitmap(lpszBitmapResourceDown)) 
                { 
                        TRACE0("Failed to load bitmap for push-down image.\n"); 
                        bAllLoaded = FALSE; 
                } 
        } 
        if (lpszBitmapResourceDisabled != NULL) 
        { 
                if (!m_bitmapDisabled.LoadBitmap(lpszBitmapResourceDisabled)) 
                        bAllLoaded = FALSE; 
        } 
 
        return bAllLoaded; 

 
// SizeToContent will resize the button to the size of the bitmap 
void CCoolButton::SizeToContent() 

        ASSERT(m_bitmap.m_hObject != NULL); 
        CSize bitmapSize; 
        BITMAP bmInfo; 
        VERIFY(m_bitmap.GetObject(sizeof(bmInfo), &bmInfo) == sizeof(bmInfo)); 
        m_clientRect = CRect(0, 0, bmInfo.bmWidth, bmInfo.bmHeight); 
                VERIFY(SetWindowPos(NULL, -1, -1, bmInfo.bmWidth, bmInfo.bmHeight, 
                SWP_NOMOVE|SWP_NOZORDER|SWP_NOREDRAW|SWP_NOACTIVATE));   

 
// Autoload will load the bitmap resources based on the text of 
//  the button 
// Using suffices "N", "U", "D" and "X" for normal/up/down/disabled 
BOOL CCoolButton::AutoLoad(UINT nID, CWnd* pParent) 

        // first attach the CCoolButton to the dialog control 
 
        if (!SubclassDlgItem(nID, pParent)) 
                return FALSE; 
 
        ASSERT(GetStyle() & BS_OWNERDRAW); 
        ModifyStyle(WS_TABSTOP, 0); 
 
        CString buttonName; 
        GetWindowText(buttonName); 
        ASSERT(!buttonName.IsEmpty());      // must provide a title 
 
        LoadBitmaps(buttonName + _T("N"), buttonName + _T("U"), 
          buttonName + _T("D"), buttonName + _T("X")); 
 
        // we need at least the primary 
        if (m_bitmap.m_hObject == NULL) 
                return FALSE; 
 
        // size to content 
        SizeToContent(); 
        return TRUE; 

 
// Draw the appropriate bitmap 
void CCoolButton::DrawItem(LPDRAWITEMSTRUCT lpDIS) 

        ASSERT(lpDIS != NULL); 
        // must have at least the first bitmap loaded before calling DrawItem 
        ASSERT(m_bitmap.m_hObject != NULL);     // required 
 
        // use the main bitmap for normal, the selected bitmap for push-down 
        CBitmap* pBitmap = &m_bitmap; 
        m_state = CBS_NORMAL; 
 
        UINT state = lpDIS->itemState; 
 
        if ((state & ODS_SELECTED) && m_bitmapDown.m_hObject != NULL) 
        { 
                pBitmap = &m_bitmapDown; 
                m_state = CBS_PUSHDOWN; 
        } 
        else if ((state & ODS_FOCUS) && m_bitmapUp.m_hObject != NULL) 
        { 
                pBitmap = &m_bitmapUp;   // third image for pop-up 
                m_state = CBS_POPUP; 
        } 
        else if ((state & ODS_DISABLED) && m_bitmapDisabled.m_hObject != NULL) 
        { 
                pBitmap = &m_bitmapDisabled;   // last image for disabled 
                m_state = CBS_DISABLED; 
        } 
                 
        // draw the whole button 
        CDC* pDC = CDC::FromHandle(lpDIS->hDC); 
        CDC memDC; 
        memDC.CreateCompatibleDC(pDC); 
        CBitmap* pOld = memDC.SelectObject(pBitmap); 
        if (pOld == NULL) 
                return;     // destructors will clean up 
 
        CRect rect; 
        rect.CopyRect(&lpDIS->rcItem); 
        pDC->BitBlt(rect.left, rect.top, rect.Width(), rect.Height(), 
                &memDC, 0, 0, SRCCOPY); 
        memDC.SelectObject(pOld); 

 
void CCoolButton::SetBitmapMode(UINT action, UINT mode) 

        DRAWITEMSTRUCT DIS; 
        DIS.CtlType = ODT_BUTTON; 
        DIS.CtlID = GetDlgCtrlID(); 
        DIS.itemAction = action; 
    DIS.itemState = mode; 
    DIS.hwndItem = GetSafeHwnd(); 
    DIS.hDC = GetDC()->GetSafeHdc(); 
    GetClientRect(&(DIS.rcItem)); 
        SendMessage(WM_DRAWITEM, (WPARAM)GetSafeHwnd(), (LPARAM)&DIS); 
        ReleaseDC(CDC::FromHandle(DIS.hDC)); 

 
///////////////////////////////////////////////////////////////////////////// 
// CCoolButton diagnostics 
#ifdef _DEBUG 
void CCoolButton::AssertValid() const 

        CButton::AssertValid(); 
 
        m_bitmap.AssertValid(); 
        m_bitmapUp.AssertValid(); 
        m_bitmapDown.AssertValid(); 
        m_bitmapDisabled.AssertValid(); 

 
void CCoolButton::Dump(CDumpContext& dc) const 

        CButton::Dump(dc); 
 
        dc << "m_bitmap = " << (UINT)m_bitmap.m_hObject; 
        dc << "\nm_bitmapUp = " << (UINT)m_bitmapUp.m_hObject; 
        dc << "\nm_bitmapDown = " << (UINT)m_bitmapDown.m_hObject; 
        dc << "\nm_bitmapDisabled = " << (UINT)m_bitmapDisabled.m_hObject; 
 
        dc << "\n"; 

#endif 
 
#ifdef AFX_INIT_SEG 
#pragma code_seg(AFX_INIT_SEG) 
#endif 
 
IMPLEMENT_DYNAMIC(CCoolButton, CButton) 
 
///////////////////////////////////////////////////////////////////////////// 
 
 
BEGIN_MESSAGE_MAP(CCoolButton, CButton) 
        //{{AFX_MSG_MAP(CCoolButton) 
        ON_WM_MOUSEMOVE() 
        ON_WM_LBUTTONUP() 
        ON_WM_LBUTTONDOWN() 
        ON_WM_CANCELMODE() 
        //}}AFX_MSG_MAP 
END_MESSAGE_MAP() 
 
///////////////////////////////////////////////////////////////////////////// 
// CCoolButton message handlers 
 
void CCoolButton::OnMouseMove(UINT nFlags, CPoint point)  

        // TODO: Add your message handler code here and/or call default 
        if (m_clientRect.PtInRect(point)) 
        { 
                if (m_state == CBS_NORMAL) 
                { 
                        // sets pop-up state 
                        SetBitmapMode(ODA_FOCUS, ODS_FOCUS); 
                        SetCapture(); 
                } 
        } 
        else 
        { 
                // if moving out of the button 
                ReleaseCapture(); 
                SetBitmapMode(); 
        } 
                 
        CWnd::OnMouseMove(nFlags, point); 

 
void CCoolButton::OnLButtonUp(UINT nFlags, CPoint point)  

        // TODO: Add your message handler code here and/or call default 
        if ((m_state == CBS_PUSHDOWN) || (m_clientRect.PtInRect(point))) 
        // if having been pressed and now released 
        // emulates a BN_CLICKED message 
        { 
                ReleaseCapture(); 
                SetBitmapMode(ODA_SELECT); 
                GetParent()->SendMessage(WM_COMMAND, MAKELPARAM(GetDlgCtrlID(), BN_CLICKED), (LPARAM)GetSafeHwnd()); 
        }        

 
void CCoolButton::OnLButtonDown(UINT nFlags, CPoint point)  

        // TODO: Add your message handler code here and/or call default 
        SetBitmapMode(ODA_SELECT, ODS_SELECTED);         

 
void CCoolButton::OnCancelMode()  

        CButton::OnCancelMode(); 
 
        // TODO: Add your message handler code here 
        // When another window switchs to the foreground 
        // reset the button state because of losing mouse capture 
        SetBitmapMode();         
}   
 

解决方案 »

  1.   

    我觉得不用那么复杂,重写OnSetCursor函数就可以了,如下:
    BOOL CFocusDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) 
    {
    if(pWnd==&m_btn)
    {
    m_btn.SetFocus();
    }
    else
    {
    SetFocus();
    }
    return CDialog::OnSetCursor(pWnd, nHitTest, message);

    }
    在OnInitDialog中加入以下两行:
    m_btn.LoadBitmaps(Button1U,Button1D,Button1D);
    m_btn.SizeToContent();
    简单吧
      

  2.   

    还要提醒一下,建议你多做一个“down”状态的位图,代替LoadBitmap函数中的第二个参数这样才有动感
      

  3.   


    两三句话就可以了!:关键字CButton
    获得按钮指针,
    判断鼠标消息;
    设置响应效果;
      

  4.   

    To dingsg
    希望把代码贴出来
      

  5.   

    最好自己做一个派生于CButton的类,我就是这样做的,派生的类可以响应各种CButton消息,这样比较好做一些.
      

  6.   

    我用了zyj_vc的方法实现了我的要求,但是
    if(pWnd==&m_btn)
    m_btn.SetFocus();
    else
    SetFocus();
    后,Dlg得到了焦点,m_btn闪来闪去,我只好加了一个不可见的IDC_STATIC,然后改代码为:
    if(pWnd==&m_btn)
    m_btn.SetFocus();
    else
    GetDlgItem(IDC_STATIC)->SetFocus();还有,运行后只有鼠标在Dlg上触发OnSetCursor后m_btn的位图才变成正常时的样子,否则一直是SetFocus时的位图,即使在OnInitDialog函数的最后加上
    GetDlgItem(IDC_STATIC)->SetFocus();
    也不行,我总觉得这个方法有点蹩脚,特别是把监测代码放在OnSetCursor中特别不舒服,还有其他办法吗?
    To dingsg:可以给出详细代码吗?
      

  7.   

    要监测鼠标移动消息一般有两个方法,一是监测WM_SETCURSOR消息,一是监测WM_MOUSEMOVE消息,我个人认为在这个问题中检测WM_SETCURSOR消息较好,首先是判断代码容易写,在WM_MOUSEMOVE消息中首先要坐标转换,再要判断。
    上次回复的方法不适用于只有一个按钮的对话框,我认为把鼠标不在按钮上时的状态设为Disable更好一些,代码做如下修改:
    //IDB_IN,IDB_OUT分别为鼠标在不在按钮上所对应的位图
    加入成员变量m_bIn代表按钮状态
    m_btn.LoadBitmaps(IDB_OUT);
    m_bIn=false;//这两句话加入到OnInitDialog中
    BOOL CFocusDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) 
    {
       if(pWnd==&m_btn)
       {
          if(!m_bIn)
          {
             m_btn.LoadBitmaps(IDB_IN);
             m_btn.Invalidate();
             m_bIn=true;
          }
       }
       else if(m_bIn)
       {
          m_btn.LoadBitmaps(IDB_OUT);
          m_btn.Invalidate();
          m_bIn=false;
       }
       return CDialog::OnSetCursor(pWnd, nHitTest, message);
    }
    处理WM_MOUSEMOVE消息很有点不好,鼠标移动到按钮上时,主窗口将接受不到消息,消息只发给按钮,所以要在按钮类中处理消息,即从CBitmapButton类中派生一个自己的类,然后处理WM_MOUSE消息,在主窗口中也要处理这一消息,太麻烦。坐标转换可以用MapWindowPoints函数。不知你说的“特别不舒服”是什么意思?鼠标移动到不同的窗口必定有WM_SETCURSOR消息,这就是我们所要的。