可适应的属性列表控件






4.68/5 (10投票s)
2000年6月20日

213988

1761
一个可以根据对象状态改变的对象属性列表控件。
引言
在我开发的一个应用程序中,需要为不同类型的对象实现某种用户界面来更改其属性。有些对象的属性是静态的,但有些对象的属性会发生变化,反映其真实状态。因此,我决定创建一个类似 Microsoft Visual Basic 的控件,它可以根据对象的状态显示不同的属性项。
举个简单的例子。我需要描述一个窗体中各个项的属性。这些项可以显示简单的文本,也可以显示来自数据库的具体表示。如果该项只显示简单的文本,那么我就不需要编辑指定数据字段和显示格式的其他参数(对象属性)。
在上面的示例屏幕截图中,当“数据绑定”项设置为“否”时,“数据”和“数据格式”类别将从属性列表中移除。
工作原理
CPropertyListCtrl
类实现了用于显示属性项和用户交互的必要功能。
CInPlaceEditImp
和 CInPlaceComboBox
类分别实现了用于编辑字符串项值和列表项或枚举项值的就地控件。
属性数据项类允许对属性数据项进行操作。
CPropertyItemString
和 CPropertyItemList
是用于基本编辑属性数据值的类。
CPropertyItemCategory
类包含一个类别的属性项。
CPropertyItemManager
是一个对象所有属性类别的容器。
CPropertyItemManagerAdaptable
类支持适应性机制。
如何使用
- 将源文件包含到您的项目中。
- 如果需要,您可以定义自己的属性列表项值(枚举),这些类派生自
CPropertyItemList
类,和/或实现自己的自定义属性项值,这些类派生自基类CPropertyItem
。 - 实现您的
CPropertyItemManager
派生属性项管理器,它描述您的对象的属性项。如果属性项会根据对象状态改变,则您的属性项管理器应派生自CPropertyItemManagerAdaptable
类。 - 在您的对话框或视图中使用
CPropertyListCtrl
控件,并通过SetPropertyItemManager
方法来操作其属性项管理器。
如何实现自己的属性项值类
将其派生自基类 CPropertyItem,并实现其 get/set 方法及其虚拟方法
virtual void DrawValue(CDC* pDC, CRect& rect)
// 用于将属性项值绘制到 CPropertyListCtrl 的 dc 上下文中。virtual void CreateInPlaceControl(CWnd* pWndParent, CRect& rect, CWnd*& pWndInPlaceControl)
// 用于创建其自身合适的就地控件来编辑其值。virtual void SetData(CWnd* pWndInPlaceControl)
// 用于在就地控件编辑结束后设置其值。
///////////////////////////////////////////////////////////////////////////// // 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; };
并定义其 GET_ITEM...
和 SET_ITEM...
宏,这些宏与 BEGIN_ITERATE_PROPERTY_ITEMS
宏结合使用。为了更好地理解,请查看 CPropertyItemString
或 CPropertyItemList
类中的实现。
如何定义自己的属性列表项值类
您可以使用预定义的宏来实现表示属性列表项值的类。在此示例中,CMyCustomPropertyItemList
定义了两个属性项。
///////////////////////////////////////////////////////////////////////////// // 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)
或者,您可以实现自己的类,派生自 CPropertyItemList 类,并重写其虚拟函数 LPCTSTR GetItemData(int nItem) const
。
///////////////////////////////////////////////////////////////////////////// // CMyOwnPropertyItemList class CMyOwnPropertyItemList : public CPropertyItemList { .... public: virtual LPCTSTR GetItemData(int nItem) const; }
如何实现自己的属性项管理器类
为了让 CPropertyListCtrl
控件知道要显示哪些项,您必须实现自己的属性项管理器来描述您对象的属性项。如果您的对象属性项是静态的,您只需将您的属性项管理器派生自 CPropertyItemManager
类,并在构造函数中使用预定义的宏声明项。
///////////////////////////////////////////////////////////////////////////// // 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() }
您还需要实现 get/set 方法来访问您对象的属性项值。对于简单的引用访问,您可以使用预定义的宏。
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; }
如果您想实现一个可适应的属性项管理器,请将其派生自 CPropertyItemManagerAdaptable
类,并定义一个 ` virtual void OnDataChanged(CPropertyItem* pPropertyItem, CPropertyListCtrl* pWndPropertyListCtrl, int nIndex)` 方法来更改属性项的状态。
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()); }