Click here to Skip to main content
15,886,362 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 
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 
I appreciate the offer, but I've already moved along to just using the CMFCPropertyGridCtrl directly.
Turns out, it's not as daunting as I'd originally thought, and is quite easy to create my own abstractions for custom properties (via subclassing CMFCPropertyGridProperty). Thanks, and good luck!
"To do is to be" - Aristotle
"To be is to do" - Voltaire
"Do be do be doo" - Frank Sinatra

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.