The CSliderCtrl Program
This section examines the tutorial code in detail.
The Declaration
 
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.
Activating and Deactivating the Control
 
// 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.
 
// Mouse hit
BOOL CGXSliderCtrl::LButtonUp(UINT nFlags, CPoint pt,
UINT nHitState)
{
// Unreferenced:
nFlags, pt, nHitState;
 
SetActive(TRUE); //1
Refresh(); //2
 
CRect r = GetCellRect(m_nRow, m_nCol);
pt.x -= r.left;
pt.y -= r.top;
 
PostMessage(WM_LBUTTONDOWN, MK_LBUTTON, MAKELONG(pt.x, pt.y));//3
PostMessage(WM_LBUTTONUP, MK_LBUTTON, MAKELONG(pt.x, pt.y));
 
// check child buttons
CGXControl::LButtonUp(nFlags, pt, nHitState);
 
return TRUE;
}
 
BOOL CGXSliderCtrl::Store()
{
// Calls SetStyleRange() and resets the modify flag
ASSERT(m_pStyle);
 
CString sValue;
if (m_pStyle && GetModify() && GetValue(sValue))
{
SetActive(FALSE);
SetModify(FALSE);
 
return Grid()->SetValueRange(
CGXRange(m_nRow, m_nCol),
sValue,
gxOverride,
0, GX_INVALIDATE);
// Cell will be automatically redrawn inactive
}
 
return TRUE;
}
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.
Drawing the Active and Inactive State
 
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.