I would like to add some application-specific data to the grid in a per cell or per column/row basis. (CGXStyle::Get/SetItemDataPtr() is not sufficient).
There are several ways of doing this and its up to you and your exact situation do 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. New with Objective Grid 6.0: 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_
#define _MYSTYLE_H_
//////////////////////////////////////////////////////////////////
// CMyStyle definition
class CMyStyle : public CGXStyle
{
DECLARE_SERIAL(CMyStyle)
public:
CMyStyle();
CMyStyle(const CMyStyle& p);
// Override the following methods
virtual void Free();
virtual void ChangeStyle(const CGXStyle& p, GXModifyType mt);
virtual CGXStyle* Clone() const; // create a new style with "new CGXStyle(*this);"
virtual ~CMyStyle();
virtual const CGXStyle& operator=(const CGXStyle& p);
virtual void Serialize(CArchive& ar, const CGXStylesMap* pStylesMap);
public:
// Attributes - Accessors
BOOL GetIncludeExpr() const;
CMyStyle& SetIncludeExpr(BOOL b);
CString CMyStyle::GetExpr() const;
const CString& CMyStyle::GetExprRef() const;
CMyStyle& CMyStyle::SetExpr(const CString& s);
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_
#include "stdafx.h"
#include "MyStyle.h"
///////////////////////////////////////////////////////////////////
// CMyStyle implementation
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 // create a new style with "new CGXStyle(*this);"
{
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
// document or clipboard.
void CMyStyle::Serialize(CArchive& ar, const CGXStylesMap* pStylesMap)
{
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
// objects. CGXGridCore always calls CreateStyle when it creates
// a style.
//
CGXStyle* CMyGridWnd::CreateStyle()
{
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 instaed of CGXStyle in your code.
// Example:
// you should use
SetStyleRange(CGXRange(1,1), CMyStyle().SetTextColor(RGB(...)));
// instaed of
// SetStyleRange(CGXRange(1,1), CGXStyle().SetTextColor(RGB(...)));
// If you want to support serialization, you shoul 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
GetDocument()->m_pParam = new CGXGridParam;
}
// pass the pointer to the grid view
SetParam(GetDocument()->m_pParam, FALSE);
if (bFirstView)
GetParam()->SetStylesMap(new CMyStylesMap);
CGXGridView::OnInitialUpdate();
...
}