65.9K
CodeProject 正在变化。 阅读更多。
Home

可适应的属性列表控件

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.68/5 (10投票s)

2000年6月20日

viewsIcon

213988

downloadIcon

1761

一个可以根据对象状态改变的对象属性列表控件。

  • 下载演示项目 - 45 Kb
  • 下载源代码文件 - 19 Kb

    Sample Image - PropertyListCtrl.gif

    引言

    在我开发的一个应用程序中,需要为不同类型的对象实现某种用户界面来更改其属性。有些对象的属性是静态的,但有些对象的属性会发生变化,反映其真实状态。因此,我决定创建一个类似 Microsoft Visual Basic 的控件,它可以根据对象的状态显示不同的属性项。

    举个简单的例子。我需要描述一个窗体中各个项的属性。这些项可以显示简单的文本,也可以显示来自数据库的具体表示。如果该项只显示简单的文本,那么我就不需要编辑指定数据字段和显示格式的其他参数(对象属性)。

    在上面的示例屏幕截图中,当“数据绑定”项设置为“否”时,“数据”和“数据格式”类别将从属性列表中移除。

    工作原理

    CPropertyListCtrl 类实现了用于显示属性项和用户交互的必要功能。

    CInPlaceEditImpCInPlaceComboBox 类分别实现了用于编辑字符串项值和列表项或枚举项值的就地控件。

    属性数据项类允许对属性数据项进行操作。

    CPropertyItemStringCPropertyItemList 是用于基本编辑属性数据值的类。

    CPropertyItemCategory 类包含一个类别的属性项。

    CPropertyItemManager 是一个对象所有属性类别的容器。

    CPropertyItemManagerAdaptable 类支持适应性机制。

    如何使用

    1. 将源文件包含到您的项目中。
    2. 如果需要,您可以定义自己的属性列表项值(枚举),这些类派生自 CPropertyItemList 类,和/或实现自己的自定义属性项值,这些类派生自基类 CPropertyItem
    3. 实现您的 CPropertyItemManager 派生属性项管理器,它描述您的对象的属性项。如果属性项会根据对象状态改变,则您的属性项管理器应派生自 CPropertyItemManagerAdaptable 类。
    4. 在您的对话框或视图中使用 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 宏结合使用。为了更好地理解,请查看 CPropertyItemStringCPropertyItemList 类中的实现。

    如何定义自己的属性列表项值类

    您可以使用预定义的宏来实现表示属性列表项值的类。在此示例中,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()); }

    待改进之处

  • 消除显示就地控件时的闪烁。
  • 添加更多编辑控件。
  • 如果您有其他改进建议,请告诉我,以便我将其纳入下一个版本。如果您想了解我如何在项目中使用此控件,请查看 http://welcome.to/StefanBelopotocan
  • © . All rights reserved.