The View Code

This is the interface of the View Class. It's a big bugger, but then you'd expect that since it's the thing that does most of the work. Once the Controller has received a message from the WinOS that something has happened, it passes that info onto the View which has to do everything else.

class CTView
{
public:
    CRect pageSize;                 // printed  area
    CRect tableSize;                // scrollable area
    CRect frameSize;                // the graph region
    CFRect frameExtent;  
    CRect viewPortSize;             // the viewable area
    float scale;                    // the scale factor for screen viewing
    int tableWidth, tableHeight;    // table dimensions for scrolling               
    CTModel* pmodel;                 // pointer to the graph instance
    float mx, my, cx, cy;           // factors for graph to page conversions 
    int scrollX, scrollY;           // scroll positions 
    CPoint start, old;              // points for resizing the axes
    BOOL mouseDraw;                 // is the mouse clicked?
    
        
    BOOL isXExtentAuto;               // should frame's X extent be automatically set
    BOOL isYExtentAuto;               // should frame's Y extent be automatically set

public:
    CTView();                    // CTView constructor
    ~CTView();                   // CTView destructor
    void SetScale(void);        // calculate the conversion factors for graph to page conversions 
    int px(float);              // convert x values from graph to page coordinates
    int py(float);              // convert y values from frame to page coordinates    
    CRect pR(CFRect);           // convert rectangle from frame to tpage coordinates
    float InvPx(int);           // convert x values from page to frame coordinates
    float InvPy(int);           // convert y values from page to frame coordinates
    CFRect InvPR(CRect);        // convert rectangle from page to frame coordinates
    CPoint table2Port(CPoint);  // convert point from table to viewport coordinates
    CRect table2Port(CRect);    // convert rectangle from table to viewport coordinates
    CPoint port2Table(CPoint);  // convert point from viewport to table coordinates
    CRect port2Table(CRect);    // convert rectangle from viewport to table coordinates
    CPoint page2Port(CPoint);   // convert point from page to viewport coordinates
    CRect page2Port(CRect);     // convert rectangle from page to viewport coordinates
    CPoint port2Page(CPoint);   // convert point from viewport to page coordinates
    CRect port2Page(CRect);     // convert rectangle from viewport to page coordinates
    void Border(CDC* pdc, CRect r); // draw a transparent rectangle
    void DrawTable(CPaintDC*);      // draw graph and page frames
    void OnLButtonDown (CClientDC*, CPoint);    // implement a mouse click and drag
    void OnLButtonUp (CClientDC*, CPoint);      //     "     "   "     "    "    "
    void OnMouseMove (CClientDC*, CPoint);      //     "     "   "     "    "    "
    void OnHScroll(UINT, UINT, int&);     // implement a horizontal scroll
    void OnVScroll(UINT, UINT, int&);     // implement a vertical scroll
    void SetExtent();             // reset the frame extent
}; 
An awful lot of the code deals with shifting the scales between the three drawing areas and as such is pretty standard C++ rather than anything Windows dependent.

There are four areas to think about. The Table, Page and Frame (which all sit there ready to be drawn into) and then the Port, which is the area of the viewing Window and shifts around these looking in.

The tableSize, pageSize and frameSize all then refer to the 'physical' postion of these in relation to each other, whilst the frameExtent is the 'virtual' size of the frame. On the screen the frameSize may be 100mm to 200mm, but it's extent may be anything, 0 to 1, 0 to 1000, -1 to 1 or even 1 to -1. The scaling takes care of all this relatively automatically and lets you plot to where you want on the page.

The functions such as page2Port() and port2Page() handle these conversions with px(), pR(), InvPy() etc handling switches between Frame and Page.

To actually draw or write onto the screen requires the use of the DC. For example, if pdc is a pointer to the DC, then:

  CRect r(100, 100, 200, 200);
  pdc->Rectangle (r);
will draw the rectangle on the screen. There are many other commands that do other related jobs to draw lines, circles, write text, change the line colour, change the font etc. You name it and there's probably half a dozen ways to do it, although, this being an MS product, none of them will do quite what you want in the way that you you would expect them to.

As a little added bonus, this Template also includes an implementation of a mouse click and drag, along with some rubber-banding. The difficulty with this is that there is always the need to keep tabs on which coordinates you are on right now which means switching between Device and Logical coordinates using the DPtoLP() and LPtoDP() functions.

For example, the OnLButtonUp() function occurs at the end of a click and drag event. The position in device coordinates is passed from the Controller to the function itself, which then has to straight away convert them to Logical coordinates and check if the point is within the required region before erasing the previously drawn rubber-banded rectangle. It then needs to convert the point to the correct coordinate system before reseting the frameExtent (to rescale the axis to the area the mouse has defined).

void CTView::OnLButtonUp (CClientDC* pdc, CPoint mousePos)
{
    pdc->DPtoLP(&mousePos, 1);
    if (page2Port(frameSize).PtInRect(mousePos) && mouseDraw)
    {
        
        // this sets up the pen for rubber banding
        CPen pen(PS_DOT, 1, RGB(155,155,155)), *oldPen;
        oldPen = pdc->SelectObject(&pen);
        pdc->SetROP2(R2_NOTXORPEN);
        
        CRect r = CRect(start.x, start.y, old.x, old.y);
    
        // this draws a dashed rectangle around the bounded area
        Border(pdc, r);

        pdc->SelectObject(oldPen);

        CRect rr = port2Page(r);
        rr.NormalizeRect();
        CFRect g = InvPR(rr);
        CFRect g1 = frameExtent;
        frameExtent = InvPR(rr);
        mouseDraw = FALSE;
    }
}
This kind of switching between coordinate systems is a large part of what the View has to do when handling input from the mouse. A real App might also not redraw the whole Window everytime, but check to see which parts had been exposed and only redraw those in order to save time. This again would need a lot of coordinate switching as the info about which areas were covered or exposed wouls be passed in Device coordinates and would need to be converted to Logical.

return to WinApp Howto

Hosted by www.Geocities.ws

1