I developed an application that includes the display and usage of a data entry screen. Data entry "forms" are read in from a text stream. Embedded in the screen are field codes that tell the application where in the parameter buffer to fill in parameter values. Status values translated from a status buffer can also be on the data entry screen for informational purposes. The status and parameter fields are color coded. When the user passes the mouse over an editable field, the field inverts. The editable fields are either strings or values that "toggle", such as yes/no choices. If the clicks on an editable field a dialog box would appear for string input if the field was a string type, or just invert its value if it was a binary "toggle" type.
Here's some abbreviated source code for inverting an editable field:
void CParStatView::OnMouseMove(UINT nFlags, CPoint point)
{
// ...
for (int i = 0; i < m_arrEditables.GetSize(); i++) {
pe = (CEditable *)m_arrEditables.GetAt(i);
if (pe->Extents().PtInRect(point) &&
pe->IsParameter()) {
if (pe->GetDisplayState() ==
CEditable::ShowNormal) {
InvertRect(pe->Extents());
}
}
else {
if (pe->GetDisplayState() ==
CEditable::ShowHighlighted) {
InvertRect(pe->Extents());
}
}
}
}
And abbreviated code for editing an editable field:
void CParStatView::OnLButtonDown(UINT nFlags, CPoint point)
{
//...
for (int i = 0; i < m_arrEditables.GetSize(); i++) {
pe = (CEditable *)m_arrEditables.GetAt(i);
if (pe->Extents().PtInRect(point) &&
pe->IsParameter()) {
if (pe->IsBinaryParameter()) {
// Read parameter from buffer,
invert it, write parameter
// to buffer
//...
}
else {
// Read string parameter from
buffer, edit it in
// dialog box, write string
to buffer
// ...
}
}
}
}
That was fine for that version of the application. The new version, however, has extra requirements, including:
"If" or "case" statements would run rampant throughout the code unless the Strategy pattern is used.
The CEditable class is the context of the strategy.
The CEditor class is the abstract class that defines all the functions needed
by CEditable:
class CEditor : public CObject
{
public:
// ...
virtual void Highlight(CView *pView, CEditable *ppse) = 0;
virtual BOOL Edit(CView *pView, CEditorValue *pev,
CPSEditable *ppse) = 0;
// ... other functions, including ContextMenu
};
The CEditorValue class mentioned is the "return" for the Edit function, holding
the new string and/or flags.
The CStatusEditor class implements the "focus rectangle" for Highlighting,
and just immediately returns FALSE for editing.
void CStatusEditor::Highlight(CView *pView, CEditable *pe)
{
CClientDC dc(pView);
dc.DrawFocusRect(pe->Extents());
}
BOOL CStatusEditor::Edit(CView *pView, CEditorValue *pev, CEditable *pe)
{
return FALSE; // status never edits
}
The CParameterEditor class inverts the rectangle, and brings up the dialog for editing.
void CParameterEditor::Highlight(CView *pView, CEditable *pe)
{
CClientDC dc(pView);
dc.InvertRect(pe->Extents());
}
BOOL CParameterEditor::Edit(CView *pView, CEditorValue *pev,
CEditable *pe)
{
CTextEditorDlg dlg(pView);
int iDlgRet;
dlg.m_strEdit = ppse->GetText();
iDlgRet = dlg.DoModal();
if (dlg.doModal() == IDOK) {
pev->m_dwFlags = CEditor::FlagText;
pev->m_strVal = dlg.m_strEdit;
return TRUE;
}
else {
return FALSE;
}
}
The CBinaryParameterEditor class is subclassed from CParameterEditor, differing only in the Edit Function.
BOOL CBinaryParameterEditor::Edit(CView *pView, CEditorValue *pev,
CEditable *pe)
{
pev->m_dwFlags = CEditor::FlagBinary; // indicator to just
toggle the current value
return TRUE;
}
The main advantage of using the Strategy pattern in this case is that the code to choose an algorithm, containing the multiple "if"'s or switch statement is limited to one place - where CEditables are created and initialized.
//...
CEditor *peditor;
CEditable *pe;
pe = new CEditable();
//...
if (pe->GetType() == STATUS)
peditor = new CStatusEditor();
else if (pe->GetType() == BINARYPARAMETER)
peditor = new CBinaryParameterEditor();
else
peditor = new CParameterEditor();
pe->SetEditor(peditor);
//...
The original functions now can do more but have less code.
void CParStatView::OnMouseMove(UINT nFlags, CPoint point)
{
// ...
for (int i = 0; i < m_arrEditables.GetSize(); i++) {
pe = (CEditable *)m_arrEditables.GetAt(i);
if (pe->Extents().PtInRect(point)) {
if (pe->GetDisplayState() ==
CEditable::ShowNormal) {
pe->Highlight(); // DrawFocusRect
for status,
// InvertRect for parameters
}
}
else {
if (pe->GetDisplayState() ==
CEditable::ShowHighlighted) {
pe->Highlight();
}
}
}
}
void CParStatView::OnLButtonDown(UINT nFlags, CPoint point)
{
//...
CEditorValue ev;
for (int i = 0; i < m_arrEditables.GetSize(); i++) {
pe = (CEditable *)m_arrEditables.GetAt(i);
if (pe->Extents().PtInRect(point)) {
if (pe->Edit(this, &ev)) {
// process the flags and values
returned
// ...
}
}
}
}