Adding mouse functionality to any control

来源:互联网 发布:sql to_char 编辑:程序博客网 时间:2024/05/16 09:54
  • Download source files - 5 Kb
  • Download demo project - 21 Kb

Introduction

What is CMouseAction? CMouseAction is a serializable baseclass that allows any CWnd derived class to implement all manner of mouse movement actions. CMouseAction provides an implementation of mouse notifications, run-time resizing and moving, control transparency, control toolips, and automatic window placement. Automatic window placement is done through serialization of the window position when created or destroyed and then MoveWindow is used to properly place the control window on its parent. SetWindowName sets the window name used during serialization, so that each control window can easily be identified in a serialized file. The serialization requires you to call Serialize with the properly set CArchive. CMouseAction works for multiple classes by using #define to control its base class.

Collapse Copy Code
// In the mouseaction header you create define that look like this//// These base classes can easily be changed to any CWnd derived class#if defined(MYBTN)  #define BASECLASS CButton#elif defined(MYSLID)  #define BASECLASS CSliderCtrl#elif defined(MYTEXT)  #define BASECLASS CStatic#else  #define BASECLASS CWnd#endif/////////////////////////////////////////////////////////////////////////////// CMouseAction windowclass CMouseAction : public BASECLASS// BASECLASS will then be replaced with the proper base class // when included in each file{}

In your derived control class header file you create a define that maps to the proper class in the Mouseaction header:

Collapse Copy Code
#define MYBTN

or else

Collapse Copy Code
#define MYSLID

When the code is compiled, CMyBtn will be derived from CMouseAction, and CMouseAction will be derived from CButton for this instance. CMySlider will be derived from CMouseAction, and CMouseAction will be derived from CSlider for this instance. CMyStatic will be derived from CMouseAction, and CMouseAction will be derived from CStatic for this instance. Even though they are all derived from the same class each, by using #defines we are able to derive each from its proper class. After processing notifications are then sent to the proper baseclass by using our define's and calling, for ex.

Collapse Copy Code
BASECLASS::OnMouseMove(nFlags,point);

would call the OnMouseMove function from the CButton, CStatic, CSliderCtrl, or CWnd class depending on the #define in the subclassed header file.

When the mouse is moved over the control, we tell windows that we want to be notified of any mouse movements by simply calling:

Collapse Copy Code
if (!m_bTracking)    {        TRACKMOUSEEVENT tme;        tme.cbSize = sizeof(tme);        tme.hwndTrack = m_hWnd;        tme.dwFlags = TME_LEAVE|TME_HOVER;        tme.dwHoverTime = 1;        //Tell Windows that we want to process all mouse Hover and Leave Messages        m_bTracking = _TrackMouseEvent(&tme);         m_point = point;    }

from within the OnMouseMove function. This will then track the mouse and present us with two new functions that we need to implement.

Collapse Copy Code
//In the cpp message map callON_MESSAGE(WM_MOUSELEAVE, OnMouseLeave)ON_MESSAGE(WM_MOUSEHOVER, OnMouseHover)//In the header AFX_MSG sectionafx_msg LRESULT OnMouseLeave(WPARAM wparam, LPARAM lparam);afx_msg LRESULT OnMouseHover(WPARAM wparam, LPARAM lparam);

Within the OnMouse functions, we set BOOL m_bHover to true if the mouse is hovering over our control, and false otherwise. After setting m_bHover, we call Invalidate() so that our control will update its interface. From within the drawing code of our control, we then call IsHovering() to determine if we need to draw the control with the mouse hovering or not. We use IsHovering() in our drawing code, so that if the hover code is changed at all, or m_bHover changed, using IsHovering() will still determine if the mouse is hovering, and thus prevent future potential problems. If you wanted to add a third MouseDown state, you could easily add OnLButtonDown() to your control, set a boolean variable in the OnLButtonDown function, and Invalidate your control, thus providing three drawing states for the control, MouseHover, MouseOut/Leave, and MouseDown.

Collapse Copy Code
HBRUSH CMouseAction::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) {    HBRUSH hbr = NULL;        if (m_bTranparent)    {        pDC->SetBkMode(TRANSPARENT);//Set transparent background        LOGBRUSH brush;        brush.lbColor = HOLLOW_BRUSH;        brush.lbHatch = 0;        brush.lbStyle = 0;        hbr = CreateBrushIndirect(&brush);//Use a transparent brush for painting    }    else        hbr = BASECLASS::OnCtlColor(pDC, pWnd, nCtlColor);//Else use default    return hbr;}

If SetTransparent(TRUE) is used, then the control is set to draw with a transparent background. The transparent background is achieved by creating a NULL brush for our control when OnCtlColor is called. If our control is not transparent then the base class OnCtlColor is called and returns the proper background drawing brush. This allows our control to be drawn transparently or with the default background from our base class that was previously set using the BASECLASS defines.

We also allow for custom tooltips for each control window. SetToolTipText is used to set the text that will be displayed in the ToolTip. When the mouse hovers over the window, the tooltip is then displayed. The tooltip is displayed by using the OnMouseHover function, and immediately after calling RedrawWindow to update our control, we the update the tooltip to display since the mouse is now hovering. We update the tooltip while the mouse hovers by using the following code:

Collapse Copy Code
DeleteToolTip();//Remove old tooltip// Create a new Tooltip with new Button Size and LocationSetToolTipText(m_tooltext);if (m_ToolTip != NULL)    if (::IsWindow(m_ToolTip->m_hWnd))         //Display ToolTipm_ToolTip->Update();

We add runtime resizing and moving to control by implementing a CRectTracker along with four functions. First we use SetMoveable() to TRUE, to allow our control to be moved and resized. We use IsMoveable in our functions and drawing code to determine if the control is allowed to be moved or not by the user at runtime. If our control is allowed to be moved and resized at runtime, then we use the OnRButtonDown to allow the user to resize and move the control. Since it is common place for most controls to perform specific tasks when the left button is down, I have decided to use the OnLButtonDown to allow subclassed controls to perform their specific functions, while reserving the OnRButtonDown to perform the resizing and moving of the control. In addition to using OnRButtonDown, OnMove is also implemented to correct any potential issues when a transparent window is moved or resized over a window that uses a bitmap or image as its background. When the right mouse button is down, the user is allowed to move the control. When the CTRL key is down and the Right Button is clicked on our control, then the user is allowed to resize the window instead. If our control is already in resize or move mode, and the right button is clicked again, then the action is cancelled. If the left button is clicked while we are resizing or moving the control, then the control window is updated to the new size and location. See OnRButtonDown below:

Collapse Copy Code
void CMouseAction::OnRButtonDown( UINT nFlags, CPoint point ){    // Are we allowed to resize the window?    if ((IsMoveable() == TRUE) && (m_bResizing == FALSE))     {        m_bResizing = TRUE;        m_point = point;        CRect rect;        BOOL bSuccess = FALSE;        GetClientRect(rect);//Tell the tracker where we are        m_track = new CRectTracker(&rect, CRectTracker::dottedLine |                                 CRectTracker::resizeInside |                                CRectTracker::hatchedBorder);        if (nFlags & MK_CONTROL) // If Ctrl + Right-Click then Resize object        {            GetWindowRect(rect);            GetParent()->ScreenToClient(&rect);            //Let user resize window            bSuccess = m_track->TrackRubberBand(GetParent(),rect.TopLeft());            m_track->m_rect.NormalizeRect();        }        else // If not Ctrl + Right-Click, then Move Object        {            bSuccess = m_track->Track(GetParent(), point);//Let user move window        }        if (bSuccess)        {            rect = LPRECT(m_track->m_rect);            //ClientToScreen(&rect);            //GetParent()->ScreenToClient(&rect);            //SetWindowPos(&wndTopMost,rect.left,rect.top,                           rect.Width(),rect.Height(),SWP_SHOWWINDOW);            //Move Window to our new position            MoveWindow(rect.left,rect.top,                       rect.Width(),rect.Height(),TRUE);        }        delete m_track;        m_track = NULL;        rect = NULL;        m_bResizing = FALSE;    }    BASECLASS::OnRButtonDown(nFlags,point);}

To avoid issues with transparent controls over bitmapped windows, we use the following to hide the window, move it, and then show it and redraw it.

Collapse Copy Code
void CMouseAction::OnMove(int x, int y) {    BASECLASS::OnMove(x, y);        // This code is so that when a transparent control is moved    // and the dialog or app window behind the transparent control    // is showing a bitmap, this forces the parent to redraw    // before we redraw so that the bitmap is shown properly    // and also eliminates any window overlapping that may occur with    // using a Transparent Window on top of a Bitmap...    // If you are not using a transparent window, you shouldn't need this...        ShowWindow(SW_HIDE);// Hide Window    CRect rect;    GetWindowRect(&rect);    GetParent()->ScreenToClient(&rect);    GetParent()->InvalidateRect(&rect);//Tell Parent to redraw the rect    ShowWindow(SW_SHOW);//Now redraw us so that Control displays correctly}

This class helps add a lot of mouse functionality to any control window, and still allows us to subclass our control window from the proper class using the set defines. Hopefully this is useful to someone... Please remember that when deriving a control from this class you must change the class it is derived from in all areas, this includes the following:

Collapse Copy Code
class CText : public CMouseAction....//This is very important or else the mouse actions will not work properlyBEGIN_MESSAGE_MAP(CText, CMouseAction)     //{{AFX_MSG_MAP(CText)        // NOTE - the ClassWizard will add and remove mapping macros here.    //}}AFX_MSG_MAPEND_MESSAGE_MAP()void CText::OnLButtonDown(...){    CMouseAction::OnLButtonDown(...); //Don't forget this either}

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Fred Ackers


Member Programming using MFC and ATL for almost 12 years now. Currently studying Operating System implementation as well as Image processing. Previously worked on DSP and the use of FFT for audio application. Programmed using ADO, ODBC, ATL, COM, MFC for shell interfacing, databasing tasks, Internet items, and customization programs.
Occupation: Web DeveloperLocation: United States United States

 

原文章 http://www.codeproject.com/KB/miscctrl/cmouseaction.aspx

原创粉丝点击