Introduction
This control lets you edit variables of objects. The control is designed to ease as much pain as possible, and as a result, only a single line of code is needed to represent each property in the control. Yes, other property controls are out there already, but this one, in my humble opinion, offers exceptional ease of use. Please comment in any way you like.
Using the code
I'll let the code speak for itself, one code strip tells more than a thousand words, so let's dig in.
The IPropertyHost interface
The control is all about implementing the IPropertyHost
interface. Objects that need be represented in the property control, must implement this interface. The control will talk to the represented object through this interface.
class IPropertyHost
{
public:
virtual void GetProperties( EPropList& PropList )
{
}
virtual bool PropertyChanging( const void* pProperty , void* pNewValue )
{
return true;
}
virtual bool IsPropertyEnabled( const void* pProperty )
{
return true;
}
}
Getting objects represented in the control
This example shows essentials. Overriding GetProperties()
is all that need be done to get started! As you can see, you pass the address of the variable, and the control will read and write to the member through this.
class SomeObject : public IPropertyHost
{
int m_nInteger;
double m_dAngle;
int m_nComboIndex;
CString m_sText;
public:
virtual void GetProperties( EPropList& PropList )
{
PropList.AddPropInt ( this , "Integer" , &m_nInteger );
PropList.AddPropDouble( this , "Angle" , &m_dAngle );
PropList.AddPropCombo ( this , "Combo" , &m_nComboIndex )
->AddString( "Choise0" )
->AddString( "Choise1" )
->AddString( "Choise2" );
PropList.AddPropString( this , "String" , &m_sText );
}
virtual bool PropertyChanging( const void* pProperty , void* pNewValue );
virtual bool IsPropertyEnabled( const void* pProperty );
};
To represent objects in the control, let that object implement the IPropertyHost
interface. In the above example, SomeObject
implements the IPropertyHost
interface and overrides GetProperties()
. Call SetPropertyHost()
on the control passing an instance of SomeObject
, to have that instance represented. The control will shortly after, ask the property host (the SomeObject
instance) to list its properties. This way, SomeObject
is itself responsible for listing the relevant properties, within its own scope, to the user. Nothing else than GetProperties()
function need be changed in order to add or remove properties.
Getting notified on changes
When the user edits a property, the property host itself is notified about the upcoming change, and can deny the change if inappropriate. The control will notify the host with a call to PropertyChanging()
. Return true if the property can change the value.
virtual bool SomeObject::PropertyChanging( const void* pProperty ,
void* pNewValue )
{
bool bPleaseChange = true;
if( pProperty == &m_nComboIndex )
{
int nNewIndex = *(int*)pNewValue;
TRACE("combo index changing from %d to %d\n",
m_nComboIndex , nNewIndex );
}
else if( pProperty == &m_dAngle )
{
bPleaseChange = ( 0 <= m_dAngle && m_dAngle < 360 );
}
return bPleaseChange;
}
The default PropertyChanging()
implementation in IPropertyHost
returns true, allowing all changes. Override this, only if you as a property host need be notified on changes, or if you want to deny a certain change.
Enabling and disabling properties on the fly
It is possible to gray out, disable, properties on the fly. On refresh, the control will ask the host if a property is enabled or not.
virtual bool SomeObject::IsPropertyEnabled( const void* pProperty )
{
bool bEnabled = true;
if( pProperty == &m_dAngle )
{
bEnabled = (m_nComboIndex==1);
}
return bEnabled;
}
The default IsPropertyEnabled()
implementation returns true, enabling all properties always. Only override this if you need dynamic enabling or disabling. You can disable a property by default when adding the property to the property list in GetProperties()
. This is useful if you have variables that the user can read, but never change. Additional information of some kind.
Points of Interest
Property types
There are more property types than those shown in the example. Custom property types are easily implemented by subclassing EProperty
or a descendant like EIconTextButtonProperty
.
Final words
If you find this code useful and use it in an application, I'd like to see a screenshot of your work. This will encourage me to continue working with the control, and make my day. Please send to 'ruskialt' at 'gmail' in the 'com' domain.
History
2005-07-27: Improved look and performance + various additional features
- Better look & feel
Strings that don't fit are now shortened and suffixed with "..." to fit. Flicker is reduced, now drawing on memory dc before copying to screen. Node openings now animated, nodes below parent nodes will fall to their new position. Splitters now change mouse cursor when hovering or dragging.
- Performance enhancements
The view will now totally skip drawing properties outside the view. Various calculations have been moved to only be calculated when 'dirty'.
- Various minor new features
Numeric types now support hex user input. Added 'special case text output' for all numeric types. Splitter added to enable comment pane resize. Multidouble
property added to support monitoring a list of double
s. SetType()
allows for specifying type of integers, just parse byte width and sign state of the integer in question.
- Various bug fixes
Combo box now opens its menu correctly when the view is scrolled. Thousand separator bug fixed. Scrollbars now update their sizes to fit both open and closed node states. Hosts adding child hosts using PropList.AddPropHost(this,&m_SomeHost)
are now notified on child change.
2005-04-15: Initial release
Began programming Amiga machine code in 1992. Was into demo programming and 3D graphics down to the pixel. Switched to PC, C++ and Java in the late 90's. Graduated B.Sc.EE in 2002, now working in maritime industry as a software engineer.