This section examines the tutorial code in detail.
class CGXSliderCtrl : public CGXControl, public CSliderCtrl { GRID_DECLARE_CONTROL(CGXSliderCtrl) public: BOOL m_bIsActive; BOOL m_bModify; // Constructor and destructor CGXSliderCtrl(CGXGridCore* pGrid); virtual ~CGXSliderCtrl(); BOOL CreateControl(int nID); virtual BOOL Store(); //Operations virtual void Draw(CDC* pDC, CRect rect, ROWCOL nRow, ROWCOL nCol, const CGXStyle& style, const CGXStyle* pStandardStyle); virtual void Init(ROWCOL nRow, ROWCOL nCol); virtual void SetValue(LPCTSTR pszRawValue); virtual BOOL GetValue(CString& strResult); // virtual BOOL GetControlText(CString& strResult, ROWCOL nRow, ROWCOL nCol, LPCTSTR pszRawValue, const CGXStyle& style); virtual BOOL GetModify(); virtual void SetModify(BOOL bModified); // Status virtual void SetActive(BOOL bActive); virtual BOOL IsActive(); virtual void Hide(); // Mouse hit virtual BOOL LButtonUp(UINT nFlags, CPoint pt, UINT nHitState); protected: CSliderCtrl* m_pStaticWnd; // For drawing static cells // Generated message map functions protected: //{{AFX_MSG(CGXSliderCtrl) //}}AFX_MSG GRID_DECLARE_MESSAGE_MAP() }; |
As stated earlier, the grid control CGXSliderCtrl derives multiply from CGXControl, the abstract interface that the grid understands, and the CSliderCtrl. There is a second instance of CSliderCtrl as this class's member m_pStaticWnd. The reason we have two instances of CSliderCtrl is to aid control sharing among different cells. While, the multiply inherited control is always used to draw an active cell, the second instance, m_pStaticWnd is used to draw all the inactive cells. The CGXSliderCtrl is created using the construct and create paradigm, similar to the CSliderCtrl. We create both the inherited CSliderCtrl and the member CSliderCtrl in the CreateControl() function.
We will examine the rest of the functions with their implementation.
// Operations void CGXSliderCtrl::Init(ROWCOL nRow, ROWCOL nCol) { ASSERT(::IsWindow(m_hWnd)); // ASSERTION-> Did you forget to call Create? ->END // Stores the cell coordinates, resets the style and // sets the window text CGXControl::Init(nRow, nCol); SetActive(FALSE); // SetValue will set the window text for the current cell NeedStyle(); SetValue(m_pStyle->GetValueRef()); //1 SetModify(FALSE); } void CGXSliderCtrl::SetValue(LPCTSTR pszRawValue) { int nPos = _ttoi(pszRawValue); if( nPos < 0) nPos = 50; SetPos(nPos); OnModifyCell(); } |
Init() is a virtual function in the CGXControl class that will be called every time a cell becomes a current cell so that you can initialize the intrinsic state of the control before it becomes active.
//1 | initializes the intrinsic state for this control.
|
LbuttonUp() is another virtual in CGXControl that will be called by the grid whenever the user clicks on an inactive cell.
//1 | Changes the state of the control to be active. |
//2 | Invalidates the cell rectangle and forces the cell to be drawn in the new active state. |
//3 | Posting the button down and up messages to the active control ensures immediate response from the active control. |
Note that the active state is something you should maintain in your derived control. The member m_bActive and the virtual overrides SetActive() and GetActive() take care of this.
BOOL CGXSliderCtrl::Store() { // Calls SetStyleRange() and resets the modify flag ASSERT(m_pStyle); CString sValue; if (m_pStyle && GetModify() && GetValue(sValue)) //1 { SetActive(FALSE); //2 SetModify(FALSE); return Grid()->SetValueRange( CGXRange(m_nRow, m_nCol), sValue, gxOverride, 0, GX_INVALIDATE); //3 // Cell will be automatically redrawn inactive } return TRUE; } |
Store() is another CGXControl virtual function that will be called by the grid when the changes in the cell made by the user has to be stored in response to a user action. Note that you will have to maintain the dirty state of the control in the derived class. The member m_bModify and the virtuals GetModify() and SetModify() take care of this.
//1 | Check if the cell is dirty; if it is, get the current position of the slider as a string to be stored as the value. GetValue() performs the conversion to a string. |
//2 | Clear the active state and the dirty flag. |
//3 | Store the new value into the grid using SetValueRange(). Note that you could instead use SetStyleRange() to make changes in other attributes of the cell's style. |
void CGXSliderCtrl::Draw(CDC* pDC, CRect rect, ROWCOL nRow, ROWCOL nCol, const CGXStyle& style, const CGXStyle* pStandardStyle) { ASSERT(::IsWindow(m_hWnd)); // ASSERTION-> Did you forget to call Create? ->END ASSERT(pDC != NULL && pDC->IsKindOf(RUNTIME_CLASS(CDC))); // ASSERTION-> Invalid Device Context ->END ASSERT(nRow <= Grid()->GetRowCount() && nCol <= Grid()->GetColCount()); // ASSERTION-> Cell coordinates out of range ->END ASSERT_VALID(pDC); // Draw the decorated window // Erase Frame around the cell DrawFrame(pDC, rect, style); // Font rect = GetCellRect(nRow, nCol, &rect, &style); CSliderCtrl* pWnd; CFont font; if (Grid()->IsCurrentCell(nRow, nCol) && nRow == m_nRow && nCol == m_nCol) { // current cell - pWnd = this; int nPosMin = style.GetUserAttribute(GX_IDA_SLIDER_MIN).GetLongValue(); int nPosMax = style.GetUserAttribute(GX_IDA_SLIDER_MAX).GetLongValue(); pWnd->SetRange(nPosMin, nPosMax); // 2 } else { // any other cell - use m_pStaticWnd // use static window and set format and initialize // window with cell settings pWnd = m_pStaticWnd; int nPosMin = style.GetUserAttribute(GX_IDA_SLIDER_MIN).GetLongValue(); int nPosMax = style.GetUserAttribute(GX_IDA_SLIDER_MAX).GetLongValue(); pWnd->SetRange(nPosMin, nPosMax); if(style.GetIncludeValue()) { int nPos = _ttoi(style.GetValue()); if(nPos < 0) nPos = 50; pWnd->SetPos(nPos); } } pWnd->MoveWindow(rect, FALSE); //3 pWnd->Invalidate(); pWnd->ShowWindow(SW_SHOW); if (nRow > Grid()->GetFrozenRows() && (Grid()->GetTopRow() > nRow || nCol > Grid()->GetFrozenCols() && Grid()->GetLeftCol() > nCol)) // Ensure that the window cannot draw outside the // clipping area! { CRect rectClip; if (pDC->GetClipBox(&rectClip) != ERROR) { CRect r = rect & Grid()->GetGridRect(); GridWnd()->ClientToScreen(&r); pWnd->ScreenToClient(&r); GridWnd()->ClientToScreen(&rectClip); pWnd->ScreenToClient(&rectClip); pWnd->ValidateRect(r); pWnd->InvalidateRect(rectClip); } } pWnd->UpdateWindow(); // 4 // if (nRow == m_nRow && nCol == m_nCol && m_bIsActive) // initialize CWnd, make it visible and set focus if (nRow == m_nRow && nCol == m_nCol && IsActive() && !Grid()->IsPrinting() && !(Grid()->GetTopRow() > nRow || Grid()->GetLeftCol() > nCol)) { // 5 Grid()->SetIgnoreFocus(TRUE); if (pWnd->GetFocus() == GridWnd()) pWnd->SetFocus(); Grid()->SetIgnoreFocus(FALSE); SetModify(TRUE); } else { pWnd->SetWindowPos(0,32000,32000,0,0, SWP_NOSENDCHANGING|SWP_NOCOPYBITS|SWP_NOACTIVATE|SWP_NOREDRAW);//6 pWnd->ShowWindow(SW_HIDE); GridWnd()->ValidateRect(rect); } // Avoid overdrawing with BitmapDC in CGXGridCore::OnDraw ExcludeClipRect(rect); // Unreferenced: pStandardStyle; } |
//1 | If this was called for the active cell, initialize the range for the active cell. |
//2 | If this was called for the inactive cell, initialize the range and position of the inactive cell. |
//3 | Move the corresponding control to this rectangle, invalidate it, and show it. |
//4 | Update the window so that it will be redrawn with its new settings. |
//5 | If called for the active state, set the focus to the active control. |
//6 | If called for the inactive state, hide the static control and validate the rectangle so that the grid will not try to redraw it. |
Copyright © Rogue Wave Software, Inc. All Rights Reserved.
The Rogue Wave name and logo, and Stingray, are registered trademarks of Rogue Wave Software. All other trademarks are the property of their respective owners.
Provide feedback to Rogue Wave about its documentation.