Click here to Skip to main content
15,885,985 members
Articles / Desktop Programming / MFC

MFC Property Grid Helper (Abstraction)

Rate me:
Please Sign up or sign in to vote.
4.50/5 (2 votes)
14 Apr 2012MIT2 min read 34.5K   1.4K   15   6
Abstraction of the MFC PropertyGrid component, allowing to easily expose properties, and receive notifications on changes.

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:

  1. The property manager, which needs to be initialized with a CMFCPropertyGridCtrl pointer
  2. Property holders, which are classes containing attributes you want to expose

Exposing your class and attributes is done in three steps:

  1. Inherit the IPropertyHolder class, and define its members by pasting this macro in the class definition: IWE_DEFINE_PROP_HOLDER();
  2. Define the attributes you want to expose using the CPropVar wrapper, like this: CPropVar<YourType>.
  3. 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:

C++
// Include this for property grid manager initialization and when setting current property holder
#include "UIPropertyGrid.h"
#include "BasePropVar.h" // Include this in your property holder class files

Initialization:

C++
//
// Initialize the property grid manager
//
void InitializeGridMgr(CMFCPropertyGridCtrl* pPropGridCtrl)
{
  CUIPropertyGrid* pUIPropGrid = new CUIPropertyGrid(pPropGridCtrl);
  
  // Keep pUIPropGrid pointer somewhere
}

Define a property holder:

C++
// A simple class exposing 5 attributes to the property grid
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()
  {
    // Do something useful here

    return m_fFloat;
  }

  void                                setFloatProp(float fVal)
  {
    m_fFloat = fVal;

    // Do something useful here
  }

private:
  // Property items, note the use of CPropVar.
  CPropVar<int>                       m_iInt;
  CPropVar<bool>                      m_bBool;
  CPropVar<float>                     m_fFloat;
  CPropVar<std::vector<std::string>>  m_vecStrList;
  CPropVar<std::string>               m_sCurStrItem;

// [Rev 1] Changed from private
protected:
  IWE_DEFINE_PROP_HOLDER();
};

// Define the property tree hierarchy, and what attributes to display
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; /* Do something useful here */ },
      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() // !SubGroup #1

  IWE_PROP_LEVEL_END() // !Group #1

IWE_IMPLEMENT_PROP_END()

Now, you just need to tell the propertygrid manager to display this property holder:

C++
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: 

C++
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 :

C++
IWE_IMPLEMENT_PROP_OVERRIDE_BEGIN(CTestChildPropertyHolder, CTestPropertyHolder)

  IWE_PROP_LEVEL_BEGIN("Child Group #1")

    // TODO: Add some properties here

  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.

C++
// A simple property holder, exposing a single property (myString)
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 : 

C++
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")

    // TODO: Add some properties here

  IWE_PROP_LEVEL_END()

  // Include this property holder
  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.  

License

This article, along with any associated source code and files, is licensed under The MIT License


Written By
France France
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionHaving trouble implementing this... Pin
Travesty14-Jun-14 5:28
Travesty14-Jun-14 5:28 
AnswerRe: Having trouble implementing this... Pin
Travesty14-Jun-14 7:32
Travesty14-Jun-14 7:32 
GeneralRe: Memory Leaks Pin
Travesty14-Jun-14 20:35
Travesty14-Jun-14 20:35 
I can now confirm that the memory leaks in your code are coming from the lack of deallocating objects allocated by your property macros, starting with objects pushed onto parentStack in IUIPropertyGrid.h...

Deleting pUIPropGrid and pTestProp (in PropertiesWnd.cpp) doesn't seem to handle these leaks, either. Can't seem to find a solid fix yet, considering the template/macro combo hell that I'd have to sort through simply just to understand your code, let alone debug it for you. However, if I somehow do manage to find a fix for this, I'll be sure to post it here for everyone.

EDIT:
Actually, I've managed to magic up a strategic way of deleting every object in parentStack, and it still doesn't clear everything up. There are still 7 more blocks of memory left to deallocate, versus the original 8 normal blocks (the largest of which eats up 80 bytes of memory). They all appear to stem from property template/macro combos, and therefore it is incredibly difficult to pinpoint their locations without potentially smashing the code to bits. All of this is from adding a single integer value to the property grid, so I can't imagine what would happen if I had several handfuls of data constantly being pushed and popped onto this control...

Someone please fix this! It's incredibly useful code, but the leaks just kill it.
"To do is to be" - Aristotle
"To be is to do" - Voltaire
"Do be do be doo" - Frank Sinatra


modified 15-Jun-14 4:04am.

GeneralRe: Memory Leaks Pin
Alexis.P15-Jun-14 23:55
Alexis.P15-Jun-14 23:55 
GeneralRe: Memory Leaks Pin
Travesty28-Jun-14 16:30
Travesty28-Jun-14 16:30 
GeneralMy vote of 4 Pin
logica27-Mar-12 22:39
logica27-Mar-12 22:39 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.