Advanced MFC Programming

Advanced MFC Programming Advanced MFC Programming

math.hcmuns.edu.vn
from math.hcmuns.edu.vn More from this publisher
11.04.2014 Views

Chapter 3. Splitter Window Brush selection can be implemented by calling function CDC::SelectObject(…). This function will return a pointer to the old brush. After using the brush, we can call this function again and pass the old brush to it. This will let the old brush be selected into the DC so the new brush is selected out. When creating a brush, we need to use RGB macro to indicate the brush color. The three parameters of RGB macro indicate the intensity of red, green and blue colors. A rectangle can be drawn by calling function CDC::Rectangle(…). We need to pass a CRect type variable to indicate the position and size of the rectangle. In the sample, function MCSplitterWnd::OnInvertTracker(…)is implemented as follows: void MCSplitterWnd::OnInvertTracker(const CRect& rect) { CDC* pDC; CBrush brush; CBrush *ptrBrushOld; ASSERT_VALID(this); ASSERT(!rect.IsRectEmpty()); ASSERT((GetStyle() & WS_CLIPCHILDREN) == 0); } pDC=GetDC(); brush.CreateSolidBrush(RGB(255, 0, 0)); ptrBrushOld=pDC->SelectObject(&brush); pDC->PatBlt(rect.left, rect.top, rect.Width(), rect.Height(), PATINVERT); pDC->SelectObject(ptrBrushOld); brush.DeleteObject(); ReleaseDC(pDC); There is no CDC type pointer passed to this function. However, for any window, its DC could always be obtained by calling function CWnd::GetDC(). This function will return a pointer to window’s device context. After we use the DC, we must release it by calling function CWnd::ReleaseDC(…). In function MCSplitterWnd::OnDrawSplitter(…), first a solid brush with red color is created, then we select it into the DC, call function CDC::PatBlt(…) to fill the interior of the rectangle using the selected brush. Function CDC::PatBlt(…) allows us to create a pattern on the device. We can choose different color output mode: we can copy the brush color to the destination, or we can combine brush color with the color on the target device using bit-wise operations. The first four parameters of function CDC::PatBlt(…) indicate the position and size of the rectangle within which we can output the pattern. The fifth parameter indicates the output mode. In the sample we use PATINVERT drawing mode, this will combine the destination color and brush color using bit-wise XOR operation. With this mode, the tracker can be easily erased if it is drawn twice. Since we use PATINVERT mode to paint the tracker, its color will become the complement color of red when the user resizes panes using the mouse. 3.5 Splitter Window That Can’t be Resized by Tracking Sometimes we want each pane of the splitter window to have a fixed size and prevent the user from resizing the panes through using mouse or keyboard. Since dynamic resizing is a built-in feature of class CSplitterWnd, whenever we directly derive a class from it, we will automatically have a resizable split bar. It is not easy to disable this feature because by default, mouse clicking and dragging events will be processed automatically. In class CSplitterWnd, four mouse messages are handled to change the state of the split bar: mouse left button down message WM_LBUTTONDOWN, mouse left button up message WM_LBUTTONUP, mouse move message WM_MOUSEMOVE, and left button double click message WM_LBUTTONDBLCLK. We need to disable only the first message handler if we want to disable tracking resize feature (Once the application cannot enter the tracking state, the rest messages will be processed normally instead of being treated as part of tracking instructions). By default, when left button is clicked on a split bar, class CSplitterWnd will respond to this event by letting the user drag the split bar and place it to a new place. We can bypass this feature by overriding 66

Chapter 3. Splitter Window WM_LBUTTONDOWN message handler. Instead of calling the message handler implemented by CSplitterWnd, we can call the default function implemented by class CWnd, which is the base class of CSplitterWnd. For a dynamic splitter window, the WM_LBUTTONDBLCLK message handler should not be overridden because after we disable the tracking, double clicking becomes the only way that can be used by the user to dynamically add or delete panes. For WM_MOUSEMOVE and WM_LBUTTONUP messages, we don’t need to modify their handlers because after message WM_LBUTTONDOWN is bypassed, the tracking will not happen anymore. Sample 3.5\Spw demonstrates how to implement splitter window that cannot be resized through tracking the split bar. It is based on sample 3.4\Spw. To let class MCSplitterWnd support both resizable and non-resizable split bars, a new Boolean type variable m_bResizable is declared in the class. Along with this variable a new member function MCSplitterWnd::SetResizable(…) is also declared, which can be called to set m_bResizable flag and indicate if tracking resize feature is currently supported. At the beginning variable MCSplitterWnd::m_bResizable is initialized to TRUE in the constructor. The following code fragment shows the modified class: class MCSplitterWnd : public CSplitterWnd { DECLARE_DYNCREATE(MCSplitterWnd) public: MCSplitterWnd(); void SetResizable(BOOL bResizable){m_bResizable=bResizable;} virtual void DeleteRow(int); virtual void DeleteColumn(int); …… protected: BOOL m_bResizable; } A WM_LBUTTONDOWN message handler is added to the application. This includes function declaration, adding ON_WM_LBUTTONDOWN message mapping macro, and the implementation of member function. Before adding message mapping, we need to make sure that DECLARE_MESSAGE_MAP macro is included in the class. This will enable massage mapping for the class. The following lists necessary steps for implementing the above message mapping: 1) Declare an afx_msg type member function OnLButtonDown(…) in the class. This function is originally declared in class CWnd, here we must declare it again in order to override it: …… …… class MCSplitterWnd : public CSplitterWnd { protected: }; afx_msg void OnLButtonDown(UINT, CPoint); DECLARE_MESSAGE_MAP() 2) In the implementation file, add ON_WM_LBUTTONDOWN macro between BEGIN_MESSAGE_MAP and END_MESSAGE_MAP macros: BEGIN_MESSAGE_MAP(MCSplitterWnd, CSplitterWnd) //{{AFX_MSG_MAP(MCSplitterWnd) //}}AFX_MSG_MAP ON_WM_LBUTTONDOWN() END_MESSAGE_MAP() Macro ON_WM_LBUTTONDOWN maps message WM_LBUTTONDOWN to function OnLButtonDown(…). 3) Implement the message handler as follows: void MCSplitterWnd::OnLButtonDown(UINT uFlags, CPoint point) { if(m_bResizable == TRUE)CSplitterWnd::OnLButtonDown(uFlags, point); 67

Chapter 3. Splitter Window<br />

Brush selection can be implemented by calling function CDC::SelectObject(…). This function will<br />

return a pointer to the old brush. After using the brush, we can call this function again and pass the old<br />

brush to it. This will let the old brush be selected into the DC so the new brush is selected out.<br />

When creating a brush, we need to use RGB macro to indicate the brush color. The three parameters of<br />

RGB macro indicate the intensity of red, green and blue colors.<br />

A rectangle can be drawn by calling function CDC::Rectangle(…). We need to pass a CRect type<br />

variable to indicate the position and size of the rectangle.<br />

In the sample, function MCSplitterWnd::OnInvertTracker(…)is implemented as follows:<br />

void MCSplitterWnd::OnInvertTracker(const CRect& rect)<br />

{<br />

CDC* pDC;<br />

CBrush brush;<br />

CBrush *ptrBrushOld;<br />

ASSERT_VALID(this);<br />

ASSERT(!rect.IsRectEmpty());<br />

ASSERT((GetStyle() & WS_CLIPCHILDREN) == 0);<br />

}<br />

pDC=GetDC();<br />

brush.CreateSolidBrush(RGB(255, 0, 0));<br />

ptrBrushOld=pDC->SelectObject(&brush);<br />

pDC->PatBlt(rect.left, rect.top, rect.Width(), rect.Height(), PATINVERT);<br />

pDC->SelectObject(ptrBrushOld);<br />

brush.DeleteObject();<br />

ReleaseDC(pDC);<br />

There is no CDC type pointer passed to this function. However, for any window, its DC could always be<br />

obtained by calling function CWnd::GetDC(). This function will return a pointer to window’s device<br />

context. After we use the DC, we must release it by calling function CWnd::ReleaseDC(…). In function<br />

MCSplitterWnd::OnDrawSplitter(…), first a solid brush with red color is created, then we select it into the<br />

DC, call function CDC::PatBlt(…) to fill the interior of the rectangle using the selected brush.<br />

Function CDC::PatBlt(…) allows us to create a pattern on the device. We can choose different color<br />

output mode: we can copy the brush color to the destination, or we can combine brush color with the color<br />

on the target device using bit-wise operations. The first four parameters of function CDC::PatBlt(…)<br />

indicate the position and size of the rectangle within which we can output the pattern. The fifth parameter<br />

indicates the output mode. In the sample we use PATINVERT drawing mode, this will combine the<br />

destination color and brush color using bit-wise XOR operation. With this mode, the tracker can be easily<br />

erased if it is drawn twice.<br />

Since we use PATINVERT mode to paint the tracker, its color will become the complement color of red<br />

when the user resizes panes using the mouse.<br />

3.5 Splitter Window That Can’t be Resized by Tracking<br />

Sometimes we want each pane of the splitter window to have a fixed size and prevent the user from<br />

resizing the panes through using mouse or keyboard. Since dynamic resizing is a built-in feature of class<br />

CSplitterWnd, whenever we directly derive a class from it, we will automatically have a resizable split bar.<br />

It is not easy to disable this feature because by default, mouse clicking and dragging events will be<br />

processed automatically.<br />

In class CSplitterWnd, four mouse messages are handled to change the state of the split bar: mouse<br />

left button down message WM_LBUTTONDOWN, mouse left button up message WM_LBUTTONUP, mouse move<br />

message WM_MOUSEMOVE, and left button double click message WM_LBUTTONDBLCLK. We need to disable only<br />

the first message handler if we want to disable tracking resize feature (Once the application cannot enter the<br />

tracking state, the rest messages will be processed normally instead of being treated as part of tracking<br />

instructions).<br />

By default, when left button is clicked on a split bar, class CSplitterWnd will respond to this event by<br />

letting the user drag the split bar and place it to a new place. We can bypass this feature by overriding<br />

66

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!