Download source files - 19 Kb
Introduction
In an application I was developing, there was a need to implement some kind of user interface for changing an objects properties for different types of objects. Some objects properties are static, but there are objects whose properties are changing, reflecting their actual state. So I decided to create a Microsoft Visual Basic like control that can display different property items according to an objects state.
Lets take a simple example. I need to describe the properties of individual items of a form. The items can display simple text, but also a concrete representation from a database. If the item displays only simple text, I do not need to edit the other parameters (object properties) that specify data field and display format.
In the sample screen above, the Data and Data format categories will be removed from the properties list when the Data binding item is set to no.
How it works
The CPropertyListCtrl
class implements the neccessary utility for presenting property items and user interaction.
The CInPlaceEditImp
and CInPlaceComboBox
classes implement in place controls for editing item values for strings and for list items or enumerations respectively.
The property data item classes allow for property data item manipulations.
The CPropertyItemString
and CPropertyItemList
are classes for basic editing of property data values.
The CPropertyItemCategory
class contains property items of one category.
The CPropertyItemManager
is a container for all the property categories of an object.
The CPropertyItemManagerAdaptable
class supports the mechanism of adaptibility.
How to use it
- Include the source files into your project.
- If you need to you can define your own property list item values (enumerations) derived from the class
CPropertyItemList
and / or implement your own custom property item value derived from the base class CPropertyItem
.
- Implement your
CPropertyItemManager
derived property item manager that describes the property items of your object. If the property items can change according to the objects state, derive your property item manager from the class CPropertyItemManagerAdaptable
.
- Use the
CPropertyListCtrl
control in your dialog or view and manipulate its property item manager with the SetPropertyItemManager
method.
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.
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();
MyDataTyp& GetData() const;
void SetData(MyDataType& myData);
virtual void DrawValue(CDC* pDC, CRect& rect);
virtual void CreateInPlaceControl(CWnd* pWndParent,
CRect& rect, CWnd*& pWndInPlaceControl);
virtual void SetData(CWnd* pWndInPlaceControl);
private:
MyDataType m_myData;
};
And define its GET_ITEM...
and SET_ITEM...
macros that are used in a conjuction with the BEGIN_ITERATE_PROPERTY_ITEMS
macros. For a better understanding of these look at how they are implemented in the CPropertyItemString
or CPropertyItemList
classes.
You can use the predefined macros for implementing classes representing property list item values. In this examle the CMyCustomPropertyItemList
is defined with two property items.
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, overriding its virtual LPCTSTR GetItemData(int nItem) const
function.
class CMyOwnPropertyItemList : public CPropertyItemList
{
....
public:
virtual LPCTSTR GetItemData(int nItem) const;
}
In order for the CPropertyListCtrl
control to know what items to display, you have to implement your own property item manager describing the property items of your object. If your object property items are static you simply derive your property item manager from the class CPropertyItemManager
and in the constructor declare the items using predefined macros :
CMyStaticPropertyItemManager::CMyStaticPropertyItemManager()
{
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()
PROPERTY_ITEM(ID_PD_DB_NODE, CPropertyItemString,
_T("Db data node"), true)
PROPERTY_ITEM(ID_PD_HISTORY, CPropertyItemListYesNo,
_T("History"), true)
END_PROPERTY_TAB()
}
You also need to implement get/set methods for accessing property item values of your object. For simple reference access you can use 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:
{
bool bEnableTabs;
static_cast <CPropertyItemList*>(pPropertyItem)->GetData(bEnableTabs);
CPropertyItemCategory* pPropertyItemTab = GetCategoryTab(1);
if(pPropertyItemTab->SetEnabled(bEnableTabs))
bDoChecking = true;
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: {
{
int nItemType;
static_cast<CPropertyItemList*>(pPropertyItem)->GetData(nItemType);
CPropertyItemCategory* pPropertyItemTab = GetCategoryTab(2);
bDoChecking = pPropertyItemTab->SetEnabled(nItemType <4);
}
break;
default:
return;
}
if(bDoChecking)
CheckState(pWndPropertyListCtrl, nIndex,
pPropertyItem->GetPropertyID());
}
Things to Improve
Eliminate flicker while 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.