I have cells containing several choices. I don’t want to display them in a combobox. Is there a way that the user is able to select items from the choice-list by clicking on spin-buttons?
Yes, you can derive a class from CGXSpinEdit and display the choice-list entry associated with the zero-based index value. You need to override GetControlText and GetValue.
If you implement the following class CSpinListControl:
// header (.h) file
class CSpinListControl: public CGXSpinEdit
{
DECLARE_CONTROL(CSpinListControl)
public:
// Constructor & Destructor
CSpinListControl(CGXGridCore* pGrid, UINT nID);
// Overrides
BOOL GetControlText(CString& strResult, ROWCOL nRow, ROWCOL nCol, LPCTSTR pszRawValue, const CGXStyle& style);
BOOL GetValue(CString& sResult);
void OnClickedButton(CGXChild* pChild);
void OnInitChildren(ROWCOL nRow, ROWCOL nCol, const CRect& rect);
void Draw(CDC* pDC, CRect rect, ROWCOL nRow, ROWCOL nCol, const CGXStyle& style, const CGXStyle* pStandardStyle);
// Attributes
BOOL m_bUseIndexValue; // TRUE if control should store zero-based index as value;
// FALSE if control should store choice list entry as value.
// Generated message map functions
protected:
//{{AFX_MSG(CSpinListControl)
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
// implementation (.cpp) file
//////////////////////////////////////////////////////////////////
// CSpinListControl control
IMPLEMENT_CONTROL(CSpinListControl, CGXSpinEdit)
CSpinListControl::CSpinListControl(CGXGridCore* pGrid, UINT nID)
: CGXSpinEdit(pGrid, nID)
{
m_bUseIndexValue = FALSE;
// TRUE if control should store zero-based index as value;
// FALSE if control should store choice list entry as value.
}
BEGIN_MESSAGE_MAP(CSpinListControl, CGXSpinEdit)
//{{AFX_MSG_MAP(CSpinListControl)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
// GetControlText
//
// Convert the value which is stored in the style object into
// the text which should be displayed in the cell.
//
BOOL CSpinListControl::GetValue(CString& sResult)
{
if (!CGXSpinEdit::GetValue(sResult))
return FALSE;
if (m_bUseIndexValue && m_pStyle && m_pStyle->GetIncludeChoiceList())
{
CString sMatch = sResult;
// finds exact string or the first string with the same prefix
int nIndex = FindStringInChoiceList(sMatch, sResult,
m_pStyle->GetChoiceListRef(), FALSE);
if (nIndex != -1)
{
wsprintf(sResult.GetBuffer(20), _T("%d"), nIndex);
sResult.ReleaseBuffer();
}
else
sResult.Empty();
}
return TRUE;
}
BOOL CSpinListControl::GetControlText(CString& strResult, ROWCOL nRow, ROWCOL nCol, LPCTSTR pszRawValue, const CGXStyle& style)
{
CString sItem;
if (m_bUseIndexValue)
{
// determine index
short nIndex = -1;
if (pszRawValue && _tcslen(pszRawValue) > 0)
nIndex = (short) _ttoi(pszRawValue);
else if (style.GetIncludeValue() && _tcslen(style.GetValueRef()) > 0)
nIndex = style.GetShortValue();
if (nIndex != -1)
GetChoiceListItem(sItem, style.GetChoiceListRef(), nIndex);
// base class version will format the text as specified in mask
pszRawValue = sItem;
}
// Now, we can format this entry.
return CGXSpinEdit::GetControlText(strResult, nRow, nCol, pszRawValue, style);
}
void CSpinListControl::OnClickedButton(CGXChild* pChild)
{
BOOL bActive = IsActive();
if (!bActive && !OnStartEditing())
return;
SetActive(TRUE);
NeedStyle();
CString strText;
GetValue(strText);
// empty cell, when user pressed alpahnumeric key
if (!IsReadOnly() || !m_pStyle->GetIncludeChoiceList())
{
LONG lValue ;
// style
TCHAR sz[20];
if ( m_bStartValue && strText.IsEmpty() )
lValue = m_nStartValue ;
else
{
// Non-Null cell
if (m_bUseIndexValue)
lValue = _ttol(strText);
else
{
CString sMatch = strText;
// finds exact string or the first string with the same prefix
lValue = FindStringInChoiceList(sMatch, strText,
m_pStyle->GetChoiceListRef(), FALSE);
}
if (pChild == m_pUpArrow)
{
if (!m_bMaxBound || lValue < m_nMaxBound)
lValue++;
else if (m_bWrap && m_bMinBound)
lValue = m_nMinBound;
}
else
{
if (!m_bMinBound || lValue > m_nMinBound)
lValue--;
else if (m_bWrap && m_bMaxBound)
lValue = m_nMaxBound;
}
}
if (m_bMinBound)
lValue = max(m_nMinBound, lValue);
if (m_bMaxBound)
lValue = min(m_nMaxBound, lValue);
if (m_bUseIndexValue)
{
wsprintf(sz, _T("%ld"), lValue);
SetValue(sz);
}
else
{
CString sItem;
GetChoiceListItem(sItem, m_pStyle->GetChoiceListRef(), lValue);
SetValue(sItem);
}
SetModify(TRUE);
OnModifyCell();
SetSel(0, -1);
}
// eventually destroys and creates CEdit with appropriate window style
if (!bActive)
Refresh();
else
UpdateEditStyle();
CGXControl::OnClickedButton(pChild);
}
void CSpinListControl::OnInitChildren(ROWCOL nRow, ROWCOL nCol, const CRect& rect)
{
nRow, nCol;
const int nEditBtnWidth = 13;
// CRect rect = CGXControl::GetCellRect(nRow, nCol, (LPRECT) &r, m_pStyle);
// init arrow buttons
CRect rectBtn;
rectBtn.IntersectRect(rect,
CRect(rect.right-2-nEditBtnWidth,
rect.top, rect.right-1, rect.bottom-1)
);
m_pUpArrow->SetRect(
CRect(rectBtn.left, rectBtn.top,
rectBtn.right, rectBtn.top+rectBtn.Height()/2)
);
m_pDownArrow->SetRect(
CRect(rectBtn.left, rectBtn.top+rectBtn.Height()/2,
rectBtn.right, rectBtn.bottom)
);
}
// override the Draw method only if you want that the spin-buttons
// are also visible for inactive cells.
void CSpinListControl::Draw(CDC* pDC, CRect rect, ROWCOL nRow, ROWCOL nCol, const CGXStyle& style, const CGXStyle* pStandardStyle)
{
pStandardStyle, style;
CGXSpinEdit::Draw(pDC, rect, nRow, nCol, style, pStandardStyle);
// Normal, spin-buttons are not drawn for inactive cells.
// The following code forces that they will be drawn.
if (!Grid()->IsCurrentCell(nRow, nCol))
{
for (int i = 0; i < GetCount(); i++)
{
CGXChild* pChild;
if ((pChild = GetChild(i)) != NULL)
pChild->Draw(pDC, !Grid()->IsPrinting());
}
}
}
You can register the control and user attribute in the OnInitialUpdate() routine of your grid class
// Register all controls and user attributes for the view
// IDS_CTRL_SPINLIST is a string resource
// you have to add to your application.
RegisterControl(IDS_CTRL_SPINLIST, new CSpinListControl(this, IDS_CTRL_SPINLIST));
and later apply it to cells with
SetStyleRange(CGXRange(4,4,10,4),
CGXStyle()
.SetControl(IDS_CTRL_SPINLIST)
.SetUserAttribute(GX_IDS_UA_SPINBOUND_MIN, "0") // index of first choice
.SetUserAttribute(GX_IDS_UA_SPINBOUND_MAX, "4") // index of last choice
.SetUserAttribute(GX_IDS_UA_SPINBOUND_WRAP, "1") // allow wrap value
.SetUserAttribute(GX_IDS_UA_SPINSTART, "0") // value when clicked in empty cell
.SetChoiceList("one\ntwo\nthree\nfour\nfive")
);
You can determine the index of the selected choice in the cell with
int nIndex = _ttoi(GetValueRowCol(nRow, nCol));