An Adaptable Property List Control


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


    Comments

    • Where is the List Control

      Posted by maverick786us on 09/24/2005 04:53am

      I downloaded your Demo project, but instead of List Control a list box was placed. Can you show me an exmaple in which a button can be placed on a column of a list control so that it can work like any command button?

      Reply
    • Here's another great properties control, one of the best !!!

      Posted by Legacy on 07/29/2003 12:00am

      Originally posted by: Scott Evans

      I just stumbled across this one the other day, very nice!
      http://www.codejock.com/products/propertygrid/

      • Thanks but no thanks

        Posted by dogbear on 05/29/2005 01:14pm

        Keep your product plugs out of codeguru, buddy. You and your tactics are sad and unwanted.

        Reply
      Reply
    • Support for buttons?

      Posted by Legacy on 06/05/2002 12:00am

      Originally posted by: Craig Schmidt

      Hi there,

      Very nice control. Any future plans to implement a inplace button control?

      Thanks

      Craig

      Reply
    • ExPropertiesList

      Posted by Legacy on 09/29/2001 12:00am

      Originally posted by: Mike Philis

      Do you need a powerfull PropertiesList? Check this out:
      
      http://www.exontrol.com/sg.jsp?content=products/expropertieslist

      Mike
      www.exontrol.com

      Reply
    • New web site

      Posted by Legacy on 07/24/2001 12:00am

      Originally posted by: Stefan Belopotocan

      My new website is at http://www.belosoft.host.sk

      Reply
    • Code update

      Posted by Legacy on 08/30/2000 12:00am

      Originally posted by: Stefan Belopotocan

      I have not yet updated my code at CodeGuru, but the latest code updates you can find http://stefanbelopotocan.miesto.sk/propertylistctrl_e.htm

      Reply
    • Scrolling inside ListBox doesn't work

      Posted by Legacy on 07/29/2000 12:00am

      Originally posted by: Sander van Woensel

      When the Listbox is not large enough to contain all items,
      it creates a vertical scrollbar (oh really?) Now, somehow this scrollbar doesn't work, didn't found a sollution yet..

      For the rest, an outstanding peice of source!

      can anybody help?

      Regards,
      S. van Woensel

      Reply
    • Listbox (Dropdown) Height fix

      Posted by Legacy on 07/29/2000 12:00am

      Originally posted by: Sander van Woensel

      Since itemheight is mostly not the same width as ::GetSystemMetrics(SM_CXHSCROLL) in a ListBox
      
      you would rather use GetItemHeight(0), summize this with 1, since the following weird thing occurs:
      GetItemHeight(0) * 2 = not enough to hold 2 items
      GetItemHeight(0) * 7 = exactly enough to hold 7 items

      When the LBS_NOINTEGRALHEIGHT style is not set, the listbox will resize the oversize back to the
      correct value it needs to hold all the items, in your case, with 2 items it is correctly resized,
      only when 7 items are the case, the listbox resizes itself one item to big ...

      So, all together, I suggest:

      #define DEFAULT_IPLISTBOX_HEIGHT (13+1) * 8 // 13+1: common itemheight with marge times 8 items

      void CInPlaceComboBoxImp::ResetListBoxHeight()
      {
      CRect rect;

      GetClientRect(rect);
      rect.right -= 1;

      int nItems = m_wndList.GetCount();
      int nListBoxHeight = nItems > 0 ? m_wndList.GetItemHeight(0)+1) * nItems : DEFAULT_IPLISTBOX_HEIGHT;

      if(nListBoxHeight > DEFAULT_IPLISTBOX_HEIGHT)
      nListBoxHeight = DEFAULT_IPLISTBOX_HEIGHT;

      m_wndList.SetWindowPos(NULL, 0, 0, rect.Width(), nListBoxHeight, SWP_NOZORDER|SWP_NOMOVE);
      }

      Reply
    • combo box dropdown clipped by Listbox window

      Posted by Legacy on 02/25/2000 12:00am

      Originally posted by: John L. Martin

      I think that because the in-place combo box's dropdown list is created as a child of the listbox window, the dropdown list is limited to the client area of the listbox window. This means that when the control is near the bottom of the window, the list box is "cut off" and it does not work like a normal windows combo box (which would extend past the window bottom).

      Does anyone have a solution?

      Reply
    • Keyboard support?

      Posted by Legacy on 01/19/2000 12:00am

      Originally posted by: Martin Filteau

      Really cool!

      The only thing *really* missing is keyboard support (try changing property settings without the mouse...) This shouldn't be that hard to code....

      Reply
    • Loading, Please Wait ...

    Leave a Comment
    • Your email address will not be published. All fields are required.

    Top White Papers and Webcasts

    • The world of data storage is changing around us as organizations continue to seek ways by which they can leverage cloud-based services. However, simply shifting everything to the public cloud is generally neither desirable nor feasible, so organizations are struggling with finding ways that they can leverage the public cloud and its outcomes where it makes sense and leave everything else on-premises. The Gorilla Guide lays out the key questions and considerations you need to think through in building your …

    • The open source cloud computing project OpenStack has come a long way since NASA and Rackspace launched it in 2010. Backed by leading technology infrastructure providers including Cisco, Dell, EMC, HP, IBM, Intel, and VMware, OpenStack underpins significant workloads at an increasingly diverse set of organizations, including BWM, CERN, Comcast, eBay, and Wal-Mart. For CIOs engaged in broader programs to win, serve, and retain customers -- and refocus business technology (BT) spend -- a planned and pragmatic …

    Most Popular Programming Stories

    More for Developers

    RSS Feeds

    Thanks for your registration, follow us on our social networks to keep up-to-date