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.
// 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.
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.
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;
}