Introduction
I needed to abstract some of the GUI (MFC) components for one of my projects, so they could be used in other modules.
This project includes a set of classes allowing you to easily expose your variables in an MFC property grid, and receive notifications through callbacks (FastDelegate or lambda functions).
Using the code
The system is split in two parts:
- The property manager, which needs to be initialized with a
CMFCPropertyGridCtrl
pointer - Property holders, which are classes containing attributes you want to expose
Exposing your class and attributes is done in three steps:
- Inherit the
IPropertyHolder
class, and define its members by pasting this macro in the class definition: IWE_DEFINE_PROP_HOLDER();
- Define the attributes you want to expose using the
CPropVar
wrapper, like this: CPropVar<YourType>
. - Define the way you want to expose your items in the property control (see the example below).
The CPropVar
wrapper implements common operators like =, +=, -=, ++, --, etc., and will automatically update the property grid.
However, in some cases, you might need to update it manually: myObj.Update();
Simple usage
The following sample demonstrates a simple usage:
Includes:
#include "UIPropertyGrid.h"
#include "BasePropVar.h" // Include this in your property holder class files
Initialization:
void InitializeGridMgr(CMFCPropertyGridCtrl* pPropGridCtrl)
{
CUIPropertyGrid* pUIPropGrid = new CUIPropertyGrid(pPropGridCtrl);
}
Define a property holder:
class CTestPropertyHolder : public IPropertyHolder
{
public:
CTestPropertyHolder()
{
m_iInt = 10;
m_bBool = false;
m_fFloat = 50.0f;
m_vecStrList.Get().push_back("Hi");
m_vecStrList.Get().push_back("What a nice weather");
m_vecStrList.Get().push_back("Today");
}
private:
float getFloatProp()
{
return m_fFloat;
}
void setFloatProp(float fVal)
{
m_fFloat = fVal;
}
private:
CPropVar<int> m_iInt;
CPropVar<bool> m_bBool;
CPropVar<float> m_fFloat;
CPropVar<std::vector<std::string>> m_vecStrList;
CPropVar<std::string> m_sCurStrItem;
protected:
IWE_DEFINE_PROP_HOLDER();
};
IWE_IMPLEMENT_PROP_BEGIN(CTestPropertyHolder)
IWE_PROP_LEVEL_BEGIN("Group #1")
IWE_PROP_INT("myInt", "This is an integer property.", m_iInt, false);
IWE_PROP_INT("myInt (RO)", "This is a read-only integer property.", m_iInt, true);
IWE_PROP_BOOL_GS("myBool", "This is a boolean property, "
"with custom lambda getter and setter.", m_bBool,
[pInst]() -> bool { return pInst->m_bBool; },
[pInst](bool bVal) { pInst->m_bBool = bVal; },
false);
IWE_PROP_LEVEL_BEGIN("SubGroup #1")
IWE_PROP_FLOAT_GS("myFloat", "This is a float property,"
" with custom fastdelegate getter and setter.", m_fFloat,
FastDelegate<float()>(pInst, &CTestPropertyHolder::getFloatProp),
FastDelegate<void(float)>(pInst, &CTestPropertyHolder::setFloatProp),
false);
IWE_PROP_COMBO("myVector", "This is a vector property.",
m_vecStrList, m_sCurStrItem, true);
IWE_PROP_LEVEL_END()
IWE_PROP_LEVEL_END()
IWE_IMPLEMENT_PROP_END()
Now, you just need to tell the propertygrid manager to display this property holder:
void setPropertyHolder(CUIPropertyGrid* pUIPropGrid, IPropertyHolder* pPropHolder)
{
pUIPropGrid->setPropertyHolder(pPropHolder);
}
[Rev 1] Property inheritance and inclusion
Property inheritance allows to inherit property holder classes, and append new properties.
Property Inheritance
This sample builds on top of the CTestPropertyHolder class we created in last example.
First, let's create a child class which inherits CTestPropertyHolder:
class CTestChildPropertyHolder : public CTestPropertyHolder
{
public:
CTestChildPropertyHolder()
{
}
protected:
IWE_DEFINE_PROP_HOLDER_OVERRIDE();
};
Note the use of IWE_DEFINE_PROP_HOLDER_OVERRIDE instead of IWE_DEFINE_PROP_HOLDER.
Now, we'll implement its properties layout :
IWE_IMPLEMENT_PROP_OVERRIDE_BEGIN(CTestChildPropertyHolder, CTestPropertyHolder)
IWE_PROP_LEVEL_BEGIN("Child Group #1")
IWE_PROP_LEVEL_END()
IWE_IMPLEMENT_PROP_END()
Again, note the use of IWE_IMPLEMENT_PROP_OVERRIDE_BEGIN instead of IWE_IMPLEMENT_PROP_BEGIN. First parameter is the child class, while second is the base class.
If you display CTestChildPropertyHolder in your property grid, you should see CTestPropertyHolder's properties with "Child Group #1" appended.
Property holder inclusion
Now, we are going to define another property holder, and include its properties in these of CTestChildPropertyHolder.
class CSimplePropertyHolder : public IPropertyHolder
{
public:
CSimplePropertyHolder()
{
m_sStrItem = "Hello world";
}
private:
CPropVar<std::string> m_sStrItem;
public:
IWE_DEFINE_PROP_HOLDER();
};
IWE_IMPLEMENT_PROP_BEGIN(CSimplePropertyHolder)
IWE_PROP_LEVEL_BEGIN("Simple Property Holder")
IWE_PROP_STRING("myString", "This is a string property", m_sStrItem, false);
IWE_PROP_LEVEL_END()
IWE_IMPLEMENT_PROP_END()
Note that IWE_DEFINE_PROP_HOLDER has public access. This is necessary if you want another class to include a given class's properties.
Now that we have a property holder object to include, we'll modify CTestChildPropertyHolder to display it :
class CTestChildPropertyHolder : public CTestPropertyHolder
{
public:
CTestChildPropertyHolder()
{
m_pPropHolderObject = new CSimplePropertyHolder();
}
private:
CSimplePropertyHolder* m_pPropHolderObject;
protected:
IWE_DEFINE_PROP_HOLDER_OVERRIDE();
};
IWE_IMPLEMENT_PROP_OVERRIDE_BEGIN(CTestChildPropertyHolder, CTestPropertyHolder)
IWE_PROP_LEVEL_BEGIN("Child Group #1")
IWE_PROP_LEVEL_END()
IWE_PROP_HOLDER_OBJ(m_pPropHolderObject);
IWE_IMPLEMENT_PROP_END()
That's it !
Special thanks: Don Clugston for his awesome fast delegates (http://www.codeproject.com/Articles/7150/Member-Function-Pointers-and-the-Fastest-Possible).
History
- 04/14/2012 - [Rev 1] Added inheritance and include features
- 03/25/2012 - [Rev 0] Initial version.
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.