Programming with Microsoft Visual C++.NET 6ed [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Programming with Microsoft Visual C++.NET 6ed [Electronic resources] - نسخه متنی

George Shepherd, David Kruglinski

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
توضیحات
افزودن یادداشت جدید


The Ex06c Example: <i class="emphasis">CScrollView </i>Revisited






The Ex06c Example: CScrollView Revisited


You saw the CScrollView class in Chapter 5 (in Ex05c). The Ex06c program allows the user to move an ellipse with a mouse by "capturing" the mouse, using a scrolling window with the MM_LOENGLISH mapping mode. Keyboard scrolling is left out, but you can add it by borrowing the OnKeyDown member function from Ex05c.

Instead of a stock brush, we'll use a pattern brush for the ellipse—a real GDI object. There's one complication with pattern brushes, however: You must reset the origin as the window scrolls; otherwise, strips of the pattern won't line up and the effect will be ugly.

As with the Ex05c program, this example involves a view class derived from CScrollView. Here are the steps to create the application:



    Run the MFC Application Wizard to generate the Ex06c project. Make it an SDI application and deselect Printing And Print Preview on the Advanced Features page. Be sure to set the view base class to CScrollView.



    Edit the CEx06cView class header in the file

    Ex06cView.h . Add the following lines in the class CEx06cView declaration:


    private:
    const CSize m_sizeEllipse; // logical
    CPoint m_pointTopLeft; // logical, top left of ellipse rectangle
    CSize m_sizeOffset; // device, from rect top left
    // to capture point
    BOOL m_bCaptured;



    Select the CEx06cView class in Class View, and then use the Properties window to add three message handlers to the CEx06cView class. Add the message handlers as follows:



















    Message


    Member Function


    WM_LBUTTONDOWN


    OnLButtonDown


    WM_LBUTTONUP


    OnLButtonUp


    WM_MOUSEMOVE


    OnMouseMove




    Edit the CEx06cView message handler functions. The code wizards available from the Properties window generated the skeletons for the functions listed in the preceding step. Find the functions in

    Ex06cView.cpp and code them as follows:

    void CEx06cView::OnLButtonDown(UINT nFlags, CPoint point) 
    {
    // still logical
    CRect rectEllipse(m_pointTopLeft, m_sizeEllipse);
    CRgn circle;
    CClientDC dc(this);
    OnPrepareDC(&dc);
    dc.LPtoDP(rectEllipse); // Now it's in device coordinates
    circle.CreateEllipticRgnIndirect(rectEllipse);
    if (circle.PtInRegion(point)) {
    // Capturing the mouse ensures subsequent LButtonUp message
    SetCapture();
    m_bCaptured = TRUE;
    CPoint pointTopLeft(m_pointTopLeft);
    dc.LPtoDP(&pointTopLeft);
    m_sizeOffset = point - pointTopLeft; // device coordinates
    // New mouse cursor is active while mouse is captured
    ::SetCursor(::LoadCursor(NULL, IDC_CROSS));
    }
    }
    void CEx06cView::OnLButtonUp(UINT nFlags, CPoint point)
    {
    if (m_bCaptured) {
    ::ReleaseCapture();
    m_bCaptured = FALSE;
    }
    }
    void CEx06cView::OnMouseMove(UINT nFlags, CPoint point)
    {
    if (m_bCaptured) {
    CClientDC dc(this);
    OnPrepareDC(&dc);
    CRect rectOld(m_pointTopLeft, m_sizeEllipse);
    dc.LPtoDP(rectOld);
    InvalidateRect(rectOld, TRUE);
    m_pointTopLeft = point - m_sizeOffset;
    dc.DPtoLP(&m_pointTopLeft);
    CRect rectNew(m_pointTopLeft, m_sizeEllipse);
    dc.LPtoDP(rectNew);
    InvalidateRect(rectNew, TRUE);
    }

    }




    Edit the CEx06cView constructor, the OnDraw function, and the OnInitialUpdate function. The MFC Application Wizard generated these skeleton functions. Find them in

    Ex06cView.cpp , and code them as follows:

    CEx06cView::CEx06cView() : m_sizeEllipse(100, -100),
    m_pointTopLeft(0, 0),
    m_sizeOffset(0, 0)
    {
    m_bCaptured = FALSE;
    }
    void CEx06cView::OnDraw(CDC* pDC)
    {
    CBrush brushHatch(HS_DIAGCROSS, RGB(255, 0, 0));
    CPoint point(0, 0); // logical (0, 0)
    pDC->LPtoDP(&point); // In device coordinates,
    pDC->SetBrushOrg(point); // align the brush with
    // the window origin
    pDC->SelectObject(&brushHatch);
    pDC->Ellipse(CRect(m_pointTopLeft, m_sizeEllipse));
    pDC->SelectStockObject(BLACK_BRUSH); // Deselect brushHatch
    // Test invalid rect
    pDC->Rectangle(CRect(100, -100, 200, -200));
    }
    void CEx06cView::OnInitialUpdate()
    {
    CScrollView::OnInitialUpdate();
    CSize sizeTotal(800, 1050); // 8-by-10.5 inches
    CSize sizePage(sizeTotal.cx / 2, sizeTotal.cy / 2);
    CSize sizeLine(sizeTotal.cx / 50, sizeTotal.cy / 50);
    SetScrollSizes(MM_LOENGLISH, sizeTotal, sizePage, sizeLine);
    }



    Build and run the Ex06c program. The program allows an ellipse to be dragged with the mouse, and it allows the window to be scrolled through. The program's window should look like the one shown here. As you move the ellipse, observe the black rectangle. You should be able to see the effects of invalidating the rectangle.





The Ex06c Program Elements


Following is a discussion of the important elements in the Ex06c example.

The m_sizeEllipse and m_pointTopLeft Data Members


Rather than store the ellipse's bounding rectangle as a single CRect object, the program separately stores its size ( m_sizeEllipse) and the position of its top left corner ( m_pointTopLeft). To move the ellipse, the program merely recalculates m_pointTopLeft, and any round-off errors in the calculation won't affect the size of the ellipse.


The m_sizeOffset Data Member


When OnMouseMove moves the ellipse, the relative position of the mouse within the ellipse must be the same as it was when the user first pressed the left mouse button. The m_sizeOffset object stores this original offset of the mouse from the top left corner of the ellipse rectangle.


The m_bCaptured Data Member


The m_bCaptured Boolean variable is set to TRUE when mouse tracking is in progress.


The SetCapture and ReleaseCapture Functions


SetCapture is the CWnd member function that "captures" the mouse, such that mouse movement messages are sent to this window even if the mouse cursor is outside the window. An unfortunate side effect of this function is that the ellipse can be moved outside the window and "lost." A desirable and necessary effect is that all subsequent mouse messages are sent to the window, including the WM_LBUTTONUP message, which would otherwise be lost. The Win32 ReleaseCapture function turns off mouse capture.


The SetCursor and LoadCursor Win32 Functions


The MFC library does not "wrap" some Win32 functions. By convention, we use the C++ scope resolution operator (::) when calling Win32 functions directly. In this case, there is no potential for conflict with a CView member function, but you can deliberately choose to call a Win32 function in place of a class member function with the same name. In that case, the :: operator ensures that you call the globally scoped Win32 function.

When the first parameter is NULL, the LoadCursor function creates a cursor resource from the specified predefined mouse cursor that Windows uses. The SetCursor function activates the specified cursor resource. This cursor remains active as long as the mouse is captured.


The CScrollView::OnPrepareDC Member Function


The CView class has a virtual OnPrepareDC function that does nothing. The CScrollView class implements the function for the purpose of setting the view's mapping mode and origin, based on the parameters that you passed to Set- ScrollSizes in OnInitialUpdate. The application framework calls OnPrepareDC for you prior to calling OnDraw, so you don't need to worry about it. You must call OnPrepareDC yourself in any other message handler function that uses the view's device context, such as OnLButtonDown and OnMouseMove.


The OnMouseMove Coordinate Transformation Code


As you can see, this function contains several translation statements. The logic can be summarized by the following steps:



    Construct the previous ellipse rectangle and convert it from logical to device coordinates.



    Invalidate the previous rectangle.



    Update the top left coordinate of the ellipse rectangle.



    Construct the new rectangle and convert it to device coordinates.



    Invalidate the new rectangle.



The function calls InvalidateRect twice. Windows "saves up" the two invalid rectangles and computes a new invalid rectangle that is the union of the two, intersected with the client rectangle.


The OnDraw Function


The SetBrushOrg call is necessary to ensure that all of the ellipse's interior pattern lines up when the user scrolls through the view. The brush is aligned with a reference point, which is at the top left of the logical window, converted to device coordinates. This is a notable exception to the rule that CDC member functions require logical coordinates.




The CScrollView SetScaleToFitSize Mode


The CScrollView class has a stretch-to-fit mode that displays the entire scrollable area in the view window. The Windows MM_ANISOTROPIC mapping mode comes into play, with one restriction: positive y values always increase in the down direction, as in MM_TEXT mode.

To use the stretch-to-fit mode, make the following call in your view's function in place of the call to SetScrollSizes:

SetScaleToFitSize(sizeTotal); 

You can make this call in response to a Shrink To Fit menu command. Thus, the display can toggle between scrolling mode and shrink-to-fit mode.



Using the Logical Twips Mapping Mode in a Scrolling View


The MFC CScrollView class allows you to specify only standard mapping modes. The Ex17a example in Chapter 17 shows a new class, CLogScrollView, that accommodates the logical twips mode.



/ 319