An Adaptable Property List Control

CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.


Environment: VC6 SP4, NT5 SP5

In an application I was developing, there was a need to implement some
kind of the user interface for changing an object properties for different types
of objects. Some object properties are static, but there are objects in which
their propreties are changing, concerning their actual state. So I have decided
to create a control like in Microsoft Visual Basic with a feature of an adapting
context of property items to an object state.
Take a look at a simple example. I need to
describe individual items of the form. This item can display
a simple text, but also a concrete representation from a database. If this item displays only a simple text, I do not need to enter
other parameters (an object property) for discribing data and for formating this data.

How it works


The CPropertyListCtrl class implements neccessary utility for presenting property
items and for an user interaction.

The CInPlaceEditImp and CInPlaceComboBox classes implement in place controls for
editing an item value, the CInPlaceEditImp for string values and the CInPlaceComboBox for
list items or enumerations.

And the end the property data item classes CPropertyItemString, CPropertyItemList,
CPropertyItemCategory, CPropertyItemManager and CPropertyItemManagerAdaptable
for property data items manipulations.
The CPropertyItemString and CPropertyItemList are classes for basic editing property data values.
The CPropertyItemCategory is the class containing property items of one category and
the CPropertyItemManager is a container for all property categories of an object.
The CPropertyItemManagerAdaptable class supports a mechanism of the adaptibility.

How to use it

  1. Include source files into your project.
  2. If you need you can define your own property list item values (enumerations) derived from the class CPropertyItemList or implement your own custom property item value derived from the base class CPropertyItem.
  3. Implement your property item manager that describes property items of your object. In the case the property items are changing according to the object state, derive your property item manager from the class CPropertyItemManagerAdaptable.
  4. Use the CPropertyListCtrl control in your dialog or
    view and set its property item manager by SetPropertyItemManager method.

How to implement an own property item value class


Derive it from the base class CPropertyItem and implement its get/set methods and its virtual methods:

virtual void DrawValue(CDC* pDC, CRect& rect), for drawing a property item value into the CPropertyListCtrl dc context.

virtual void CreateInPlaceControl(CWnd* pWndParent, CRect& rect, CWnd*& pWndInPlaceControl), for creating its own suitable in place control for editing its value.

virtual void SetData(CWnd* pWndInPlaceControl), for setting its value after the end of editing by an in place control.


// CMyPropertyItem

class CMyPropertyItem : public CPropertyItem
{
CMyPropertyItem(const CMyPropertyItem& d);
CMyPropertyItem& operator=(const CMyPropertyItem& d);

protected:
DECLARE_DYNAMIC(CMyPropertyItem)

public:
CMyPropertyItem(MyDataType myData = INI_VALUE);
virtual ~CMyPropertyItem();

// Attributes
MyDataTyp& GetData() const;
void SetData(MyDataType& myData);

// Operations
virtual void DrawValue(CDC* pDC, CRect& rect);
virtual void CreateInPlaceControl(CWnd* pWndParent,
CRect& rect,
CWnd*& pWndInPlaceControl);
virtual void SetData(CWnd* pWndInPlaceControl);

// Data
private:
MyDataType m_myData;
};


And define its GET_ITEM… and SET_ITEM… macros that are used in a conjuction with BEGIN_ITERATE_PROPERTY_ITEMS macros.
For better understanding look at how are implemented the CPropertyItemString or CPropertyItemList classes.


How to define an own property list item value class


Use predefined macros for implementing property list item values. In this examles is defined the CMyCustomPropertyItemList with two property items.


// CMyCustomPropertyItemList

BEGIN_LIST_ITEM_DATA_TYPE(CMyCustomPropertyItemList)
LPCTSTR_STRING_ITEM_DATA(_T(“A string value here”)),
ID_STRING_ITEM_DATA(IDS_STRING_VALUE_FROM_RESOURCES)
END_LIST_ITEM_DATA_TYPE(CMyCustomPropertyItemList)


Or you can implement your own class, derived from the CPropertyItemList class, declaring its virtual ‘LPCTSTR GetItemData(int nItem) const’ function.


// CMyOwnPropertyItemList
class CMyOwnPropertyItemList : public CPropertyItemList
{
//….
public:
virtual LPCTSTR GetItemData(int nItem) const;
}

How to implement an own property item manager class

In order to the CPropertyListCtrl control knows that items display, you have to implement
your own property item manager describing property items of your object. If your object
property items are static you simple derive your property item manager from the class CPropertyItemManager
and in the constructor declare these items using predefined macros :


// CMyStaticPropertyItemManager
CMyStaticPropertyItemManager::CMyStaticPropertyItemManager()
{
// General
BEGIN_PROPERTY_TAB(_T(“General”), true)
PROPERTY_ITEM(ID_PD_NAME, CPropertyItemString,
_T(“Name”), true)

PROPERTY_ITEM(ID_PD_DESCRIPTION, CPropertyItemString,
_T(“Description”), true)

PROPERTY_ITEM(ID_PD_BIND_DATA, CPropertyItemListYesNo,
_T(“Data binding”), true)
END_PROPERTY_TAB()

// Data
BEGIN_PROPERTY_TAB(_T(“Data”), true)
PROPERTY_ITEM(ID_PD_DB_NODE, CPropertyItemString,
_T(“Db data node”), true)

PROPERTY_ITEM(ID_PD_HISTORY, CPropertyItemListYesNo,
_T(“History”), true)
END_PROPERTY_TAB()
}


And implement get/set methods for accessing property item values of your object. For simple reference
access you can ues predefined macros.


bool CMyStaticPropertyItemManager::SetData(const CObject* pData)
{
const CMyData* pMyData = static_cast< const CMyData* >(pData);

BEGIN_ITERATE_PROPERTY_ITEMS()
SET_ITEM_STRING(ID_PD_NAME, pMyData->m_strName)
SET_ITEM_STRING(ID_PD_DESCRIPTION, pMyData->m_strDescription)
SET_ITEM_LIST(ID_PD_BIND_DATA, pMyData->m_bBindDatabaseData)
SET_ITEM_STRING(ID_PD_DB_NODE, pMyData->m_strDbTableColumn)
SET_ITEM_LIST(ID_PD_HISTORY, pMyData->m_bCreateHistoryData)
END_ITERATE_PROPERTY_ITEMS()

return true;
}

bool CMyStaticPropertyItemManager::GetData(CObject* pData) const
{
CMyData* pMyData = static_cast< CMyData* >(pData);

BEGIN_ITERATE_PROPERTY_ITEMS()
GET_ITEM_STRING(ID_PD_NAME, pMyData->m_strName)
GET_ITEM_STRING(ID_PD_DESCRIPTION, pMyData->m_strDescription)
GET_ITEM_LIST(ID_PD_BIND_DATA, pMyData->m_bBindDatabaseData)
GET_ITEM_STRING(ID_PD_DB_NODE, pMyData->m_strDbTableColumn)
GET_ITEM_LIST(ID_PD_HISTORY, pMyData->m_bCreateHistoryData)
END_ITERATE_PROPERTY_ITEMS()

return true;
}


If you want to implement an adaptable property item manager derive it from the class CPropertyItemManagerAdaptable
and define a virtual void OnDataChanged(CPropertyItem* pPropertyItem, CPropertyListCtrl* pWndPropertyListCtrl, int nIndex) method
for changing a property items state.


void CMyAdaptablePropertyItemManager::OnDataChanged(CPropertyItem* pPropertyItem,
CPropertyListCtrl* pWndPropertyListCtrl,
int nIndex)
{
bool bDoChecking = false;

switch(pPropertyItem->GetPropertyID())
{
case ID_PD_BIND_DATA:
{
// Yes/No item
bool bEnableTabs;
static_cast<CPropertyItemList*>(pPropertyItem)->GetData(bEnableTabs);

// Enable/Disable tabs 1
CPropertyItemCategory* pPropertyItemTab = GetCategoryTab(1);

if(pPropertyItemTab->SetEnabled(bEnableTabs))
bDoChecking = true;

// Enable/Disable tabs 2
int nItemType;

static_cast<CPropertyItemList*>
(pPropertyItemTab->GetPropertyItem(ID_PD_DATA_TYPE))->GetData(nItemType);

pPropertyItemTab = GetCategoryTab(2);

if(pPropertyItemTab->SetEnabled(bEnableTabs && nItemType < 4))
bDoChecking = true;
}
break;

case ID_PD_DATA_TYPE:
{
// Enumerate item
int nItemType;
static_cast<CPropertyItemList*>(pPropertyItem)->GetData(nItemType);

For items 4 (Form) and 5 (Macro) disable tab #2, for others enable
CPropertyItemCategory* pPropertyItemTab = GetCategoryTab(2);
bDoChecking = pPropertyItemTab->SetEnabled(nItemType < 4);
}
break;

default:
return;
}

if(bDoChecking)
CheckState(pWndPropertyListCtrl, nIndex, pPropertyItem->GetPropertyID());
}

Things to Improve


  • Eliminate flicker meanwhile showing in place controls.

  • Add further edit controls.


  • If you have any other suggested improvements, please let me know so that I can incorporate them into the next release.
    If you want to see how I have used this control in my projects, take a look at http://welcome.to/StefanBelopotocan.

    Downloads

    Download demo project – 45 Kb

    Download source – 19 Kb

    Version History



    • June 2000 – Keybord and searching support, correct displaying of CInPlaceComboBoxImp control

    • September 1999 – Initial release

    More by Author

    Get the Free Newsletter!

    Subscribe to Developer Insider for top news, trends & analysis

    Must Read