| | | | Browse by category |
Problem
I would like to add some application-specific data to the grid in a per cell or per column/row basis. (CGXStyle::GetItemDataPtr()/SetItemDataPtr() is not sufficient)
Cause
Action
There are several ways of doing this and its up to you and your exact situation to decide which approach to take. Your options are:
1. Define a unique integer id or string resource id, e.g.
#define IDS_ATTR_USERATTR 1 |
and later use this user attribute:
style.SetUserAttribute(IDS_ATTR_USERATTR, "My Row Value"); |
In your derived control class you can call style.GetUserAttribute to determine the setting of the attribute. If you want the user attribute to appear in the CGXStyleSheet make IDS_ATTR_USERATTR a string resource id and register the attribute with:
GetParam()->GetStylesMap()->AddUserAttribute(IDS_ATTR_USERATTR); |
2. Store something in the choice list or any other attribute you don't need for the specific control.
For example column 0 is normally a header and ignores the choice list, so you could easily store a value there.
SetStyleRange(CGXRange(nRow, 0), CGXStyle().SetChoiceList("My Row Value")); |
3. Subclass CGXAbstractUserAttribute
By deriving from CGXAbstractUserAttribute you can store binary data into any CGXStyle object. Using a CGXAbstractUserAttribute will be much more convenient for you than using SetItemDataPtr because you don't have to worry about deleting the objects any more.
If you want to create user attribute class for your own binary objects, you have to override Clone(). This method is called in order to copy your binary object from one style object to another.
Here is an example override:
// Copying attribute (e.g. from one style object to another) CGXAbstractUserAttribute* CGXUserAttribute::Clone() const { |
||
return new CGXUserAttribute(*this); | ||
} |
If you want to add support for Ole Drag and Drop, Clipboard and Serialization you should override Serialize.
Example:
void CGXAbstractUserAttribute::Serialize(CArchive& ar) { |
|||
if (ar.IsStoring()) | |||
ar << GetValue(); | |||
else { |
|||
CString s; ar >> s; SetValue(s); |
|||
} | |||
} |
If you want to add support for the user attribute page in the grid and/or registry, you should also override the GetValue() and SetValue() methods. They convert your binary object into a string and back. See the class reference for CGXAbstractUserAttribute if you want to do this. Some more overrides let you fine-tune your derivative, as for example IsEqual, IsEmpty and more.
4. Subclass CGXStyle (takes the longest and the most code) Here is a sample for deriving the CGXStyle class:
// mystyle.h
#ifndef _MYSTYLE_H_ ////////////////////////////////////////////////////////////////// |
||||
DECLARE_SERIAL(CMyStyle) | ||||
public: | ||||
CMyStyle(); CMyStyle(const CMyStyle& p); // Override the following methods // Attributes - Accessors |
||||
protected: | ||||
BOOL m_bIncludeExpr; CString m_strExpr; |
||||
};
inline BOOL CMyStyle::GetIncludeExpr() const |
||||
{ return m_bIncludeExpr; } | ||||
inline CMyStyle& CMyStyle::SetIncludeExpr(BOOL b) | ||||
{ stylebits.userattr |= b; m_bIncludeExpr = b; if (!b) m_strExpr.Empty(); return *this; } | ||||
inline CString CMyStyle::GetExpr() const | ||||
{ return m_strExpr; } | ||||
inline const CString& CMyStyle::GetExprRef() const | ||||
{ return m_strExpr; } | ||||
inline CmyStyle& CMyStyle::SetExpr(const CString& s) | ||||
{ m_strExpr = s; return SetIncludeExpr(TRUE); } | ||||
#endif _MYSTYLE_H_
// mystyle.cpp #include "stdafx.h" /////////////////////////////////////////////////////////////////// IMPLEMENT_SERIAL(CMyStyle, CGXStyle, 0 /* schema number*/ ) CmyStyle::CMyStyle() |
||||
: CGXStyle() | ||||
{ | ||||
m_strExpr = ""; m_bIncludeExpr = TRUE; |
||||
}
CMyStyle::CMyStyle(const CMyStyle& p) |
||||
: CGXStyle(p) | ||||
{ | ||||
if (p.IsKindOf(RUNTIME_CLASS(CMyStyle))) { |
||||
m_strExpr = p.m_strExpr; m_bIncludeExpr = p.m_bIncludeExpr; |
||||
} | ||||
}
void CMyStyle::Free() |
||||
CGXStyle::Free(); m_bIncludeExpr = FALSE; m_strExpr.Empty(); |
||||
}
CMyStyle::~CMyStyle() |
||||
m_bIncludeExpr = FALSE; | ||||
}
CGXStyle* CMyStyle::Clone() const |
||||
return new CMyStyle(*this); | ||||
}
void CMyStyle::ChangeStyle(const CGXStyle& p, GXModifyType mt) |
||||
CGXStyle::ChangeStyle(p,mt); const CMyStyle* pp = NULL; if (p.IsKindOf(RUNTIME_CLASS(CMyStyle))) |
||||
pp = (const CMyStyle*) &p | ||||
switch (mt) { case gxExclude: |
||||
// Exclude Expression if (pp && pp->GetIncludeExpr()) |
||||
SetIncludeExpr(FALSE); | ||||
break; | ||||
case gxCopy: | ||||
// base class version did call operator= // which did already copy the expression break; |
||||
case gxOverride: | ||||
if (pp && pp->GetIncludeExpr()) | ||||
SetExpr(pp->GetExprRef()); | ||||
break; | ||||
case gxApplyNew: | ||||
if (!GetIncludeExpr() && pp && pp->GetIncludeExpr()) | ||||
SetExpr(pp->GetExprRef()); | ||||
break; | ||||
} | ||||
}
const CGXStyle& CMyStyle::operator=(const CGXStyle& p) |
||||
if (&p == this) | ||||
return *this; | ||||
CGXStyle::operator=(p); const CMyStyle* pp = NULL; if (p.IsKindOf(RUNTIME_CLASS(CMyStyle))) |
||||
pp = (const CMyStyle*) &p | ||||
if (pp && pp->GetIncludeExpr()) | ||||
SetExpr(pp->GetExprRef()); | ||||
else | ||||
SetIncludeExpr(FALSE); | ||||
return *this; | ||||
}
// Serialize is needed for serializing data to |
||||
CGXStyle::Serialize(ar, pStylesMap);
if (ar.IsStoring()) |
||||
ar << (BYTE) m_bIncludeExpr; ar m_strExpr; |
||||
} else { |
||||
BYTE b; ar >> b; m_bIncludeExpr = b; ar >> m_strExpr; |
||||
} | ||||
}
// In CGXGridCore there is a so called "factory-method" for CGXStyle |
||||
if (m_StyleBuffer.IsEmpty()) { |
||||
CGXStyle* pStyle = new CMyStyle; return pStyle; |
||||
}
return (CGXStyle*) m_StyleBuffer.RemoveHead(); |
||||
} |
You should also be careful that you always instantiate CmyStyle instead of CGXStyle in your code.
Example:
you should use:
SetStyleRange(CGXRange(1,1), CMyStyle().SetTextColor(RGB(...))); |
instead of:
SetStyleRange(CGXRange(1,1), CGXStyle().SetTextColor(RGB(...))); |
If you want to support serialization, you should also derive a class from CGXStylesMap and implement the following constructor:
class CMyStylesMap: public CGXStylesMap() { |
||
DECLARE_SERIAL(CMyStylesMap); | ||
public: | ||
CMyStylesMap(); | ||
};
IMPLEMENT_SERIAL(CMyStylesMap, CGXStylesMap, 0); CMyStylesMap::CMyStylesMap() |
||
: CGXStylesMap(RUNTIME_CLASS(CMyStyle)) | ||
{
} |
The problem is that when CGXStylesMap is serialized, the default constructor is called which sets the style class to be CGXStyle. By explicitly using the above constructor, which will be called from serialization the style class will be set to CMyStyle. You have to bind the CMyStylesMap to your paramater object instead of using the default CGXStylesMap.
This should be done in your OnInitialUpdate method like this:
void C1stGridView::OnInitialUpdate() { |
|||
BOOL bFirstView = FALSE;
if (GetDocument()->m_pParam == NULL) |
|||
bFirstView = TRUE;
// construct parameter object |
|||
}
// pass the pointer to the grid view if (bFirstView) |
|||
GetParam()->SetStylesMap(new CMyStylesMap); | |||
CGXGridView::OnInitialUpdate();
... |
|||
} |