VS2019MFC类库之CrectTracker重写改造

35 篇文章 8 订阅
2 篇文章 0 订阅

        CrectTracker改造重写完成后的效果图如下:比起未改造之前的效果有了质的飞跃。下一步研究一下利用橡皮筋库截图功能。

        【重要说明】

        CrectTracker重写后,8个手柄可以实心填充也可以不用填充!应用程序文件参考网上代码随便写了一下,第一次创作,写的不清楚之处忘各位大神多担待。VS2019 CrectTracker的CPP文件在"C:/Program Files (x86)/Microsoft Visual Studio/2019/Professional/VC/Tools/MSVC/14.29.30133/atlmfc/src目录下文件名为TRCKRECT.CPP。头文件在AFXEXT.H。

 

         重建类CMyCrectTracker源文件和头文件以及对话框工程文件源码如下:

​
CMyCrectTracker.CPP文件:

#include "pch.h"
#include "CMyCrectTracker.h"
#include "C:/Program Files (x86)/Microsoft Visual Studio/2019/Professional/VC/Tools/MSVC/14.29.30133/atlmfc/src/mfc/afximpl.h"

#define new DEBUG_NEW

/
// CRectTracker global state

// various GDI objects we need to draw
AFX_STATIC_DATA HCURSOR _afxCursors[10] = { 0, };
AFX_STATIC_DATA HBRUSH _afxHatchBrush = 0;
AFX_STATIC_DATA HPEN _afxBlackDottedPen = 0;
AFX_STATIC_DATA int _afxHandleSize = 0;

void AFX_CDECL AfxTrackerTerm()
{
    AfxDeleteObject((HGDIOBJ*)&_afxHatchBrush);
    AfxDeleteObject((HGDIOBJ*)&_afxBlackDottedPen);
}
char _afxTrackerTerm = 0;

// the struct below is used to determine the qualities of a particular handle
struct AFX_HANDLEINFO
{
    size_t nOffsetX;    // offset within RECT for X coordinate
    size_t nOffsetY;    // offset within RECT for Y coordinate
    int nCenterX;       // adjust X by Width()/2 * this number
    int nCenterY;       // adjust Y by Height()/2 * this number
    int nHandleX;       // adjust X by handle size * this number
    int nHandleY;       // adjust Y by handle size * this number
    int nInvertX;       // handle converts to this when X inverted
    int nInvertY;       // handle converts to this when Y inverted
};

// this array describes all 8 handles (clock-wise)
AFX_STATIC_DATA const AFX_HANDLEINFO _afxHandleInfo[] =
{
    // corner handles (top-left, top-right, bottom-right, bottom-left
    { offsetof(RECT, left), offsetof(RECT, top),        0, 0,  0,  0, 1, 3 },
    { offsetof(RECT, right), offsetof(RECT, top),       0, 0, -1,  0, 0, 2 },
    { offsetof(RECT, right), offsetof(RECT, bottom),    0, 0, -1, -1, 3, 1 },
    { offsetof(RECT, left), offsetof(RECT, bottom),     0, 0,  0, -1, 2, 0 },

    // side handles (top, right, bottom, left)
    { offsetof(RECT, left), offsetof(RECT, top),        1, 0,  0,  0, 4, 6 },
    { offsetof(RECT, right), offsetof(RECT, top),       0, 1, -1,  0, 7, 5 },
    { offsetof(RECT, left), offsetof(RECT, bottom),     1, 0,  0, -1, 6, 4 },
    { offsetof(RECT, left), offsetof(RECT, top),        0, 1,  0,  0, 5, 7 }
};

// the struct below gives us information on the layout of a RECT struct and
//  the relationship between its members
struct AFX_RECTINFO
{
    size_t nOffsetAcross;   // offset of opposite point (ie. left->right)
    int nSignAcross;        // sign relative to that point (ie. add/subtract)
};
// this array is indexed by the offset of the RECT member / sizeof(int)
AFX_STATIC_DATA const AFX_RECTINFO _afxRectInfo[] =
{
    { offsetof(RECT, right), +1 },
    { offsetof(RECT, bottom), +1 },
    { offsetof(RECT, left), -1 },
    { offsetof(RECT, top), -1 },
};
CMyCrectTracker::CMyCrectTracker()
{
    Construct();
}

CMyCrectTracker::CMyCrectTracker(LPCRECT lpSrcRect, UINT nStyle)
{
    ASSERT(AfxIsValidAddress(lpSrcRect, sizeof(RECT), FALSE));
    Construct();
    m_rect.CopyRect(lpSrcRect);
    m_nStyle = nStyle;
}

void CMyCrectTracker::Draw(CDC* pDC,CPen *SolidPen,CPen *DashDotPen,COLORREF HandlePen) const
{
    VERIFY(pDC->SaveDC() != 0);
    pDC->SetMapMode(MM_TEXT);
    pDC->SetViewportOrg(0, 0);
    pDC->SetWindowOrg(0, 0);
    // get normalized rectangle
    CRect rect = m_rect;
    rect.NormalizeRect();

    CPen* pOldPen = NULL;
    CBrush* pOldBrush = NULL;
    CGdiObject* pTemp;
    int nOldROP;
    if ((m_nStyle & (dottedLine | solidLine)) != 0)
    {
        if (m_nStyle & dottedLine)
        {        
            pOldPen = pDC->SelectObject(DashDotPen);
        }
        else
        {        
            pOldPen = pDC->SelectObject(SolidPen);
        }
        pOldBrush = (CBrush*)pDC->SelectStockObject(NULL_BRUSH);
        rect.InflateRect(5, 5);   // borders are one pixel outside
        pDC->Rectangle(rect.left, rect.top, rect.right, rect.bottom);
        pDC->SelectObject(&pOldPen);
    }
    // if hatchBrush is going to be used, need to unrealize it
    if ((m_nStyle & (hatchInside | hatchedBorder)) != 0)
        UnrealizeObject(_afxHatchBrush);
    // hatch inside
    if ((m_nStyle & hatchInside) != 0)
    {
        pTemp = pDC->SelectStockObject(NULL_PEN);
        if (pOldPen == NULL)
            pOldPen = (CPen*)pTemp;
        pTemp = pDC->SelectObject(CBrush::FromHandle(_afxHatchBrush));
        if (pOldBrush == NULL)
            pOldBrush = (CBrush*)pTemp;
        pDC->SetBkMode(TRANSPARENT);
        nOldROP = pDC->SetROP2(R2_MASKNOTPEN);
        pDC->Rectangle(rect.left + 1, rect.top + 1, rect.right, rect.bottom);
        pDC->SetROP2(nOldROP);
    }
    // draw hatched border
    if ((m_nStyle & hatchedBorder) != 0)
    {
        pTemp = pDC->SelectObject(CBrush::FromHandle(_afxHatchBrush));
        if (pOldBrush == NULL)
            pOldBrush = (CBrush*)pTemp;
        pDC->SetBkMode(OPAQUE);
        CRect rectTrue;
        GetTrueRect(&rectTrue);
        pDC->PatBlt(rectTrue.left, rectTrue.top, rectTrue.Width(),
            rect.top - rectTrue.top, 0x000F0001 /* Pn */);
        pDC->PatBlt(rectTrue.left, rect.bottom,
            rectTrue.Width(), rectTrue.bottom - rect.bottom, 0x000F0001 /* Pn */);
        pDC->PatBlt(rectTrue.left, rect.top, rect.left - rectTrue.left,
            rect.Height(), 0x000F0001 /* Pn */);
        pDC->PatBlt(rect.right, rect.top, rectTrue.right - rect.right,
            rect.Height(), 0x000F0001 /* Pn */);
    }
    // draw resize handles
    if ((m_nStyle & (resizeInside | resizeOutside)) != 0)
    {
        UINT mask = GetHandleMask();
        for (int i = 0; i < 8; ++i)
        {
            if (mask & (1 << i))
            {
                GetHandleRect((TrackerHit)i, &rect);
                
                if (m_nStyle & dottedLine)
                {
                    pDC->FillSolidRect(rect, HandlePen);
                }
                else
                {
                    pDC->Rectangle(&rect);
                }
                
            }
        }
    }
    // cleanup pDC state
    if (pOldPen != NULL)
        pDC->SelectObject(pOldPen);
    if (pOldBrush != NULL)
        pDC->SelectObject(pOldBrush);
    VERIFY(pDC->RestoreDC(-1));
}

void CMyCrectTracker::GetTrueRect(LPRECT lpTrueRect) const
{
    ASSERT(AfxIsValidAddress(lpTrueRect, sizeof(RECT)));

    CRect rect = m_rect;
    rect.NormalizeRect();
    int nInflateBy = 0;
    if ((m_nStyle & (resizeOutside | hatchedBorder)) != 0)
        nInflateBy += GetHandleSize() - 1;
    if ((m_nStyle & (solidLine | dottedLine)) != 0)
        ++nInflateBy;
    rect.InflateRect(nInflateBy, nInflateBy);
    *lpTrueRect = rect;
}

BOOL CMyCrectTracker::SetCursor(CWnd* pWnd, UINT nHitTest) const
{
    // trackers should only be in client area
    if (nHitTest != HTCLIENT)
        return FALSE;

    // convert cursor position to client co-ordinates
    CPoint point;
    GetCursorPos(&point);
    pWnd->ScreenToClient(&point);

    // do hittest and normalize hit
    int nHandle = HitTestHandles(point);
    if (nHandle < 0)
        return FALSE;
    // need to normalize the hittest such that we get proper cursors
    nHandle = NormalizeHit(nHandle);

    // handle special case of hitting area between handles
    //  (logically the same -- handled as a move -- but different cursor)
    if (nHandle == hitMiddle && !m_rect.PtInRect(point))
    {
        // only for trackers with hatchedBorder (ie. in-place resizing)
        if (m_nStyle & hatchedBorder)
            nHandle = (TrackerHit)9;
    }
    ENSURE(nHandle < _countof(_afxCursors));
    ::SetCursor(_afxCursors[nHandle]);
    return TRUE;
}
BOOL CMyCrectTracker::Track(CWnd* pWnd, CPoint point, BOOL bAllowInvert, CWnd* pWndClipTo)
{
    // perform hit testing on the handles
    int nHandle = HitTestHandles(point);
    if (nHandle < 0)
    {
        // didn't hit a handle, so just return FALSE
        return FALSE;
    }
    // otherwise, call helper function to do the tracking
    m_bAllowInvert = bAllowInvert;
    return TrackHandle(nHandle, pWnd, point, pWndClipTo);
}

BOOL CMyCrectTracker::TrackRubberBand(CWnd* pWnd, CPoint point, BOOL bAllowInvert)
{
    // simply call helper function to track from bottom right handle
    m_bAllowInvert = bAllowInvert;
    m_rect.SetRect(point.x, point.y, point.x, point.y);
    return TrackHandle(hitBottomRight, pWnd, point, NULL);
}

int CMyCrectTracker::HitTest(CPoint point) const
{
    TrackerHit hitResult = hitNothing;

    CRect rectTrue;
    GetTrueRect(&rectTrue);
    ASSERT(rectTrue.left <= rectTrue.right);
    ASSERT(rectTrue.top <= rectTrue.bottom);
    if (rectTrue.PtInRect(point))
    {
        if ((m_nStyle & (resizeInside | resizeOutside)) != 0)
            hitResult = (TrackerHit)HitTestHandles(point);
        else
            hitResult = hitMiddle;
    }
    return hitResult;
}

int CMyCrectTracker::NormalizeHit(int nHandle) const
{
    ENSURE(nHandle <= 8 && nHandle >= -1);
    if (nHandle == hitMiddle || nHandle == hitNothing)
        return nHandle;
    ENSURE(0 <= nHandle && nHandle < _countof(_afxHandleInfo));
    const AFX_HANDLEINFO* pHandleInfo = &_afxHandleInfo[nHandle];
    if (m_rect.Width() < 0)
    {
        nHandle = (TrackerHit)pHandleInfo->nInvertX;
        ENSURE(0 <= nHandle && nHandle < _countof(_afxHandleInfo));
        pHandleInfo = &_afxHandleInfo[nHandle];
    }
    if (m_rect.Height() < 0)
        nHandle = (TrackerHit)pHandleInfo->nInvertY;
    return nHandle;
}

void CMyCrectTracker::DrawTrackerRect(LPCRECT lpRect, CWnd* pWndClipTo, CDC* pDC, CWnd* pWnd)
{
    CRect rect = *lpRect;
    rect.NormalizeRect();

    // convert to client coordinates
    if (pWndClipTo != NULL)
    {
        pWnd->ClientToScreen(&rect);
        pWndClipTo->ScreenToClient(&rect);
    }

    CSize size(0, 0);
    if (!m_bFinalErase)
    {
        // otherwise, size depends on the style
        if (m_nStyle & hatchedBorder)
        {
            size.cx = size.cy = max(1, GetHandleSize(rect) - 1);
            rect.InflateRect(size);
        }
        else
        {
            size.cx = AFX_CX_BORDER;
            size.cy = AFX_CY_BORDER;
        }
    }

    // and draw it
    if (m_bFinalErase || !m_bErase)
        pDC->DrawDragRect(rect, size, m_rectLast, m_sizeLast);

    // remember last rectangles
    m_rectLast = rect;
    m_sizeLast = size;
}

void CMyCrectTracker::AdjustRect(int nHandle, LPRECT lpRect)
{

    if (nHandle == hitMiddle)
        return;

    // convert the handle into locations within m_rect
    int* px, * py;
    GetModifyPointers(nHandle, &px, &py, NULL, NULL);

    // enforce minimum width
    int nNewWidth = m_rect.Width();
    int nAbsWidth = m_bAllowInvert ? abs(nNewWidth) : nNewWidth;
    if (px != NULL && nAbsWidth < m_sizeMin.cx)
    {
        nNewWidth = nAbsWidth != 0 ? nNewWidth / nAbsWidth : 1;
        ptrdiff_t iRectInfo = (int*)px - (int*)&m_rect;
        ENSURE(0 <= iRectInfo && iRectInfo < _countof(_afxRectInfo));
        const AFX_RECTINFO* pRectInfo = &_afxRectInfo[iRectInfo];
        *px = *(int*)((BYTE*)&m_rect + pRectInfo->nOffsetAcross) +
            nNewWidth * m_sizeMin.cx * -pRectInfo->nSignAcross;
    }

    // enforce minimum height
    int nNewHeight = m_rect.Height();
    int nAbsHeight = m_bAllowInvert ? abs(nNewHeight) : nNewHeight;
    if (py != NULL && nAbsHeight < m_sizeMin.cy)
    {
        nNewHeight = nAbsHeight != 0 ? nNewHeight / nAbsHeight : 1;
        ptrdiff_t iRectInfo = (int*)py - (int*)&m_rect;
        ENSURE(0 <= iRectInfo && iRectInfo < _countof(_afxRectInfo));
        const AFX_RECTINFO* pRectInfo = &_afxRectInfo[iRectInfo];
        *py = *(int*)((BYTE*)&m_rect + pRectInfo->nOffsetAcross) +
            nNewHeight * m_sizeMin.cy * -pRectInfo->nSignAcross;
    }
}

void CMyCrectTracker::OnChangedRect(const CRect& rectOld)
{
}

UINT CMyCrectTracker::GetHandleMask() const
{
    UINT mask = 0x0F;   // always have 4 corner handles
    int size = m_nHandleSize * 3;
    if (abs(m_rect.Width()) - size > 4)
        mask |= 0x50;
    if (abs(m_rect.Height()) - size > 4)
        mask |= 0xA0;
    return mask;
}

CMyCrectTracker::~CMyCrectTracker()
{
}

int CMyCrectTracker::HitTestHandles(CPoint point) const
{
    CRect rect;
    UINT mask = GetHandleMask();

    // see if hit anywhere inside the tracker
    GetTrueRect(&rect);
    if (!rect.PtInRect(point))
        return hitNothing;  // totally missed

    // see if we hit a handle
    for (int i = 0; i < 8; ++i)
    {
        if (mask & (1 << i))
        {
            GetHandleRect((TrackerHit)i, &rect);
            if (rect.PtInRect(point))
                return (TrackerHit)i;
        }
    }
    // last of all, check for non-hit outside of object, between resize handles
    if ((m_nStyle & hatchedBorder) == 0)
    {
        CRect rect = m_rect;
        rect.NormalizeRect();
        if ((m_nStyle & dottedLine | solidLine) != 0)
            rect.InflateRect(+m_nHandleSize / 2, +m_nHandleSize / 2);
        if (!rect.PtInRect(point))
            return hitNothing;  // must have been between resize handles
    }
    return hitMiddle;   // no handle hit, but hit 
}

void CMyCrectTracker::GetHandleRect(int nHandle, CRect* pHandleRect) const
{
    ASSERT(nHandle < 8);

    // get normalized rectangle of the tracker
    CRect rectT = m_rect;
    rectT.NormalizeRect();
    if ((m_nStyle & (solidLine | dottedLine)) != 0)
        rectT.InflateRect(+m_nHandleSize/2, +m_nHandleSize/2);
    // since the rectangle itself was normalized, we also have to invert the
    //  resize handles.
    nHandle = NormalizeHit(nHandle);

    // handle case of resize handles outside the tracker
    int size = GetHandleSize();
    if (m_nStyle & resizeOutside)
        rectT.InflateRect(size -m_nHandleSize / 2, size - m_nHandleSize / 2);

    // calculate position of the resize handle
    int nWidth = rectT.Width();
    int nHeight = rectT.Height();
    CRect rect;
    const AFX_HANDLEINFO* pHandleInfo = &_afxHandleInfo[nHandle];
    rect.left = *(int*)((BYTE*)&rectT + pHandleInfo->nOffsetX);
    rect.top = *(int*)((BYTE*)&rectT + pHandleInfo->nOffsetY);
    rect.left += size * pHandleInfo->nHandleX;
    rect.top += size * pHandleInfo->nHandleY;
    rect.left += pHandleInfo->nCenterX * (nWidth -size) / 2;
    rect.top += pHandleInfo->nCenterY * (nHeight - size) / 2;
    rect.right = rect.left + size;
    rect.bottom = rect.top + size;

    *pHandleRect = rect;
}

void CMyCrectTracker::GetModifyPointers(int nHandle, int** ppx, int** ppy, int* px, int* py)
{
ENSURE(nHandle >= 0);
ENSURE(nHandle <= 8);

if (nHandle == hitMiddle)
nHandle = hitTopLeft;   // same as hitting top-left

*ppx = NULL;
*ppy = NULL;

// fill in the part of the rect that this handle modifies
//  (Note: handles that map to themselves along a given axis when that
//   axis is inverted don't modify the value on that axis)

const AFX_HANDLEINFO* pHandleInfo = &_afxHandleInfo[nHandle];
if (pHandleInfo->nInvertX != nHandle)
{
    *ppx = (int*)((BYTE*)&m_rect + pHandleInfo->nOffsetX);
    if (px != NULL)
        *px = **ppx;
}
else
{
    // middle handle on X axis
    if (px != NULL)
        *px = m_rect.left + abs(m_rect.Width()) / 2;
}
if (pHandleInfo->nInvertY != nHandle)
{
    *ppy = (int*)((BYTE*)&m_rect + pHandleInfo->nOffsetY);
    if (py != NULL)
        *py = **ppy;
}
else
{
    // middle handle on Y axis
    if (py != NULL)
        *py = m_rect.top + abs(m_rect.Height()) / 2;
}
}

int CMyCrectTracker::GetHandleSize(LPCRECT lpRect) const
{
    if (lpRect == NULL)
        lpRect = &m_rect;

    int size = m_nHandleSize;
    if (!(m_nStyle & resizeOutside))
    {
        // make sure size is small enough for the size of the rect
        int sizeMax = min(abs(lpRect->right - lpRect->left),
            abs(lpRect->bottom - lpRect->top));
        if (size * 2 > sizeMax)
            size = sizeMax / 2;
    }
    return size;
}

BOOL CMyCrectTracker::TrackHandle(int nHandle, CWnd* pWnd, CPoint point, CWnd* pWndClipTo)
{
    ASSERT(nHandle >= 0);
    ASSERT(nHandle <= 8);   // handle 8 is inside the rect

    // don't handle if capture already set
    if (::GetCapture() != NULL)
        return FALSE;

    AfxLockTempMaps();  // protect maps while looping

    ASSERT(!m_bFinalErase);

    // save original width & height in pixels
    int nWidth = m_rect.Width();
    int nHeight = m_rect.Height();

    // set capture to the window which received this message
    pWnd->SetCapture();
    ASSERT(pWnd == CWnd::GetCapture());
    pWnd->UpdateWindow();
    if (pWndClipTo != NULL)
        pWndClipTo->UpdateWindow();
    CRect rectSave = m_rect;

    // find out what x/y coords we are supposed to modify
    int* px, * py;
    int xDiff, yDiff;
    GetModifyPointers(nHandle, &px, &py, &xDiff, &yDiff);
    xDiff = point.x - xDiff;
    yDiff = point.y - yDiff;

    // get DC for drawing
    CDC* pDrawDC;
    if (pWndClipTo != NULL)
    {
        // clip to arbitrary window by using adjusted Window DC
        pDrawDC = pWndClipTo->GetDCEx(NULL, DCX_CACHE);
    }
    else
    {
        // otherwise, just use normal DC
        pDrawDC = pWnd->GetDC();
    }
    ENSURE_VALID(pDrawDC);

    CRect rectOld;
    BOOL bMoved = FALSE;

    // get messages until capture lost or cancelled/accepted
    for (;;)
    {
        MSG msg;
        VERIFY(::GetMessage(&msg, NULL, 0, 0));

        if (CWnd::GetCapture() != pWnd)
            break;

        switch (msg.message)
        {
            // handle movement/accept messages
        case WM_LBUTTONUP:
        case WM_MOUSEMOVE:
            rectOld = m_rect;
            // handle resize cases (and part of move)
            if (px != NULL)
                *px = GET_X_LPARAM(msg.lParam) - xDiff;
            if (py != NULL)
                *py = GET_Y_LPARAM(msg.lParam) - yDiff;

            // handle move case
            if (nHandle == hitMiddle)
            {
                m_rect.right = m_rect.left + nWidth;
                m_rect.bottom = m_rect.top + nHeight;
            }
            // allow caller to adjust the rectangle if necessary
            AdjustRect(nHandle, &m_rect);

            // only redraw and callback if the rect actually changed!
            m_bFinalErase = (msg.message == WM_LBUTTONUP);
            if (!rectOld.EqualRect(&m_rect) || m_bFinalErase)
            {
                if (bMoved)
                {
                    m_bErase = TRUE;
                    DrawTrackerRect(&rectOld, pWndClipTo, pDrawDC, pWnd);
                }
                OnChangedRect(rectOld);
                if (msg.message != WM_LBUTTONUP)
                    bMoved = TRUE;
            }
            if (m_bFinalErase)
                goto ExitLoop;

            if (!rectOld.EqualRect(&m_rect))
            {
                m_bErase = FALSE;
                DrawTrackerRect(&m_rect, pWndClipTo, pDrawDC, pWnd);
            }
            break;

            // handle cancel messages
        case WM_KEYDOWN:
            if (msg.wParam != VK_ESCAPE)
                break;
        case WM_RBUTTONDOWN:
            if (bMoved)
            {
                m_bErase = m_bFinalErase = TRUE;
                DrawTrackerRect(&m_rect, pWndClipTo, pDrawDC, pWnd);
            }
            m_rect = rectSave;
            goto ExitLoop;

            // just dispatch rest of the messages
        default:
            DispatchMessage(&msg);
            break;
        }
    }

ExitLoop:
    if (pWndClipTo != NULL)
        pWndClipTo->ReleaseDC(pDrawDC);
    else
        pWnd->ReleaseDC(pDrawDC);
    ReleaseCapture();

    AfxUnlockTempMaps(FALSE);

    // restore rect in case bMoved is still FALSE
    if (!bMoved)
        m_rect = rectSave;
    m_bFinalErase = FALSE;
    m_bErase = FALSE;

    // return TRUE only if rect has changed
    return !rectSave.EqualRect(&m_rect);
}

void CMyCrectTracker::Construct()
{
    // do one-time initialization if necessary
    AfxLockGlobals(CRIT_RECTTRACKER);
    static BOOL bInitialized;
    if (!bInitialized)
    {


        // sanity checks for assumptions we make in the code
        ASSERT(sizeof(((RECT*)NULL)->left) == sizeof(int));
        ASSERT(offsetof(RECT, top) > offsetof(RECT, left));
        ASSERT(offsetof(RECT, right) > offsetof(RECT, top));
        ASSERT(offsetof(RECT, bottom) > offsetof(RECT, right));

        if (_afxHatchBrush == NULL)
        {
            // create the hatch pattern + bitmap
            WORD hatchPattern[8];
            WORD wPattern = 0x1111;
            for (int i = 0; i < 4; i++)
            {
                hatchPattern[i] = wPattern;
                hatchPattern[i + 4] = wPattern;
                wPattern <<= 1;
            }
            HBITMAP hatchBitmap = CreateBitmap(8, 8, 1, 1, hatchPattern);
            if (hatchBitmap == NULL)
            {
                AfxUnlockGlobals(CRIT_RECTTRACKER);
                AfxThrowResourceException();
            }

            // create black hatched brush
            _afxHatchBrush = CreatePatternBrush(hatchBitmap);
            DeleteObject(hatchBitmap);
            if (_afxHatchBrush == NULL)
            {
                AfxUnlockGlobals(CRIT_RECTTRACKER);
                AfxThrowResourceException();
            }
        }

        if (_afxBlackDottedPen == NULL)
        {
            // create black dotted pen
            _afxBlackDottedPen = CreatePen(PS_DOT, 0, RGB(0, 0, 0));
            if (_afxBlackDottedPen == NULL)
            {
                AfxUnlockGlobals(CRIT_RECTTRACKER);
                AfxThrowResourceException();
            }
        }

        // Note: all track cursors must live in same module
        HINSTANCE hInst = AfxFindResourceHandle(
            ATL_MAKEINTRESOURCE(AFX_IDC_TRACK4WAY), ATL_RT_GROUP_CURSOR);

        // initialize the cursor array
        _afxCursors[0] = ::LoadCursorW(hInst, ATL_MAKEINTRESOURCEW(AFX_IDC_TRACKNWSE));
        _afxCursors[1] = ::LoadCursorW(hInst, ATL_MAKEINTRESOURCEW(AFX_IDC_TRACKNESW));
        _afxCursors[2] = _afxCursors[0];
        _afxCursors[3] = _afxCursors[1];
        _afxCursors[4] = ::LoadCursorW(hInst, ATL_MAKEINTRESOURCEW(AFX_IDC_TRACKNS));
        _afxCursors[5] = ::LoadCursorW(hInst, ATL_MAKEINTRESOURCEW(AFX_IDC_TRACKWE));
        _afxCursors[6] = _afxCursors[4];
        _afxCursors[7] = _afxCursors[5];
        _afxCursors[8] = ::LoadCursorW(hInst, ATL_MAKEINTRESOURCEW(AFX_IDC_TRACK4WAY));
        _afxCursors[9] = ::LoadCursorW(hInst, ATL_MAKEINTRESOURCEW(AFX_IDC_MOVE4WAY));

        // get default handle size from Windows profile setting
        static const TCHAR szWindows[] = _T("windows");
        static const TCHAR szInplaceBorderWidth[] =
            _T("oleinplaceborderwidth");
        _afxHandleSize = GetProfileInt(szWindows, szInplaceBorderWidth, 4);
        bInitialized = TRUE;
    }
    if (!_afxTrackerTerm)
        _afxTrackerTerm = (char)!atexit(&AfxTrackerTerm);
    AfxUnlockGlobals(CRIT_RECTTRACKER);

    m_nStyle = 0;
    m_nHandleSize = _afxHandleSize;
    m_sizeMin.cy = m_sizeMin.cx = m_nHandleSize * 2;

    m_rectLast.SetRectEmpty();
    m_sizeLast.cx = m_sizeLast.cy = 0;
    m_bErase = FALSE;
    m_bFinalErase = FALSE;
}
 

CMyCrectTracker.h文件:

#pragma once
#include <afxext.h>
class CMyCrectTracker/* :
    public CRectTracker*/
{
   /* DECLARE_DYNAMIC(CRectTracker)*/
public:
    CMyCrectTracker();
    CMyCrectTracker(LPCRECT lpSrcRect, UINT nStyle);

    // Style Flags
    enum StyleFlags
    {
        solidLine = 1, dottedLine = 2, hatchedBorder = 4,
        resizeInside = 8, resizeOutside = 16, hatchInside = 32,
    };
    // Hit-Test codes
    enum TrackerHit
    {
        hitNothing = -1,
        hitTopLeft = 0, hitTopRight = 1, hitBottomRight = 2, hitBottomLeft = 3,
        hitTop = 4, hitRight = 5, hitBottom = 6, hitLeft = 7, hitMiddle = 8
    };
    // Attributes
    UINT m_nStyle;      // current state
    CRect m_rect;       // current position (always in pixels)
    CSize m_sizeMin;    // minimum X and Y size during track operation
    int m_nHandleSize;  // size of resize handles (default from WIN.INI)
    // Operations
    void Draw(CDC* pDC, CPen *SolidPen, CPen *DashDotPen, COLORREF HandlePen) const;
    void GetTrueRect(LPRECT lpTrueRect) const;
    BOOL SetCursor(CWnd* pWnd, UINT nHitTest) const;
    BOOL Track(CWnd* pWnd, CPoint point, BOOL bAllowInvert = FALSE,
        CWnd* pWndClipTo = NULL);
    BOOL TrackRubberBand(CWnd* pWnd, CPoint point, BOOL bAllowInvert = TRUE);
    int HitTest(CPoint point) const;
    int NormalizeHit(int nHandle) const;

    // Overridables
    virtual void DrawTrackerRect(LPCRECT lpRect, CWnd* pWndClipTo,
        CDC* pDC, CWnd* pWnd);
    virtual void AdjustRect(int nHandle, LPRECT lpRect);
    virtual void OnChangedRect(const CRect& rectOld);
    virtual UINT GetHandleMask() const;

    // Implementation
public:
    virtual ~CMyCrectTracker();

protected:
    BOOL  m_bAllowInvert;    // flag passed to Track or TrackRubberBand
    CRect m_rectLast;
    CSize m_sizeLast;
    BOOL  m_bErase;          // TRUE if DrawTrackerRect is called for erasing
    BOOL  m_bFinalErase;     // TRUE if DragTrackerRect called for final erase

    // implementation helpers
    int HitTestHandles(CPoint point) const;
    void GetHandleRect(int nHandle, CRect* pHandleRect) const;
    void GetModifyPointers(int nHandle, int** ppx, int** ppy, int* px, int* py);
    virtual int GetHandleSize(LPCRECT lpRect = NULL) const;
    BOOL TrackHandle(int nHandle, CWnd* pWnd, CPoint point, CWnd* pWndClipTo);
    void Construct();
};



 

 

 

 

 

 应用程序CPP文件:

#include "pch.h"
#include "framework.h"
#include "MFCApplicationCRectTracker.h"
#include "MFCApplicationCRectTrackerDlg.h"
#include "afxdialogex.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif
CPen     m_SPen(PS_SOLID, 1, RGB(0, 255, 0));
CPen     m_DPen(PS_DASHDOT, 1, RGB(0, 0, 255));
COLORREF m_ColorHandle = RGB(0, 0, 255);
class CAboutDlg : public CDialogEx
{
public:
    CAboutDlg();

#ifdef AFX_DESIGN_TIME
    enum { IDD = IDD_ABOUTBOX };
#endif

    protected:
    virtual void DoDataExchange(CDataExchange* pDX);   

protected:
    DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
    EnableActiveAccessibility();
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()


CMFCApplicationCRectTrackerDlg::CMFCApplicationCRectTrackerDlg(CWnd* pParent /*=nullptr*/)
    : CDialogEx(IDD_MFCAPPLICATIONCRECTTRACKER_DIALOG, pParent)
{
    EnableActiveAccessibility();
    m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
    m_IsChose = FALSE;
    m_IsDraw = FALSE;
    m_rectNum = 0;
    m_rectChoseNum = 0;
    m_FlaMoveStep = 1;
    m_dirct = 0;
}

void CMFCApplicationCRectTrackerDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CMFCApplicationCRectTrackerDlg, CDialogEx)
    ON_WM_SYSCOMMAND()
    ON_WM_PAINT()
    ON_WM_QUERYDRAGICON()
    ON_WM_LBUTTONDOWN()
    ON_WM_SETCURSOR()
    ON_WM_KEYDOWN()
    ON_BN_CLICKED(IDC_BUTTONDRAW, &CMFCApplicationCRectTrackerDlg::OnBnClickedButtondraw)
END_MESSAGE_MAP()


BOOL CMFCApplicationCRectTrackerDlg::OnInitDialog()
{
    CDialogEx::OnInitDialog();
    ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
    ASSERT(IDM_ABOUTBOX < 0xF000);

    CMenu* pSysMenu = GetSystemMenu(FALSE);
    if (pSysMenu != nullptr)
    {
        BOOL bNameValid;
        CString strAboutMenu;
        bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
        ASSERT(bNameValid);
        if (!strAboutMenu.IsEmpty())
        {
            pSysMenu->AppendMenu(MF_SEPARATOR);
            pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
        }
    }

    SetIcon(m_hIcon, TRUE);            // 设置大图标
    SetIcon(m_hIcon, FALSE);        // 设置小图标
    ShowWindow(SW_MAXIMIZE);
    m_rctCurTracker.m_rect.SetRect(0,0,0,0);
    m_rctCurTracker.m_nStyle = CMyCrectTracker::dottedLine | CMyCrectTracker::resizeInside;
    m_rctCurTracker.m_nHandleSize = 10;
    for (int i = 0; i < MAX_RECT_NUM; i++)
    {
        m_rctTracker[i].m_rect.SetRect(0,0,0,0);
        m_rctTracker[i].m_nStyle= CMyCrectTracker::dottedLine | CMyCrectTracker::resizeInside;
        m_rctTracker[i].m_nHandleSize = 10;
    }

    return TRUE;  
}

void CMFCApplicationCRectTrackerDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
    if ((nID & 0xFFF0) == IDM_ABOUTBOX)
    {
        CAboutDlg dlgAbout;
        dlgAbout.DoModal();
    }
    else
    {
        CDialogEx::OnSysCommand(nID, lParam);
    }
}

void CMFCApplicationCRectTrackerDlg::OnPaint()
{
    CPaintDC pDC(this);
    if (IsIconic())
    {
        CPaintDC dc(this); 

        SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

        int cxIcon = GetSystemMetrics(SM_CXICON);
        int cyIcon = GetSystemMetrics(SM_CYICON);
        CRect rect;
        GetClientRect(&rect);
        int x = (rect.Width() - cxIcon + 1) / 2;
        int y = (rect.Height() - cyIcon + 1) / 2;

        dc.DrawIcon(x, y, m_hIcon);
    }
    else
    {
        CDialogEx::OnPaint();
     {
            if (m_IsChose)
                m_rctCurTracker.Draw(&pDC,&m_SPen,&m_DPen,m_ColorHandle);
                CPen m_Pen(PS_SOLID, 1, RGB(255, 0, 0));
                CPen* m_OldPen;
                m_OldPen = NULL;
                m_OldPen = pDC.SelectObject(&m_Pen);
                CBrush* m_CBrush = CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));
                pDC.SelectObject(m_CBrush);
                CRect rct;
                m_rctCurTracker.GetTrueRect(&rct);
                pDC.Rectangle(&rct);
                CSize rct_size;
                for (int i = 0; i < MAX_RECT_NUM; i++)
                {
                    m_rctTracker[i].GetTrueRect(&rct);
                    rct_size = m_rctTracker[i].m_rect.Size();
                    if (rct_size.cx * rct_size.cy == 0 || i == m_rectChoseNum)
                    {
                        continue;
                    }
                    pDC.Rectangle(&rct);
                }
                m_Pen.DeleteObject();
     }
        
    }
}

HCURSOR CMFCApplicationCRectTrackerDlg::OnQueryDragIcon()
{
    return static_cast<HCURSOR>(m_hIcon);
}

BOOL CMFCApplicationCRectTrackerDlg::PreTranslateMessage(MSG* pMsg)
{
    if (pMsg->message==WM_KEYDOWN)
    {
        switch (pMsg->wParam)
        {
        case VK_LEFT:
                m_dirct = 1;
                break;
        case VK_RIGHT:
                m_dirct = 2;
                break;
        case VK_UP:
                m_dirct = 3;
                break;
        case VK_DOWN:
                m_dirct = 4;
                break;
        case VK_DELETE:
                m_dirct = 5;
                break;
        default:
                m_dirct = 0;
            break;
        }
        ChangeRectPt(m_dirct);
    }
    return CDialogEx::PreTranslateMessage(pMsg);
}


void CMFCApplicationCRectTrackerDlg::OnLButtonDown(UINT nFlags, CPoint point)
{
    BOOL IsInRct = FALSE;
    int i = 0;
    do
    {
        if (m_rctTracker[i].HitTest(point)<0)
        {
            IsInRct = FALSE;
        }
        else
        {
            IsInRct = TRUE;
            m_rectChoseNum = i;
            m_rctCurTracker = m_rctTracker[m_rectChoseNum];
            m_IsChose = TRUE;
            break;
        }
        i++;

    } while (i<m_rectNum);
    if (!IsInRct)
    {
        CMyCrectTracker temRectTracker;
        CRect p_rect;
        temRectTracker.TrackRubberBand(this,point);
        temRectTracker.m_rect.NormalizeRect();
        if (p_rect.IntersectRect(temRectTracker.m_rect,m_rctCurTracker.m_rect))
            m_IsChose = TRUE;
        else
        {
            m_IsChose = FALSE;
            if (m_IsDraw)
            {
                m_rctTracker[m_rectNum].m_rect = temRectTracker.m_rect;
                m_rctCurTracker.m_rect = temRectTracker.m_rect;
                CClientDC dc(this);
                m_rctCurTracker.Draw(&dc, &m_SPen, &m_DPen, m_ColorHandle);
                m_rectChoseNum = m_rectNum;
                m_rectNum++;
                if (m_rectNum>MAX_RECT_NUM)
                {
                    m_rectNum = MAX_RECT_NUM;
                    MessageBox(_T("已画区域超出上限,不能继续绘画!"),_T("警告!"),MB_OK|MB_ICONWARNING);
                }
                m_IsChose = TRUE;
                m_IsDraw = FALSE;
                Invalidate();
            }
        }
        Invalidate();
    }
    else
    {
        CClientDC pc(this);
        m_rctCurTracker.Track(this,point);
        m_rctCurTracker.m_rect.NormalizeRect();
        m_rctTracker[m_rectChoseNum] = m_rctCurTracker;
        m_IsChose = TRUE;
        Invalidate();
    }
    CDialogEx::OnLButtonDown(nFlags, point);
}


BOOL CMFCApplicationCRectTrackerDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
    if (pWnd==this&&m_rctCurTracker.SetCursor(this,nHitTest))
    {
        return TRUE;
    }
    return CDialogEx::OnSetCursor(pWnd, nHitTest, message);
}


void CMFCApplicationCRectTrackerDlg::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
    
    CDialogEx::OnKeyDown(nChar, nRepCnt, nFlags);
}


void CMFCApplicationCRectTrackerDlg::OnBnClickedButtondraw()
{
    m_IsDraw = TRUE;
}

void CMFCApplicationCRectTrackerDlg::ChangeRectPt(int ChangeDir)
{
    CRect rct;
    rct = m_rctCurTracker.m_rect;
    switch (ChangeDir)
    {
    case 1:
        rct.TopLeft().x-= m_FlaMoveStep;
        rct.BottomRight().x-= m_FlaMoveStep;
        break;
    case 2:
        rct.TopLeft().x += m_FlaMoveStep;
        rct.BottomRight().x+= m_FlaMoveStep;
        break;
    case 3:
        rct.TopLeft().y-= m_FlaMoveStep;
        rct.BottomRight().y-= m_FlaMoveStep;
        break;
    case 4:
        rct.TopLeft().y+= m_FlaMoveStep;
        rct.BottomRight().y+= m_FlaMoveStep;
        break;
    case 5:
        m_rctCurTracker.m_rect.SetRect(0,0,0,0);
        m_rctTracker[m_rectChoseNum] = m_rctCurTracker;
        m_dirct = 0;
        Invalidate();
        return;
    }
    m_rctCurTracker.m_rect.SetRect(rct.TopLeft(),rct.BottomRight());
    m_rctTracker[m_rectChoseNum] = m_rctCurTracker;
    if (ChangeDir!=0)
    {
        Invalidate();
    }
    m_dirct = 0;
}
应用程序头文件:

#include"CMyCrectTracker.h"
#pragma once

class CMFCApplicationCRectTrackerDlg : public CDialogEx
{

public:
    CMFCApplicationCRectTrackerDlg(CWnd* pParent = nullptr);    

#ifdef AFX_DESIGN_TIME
    enum { IDD = IDD_MFCAPPLICATIONCRECTTRACKER_DIALOG };
#endif

    protected:
    virtual void DoDataExchange(CDataExchange* pDX);    

protected:
    HICON m_hIcon;
    virtual BOOL OnInitDialog();
    afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
    afx_msg void OnPaint();
    afx_msg HCURSOR OnQueryDragIcon();
    DECLARE_MESSAGE_MAP()
public:
    
    CMyCrectTracker m_rctCurTracker;                //当前选中的矩形区域
    CMyCrectTracker m_rctTracker[MAX_RECT_NUM];        //存储自绘矩形区域
    BOOL            m_IsChose;                        //标记是否选中
    BOOL            m_IsDraw;                        //标记按钮是否按下
    int                m_rectNum;                        //当前实际已绘矩形的个数
    int                m_rectChoseNum;                    //当前选中矩形的编号
    int                m_FlaMoveStep;                    //键盘方向键没响应一次图像移动的像素单位上的步长
    int                m_dirct;                        //用于标记那个方向键按下。1:左,2:右,3:上,4:下,5:delete
    virtual BOOL PreTranslateMessage(MSG* pMsg);
    afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
    afx_msg BOOL OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message);
    afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
    afx_msg void OnBnClickedButtondraw();
    afx_msg void ChangeRectPt(int ChangeDir);
};

​

 最新更新:增加截图功能和JPG保存功能(细节方面后期还须进一步更新),下面代码为保存选择区域“按钮”事件源代码。

void CMFCApplicationCRectTrackerDlg::OnBnClickedButtonsavepicture()
{
	CBitmap m_PictureSaveToPC;
	CDC     m_DcSave;
	CClientDC m_SaveDc(this);
	CString CFilePathName;
	TCHAR szFilter[] = _T("*.JPG图片文件(.JPG)|*.txt|配置文件(.ini)|*.ini|word文件(.doc)|*.doc|所有文件(*.*)|*.*||");
	CFileDialog dlg(FALSE,_T(".JPG"), _T("m_PictureSaveToPC.JPG"), OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, szFilter, this, 0, TRUE);
	if (dlg.DoModal() == IDCANCEL)
	{
		MessageBox(_T("保存文件已取消!"), _T("信息提示!"), MB_OK | MB_OKCANCEL | MB_ICONINFORMATION);
		return;
	}
	CFilePathName = dlg.m_ofn.lpstrFile;
	CFilePathName.TrimRight(_T(".txt"));
	CFilePathName = CFilePathName + dlg.m_ofn.lpstrDefExt;
	if (m_IsChose==TRUE)
	{
		m_PictureSaveToPC.CreateCompatibleBitmap(&m_SaveDc, m_rctCurTracker.m_rect.Width(), m_rctCurTracker.m_rect.Height());
		m_DcSave.CreateCompatibleDC(&m_SaveDc);
		m_DcSave.SelectObject(m_PictureSaveToPC);
		m_DcSave.BitBlt(0,0, m_rctCurTracker.m_rect.Width(), m_rctCurTracker.m_rect.Height(),&m_SaveDc, m_rctCurTracker.m_rect.left, m_rctCurTracker.m_rect.top,SRCCOPY);
		CImage m_Image;
		m_Image.Attach((HBITMAP)m_PictureSaveToPC);
		m_Image.Save(CFilePathName);
		m_Image.Detach();
		MessageBox(_T("选择区域已保存到:")+ CFilePathName+_T("目录下!"), _T("信息提示:"), MB_OK | MB_ICONINFORMATION);
		m_PictureSaveToPC.DeleteObject();
		m_DcSave.DeleteDC();
	}
	else
	{
		MessageBox(_T("当前要保存的区域没有选中,请先选中后重试!"),_T("信息提示:"),MB_OK|MB_ICONERROR);
	}

}

VS2019 MFC类库之CrectTracker类库重写源代码连接如下: 

https://download.csdn.net/download/lzc881012/74778076https://download.csdn.net/download/lzc881012/74778076

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值