Separating the Behavior from the View Window

[Previous] [Next]

We might want to have any number of window types that can draw to and render from the same data model. To achieve this, we've encapsulated the drawing and message handling specific to the scribble behavior in a separate base class. CScribbleImpl derives from the ATL class CMessageMap and our abstract base class, CObserver. CScribbleImpl takes the derived class name as a template parameter, so it can downcast to get a window handle. We derive CView from CScribbleImpl. CView routes messages to CScribbleImpl by adding a CHAIN_MSG_MAP macro to its message map, as shown here:

 class CView : public CWindowImpl<CView, CWindow,     CViewWinTraits>, public CScribbleImpl<CView> { public:     CView();     virtual ~CView();     DECLARE_WND_CLASS(_T("InAtl:View"))     BEGIN_MSG_MAP(CView)         CHAIN_MSG_MAP(CScribbleImpl<CView>)         MESSAGE_HANDLER(WM_PAINT, OnPaint)     END_MSG_MAP()     LRESULT OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam,         BOOL& bHandled)     {         // Render the point array from the model.         HDC hdc = GetDC();         // Delegate to CScribbleImpl.         DoPaint(hdc);         ReleaseDC(hdc);         bHandled = FALSE;         return 1;     } }; 

CView delegates its painting to CScribbleImpl as well. We could have handled WM_PAINT in CScribbleImpl, but the code is made more portable by allowing the window to prepare its device context before delegating to CScribbleImpl::DoPaint.

To enable CScribbleImpl to get notifications when a point is added or when the data model is modified, we've created an abstract CObserver class and derived CScribbleImpl from it. CObserver has only one function: OnUpdate. The data model calls OnUpdate for every observer when a sketch point is added. CScribbleImpl initiates the update by calling UpdateAllObservers, as you can see in the mouse event handlers:

 LRESULT OnLButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam,     BOOL& bHandled) {     T* pT = static_cast<T*>(this);     ::SetCapture(pT->m_hWnd);     _DataModel.StartStroke();     m_bStroke = TRUE;     m_point.x = LOWORD(lParam);     m_point.y = HIWORD(lParam);     _DataModel.AddPoint(m_point);     _DataModel.UpdateAllObservers(this, CDataModel::UPDATE_ALL);     return 0; } LRESULT OnMouseMove(UINT uMsg, WPARAM wParam, LPARAM lParam,     BOOL& bHandled) {     if(m_bStroke && (wParam & MK_LBUTTON))     {         m_point.x = LOWORD(lParam);         m_point.y = HIWORD(lParam);         _DataModel.AddPoint(m_point);         _DataModel.UpdateAllObservers(this,             CDataModel::UPDATE_ALL);     }     return 0; } LRESULT OnLButtonUP(UINT uMsg, WPARAM wParam, LPARAM lParam,     BOOL& bHandled) {     m_bStroke = FALSE;     ::ReleaseCapture();     return 0; } 

In response to UpdateAllObservers, the data model iterates over any subscribed observers and calls OnUpdate on each one. CScribbleImpl implements OnUpdate by simply invalidating the derived classes window, as shown here:

 void OnUpdate(CSubject* pSubject, CObserver* pSender,     DWORD dwType) {     T* pT = static_cast<T*>(this);     ::InvalidateRect(pT->m_hWnd, NULL, TRUE); } 

When the WM_PAINT message is received by the view, the handler gets a device context and delegates the painting to CScribbleImpl::DoPaint, which reads each stroke from the data model and renders it.



Inside Atl
Inside ATL (Programming Languages/C)
ISBN: 1572318589
EAN: 2147483647
Year: 1998
Pages: 127

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net