Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

An Adaptable Property List Control

0.00/5 (No votes)
25 Apr 2003 1  
An object properties list control than can change based on the objects state.
  • Download demo project - 45 Kb
  • Download source files - 19 Kb

    Sample Image - PropertyListCtrl.gif

    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

    1. Include the source files into your project.
    2. 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.
    3. 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.
    4. Use the CPropertyListCtrl control in your dialog or view and manipulate its property item manager with the SetPropertyItemManager method.

    How to implement your 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 the BEGIN_ITERATE_PROPERTY_ITEMS macros. For a better understanding of these look at how they are implemented in the CPropertyItemString or CPropertyItemList classes.

    How to define your own property list item value class

    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.

    /////////////////////////////////////////////////////////////////////////////
    
    // 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, overriding its virtual LPCTSTR GetItemData(int nItem) const function.

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

    How to implement your own property item manager class

    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::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() }

    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: { // 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 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.
  • License

    This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

    A list of licenses authors might use can be found here