属性表和属性页

对象的属性通过 COM 接口或对象的 IDispatch 实现向客户端公开,从而允许通过调用这些方法的程序更改属性。 属性页的 OLE 技术提供了根据 Windows 用户界面标准为对象的属性生成用户界面的方法。 因此,这些属性向最终用户公开。 对象的属性表是选项卡式对话框,其中每个选项卡对应于特定的属性页。 用于处理属性页的 OLE 模型包含以下功能:

  • 每个属性页由进程内对象管理,该对象实现 IPropertyPageIPropertyPage2。 每个页面都使用其自己的唯一 CLSID 进行标识。
  • 对象通过实现 ISpecifyPropertyPages来指定对属性页的支持。 通过此接口,调用方可以获取标识对象支持的特定属性页的 CLSID 列表。 如果对象指定属性页 CLSID,该对象必须能够从属性页接收属性更改。
  • 任何想要显示对象的属性表的代码(客户端或对象)都会传递对象的 IUnknown 指针(或数组(如果多个对象受到影响)以及页 CLSID 数组,以 OleCreatePropertyFrameOleCreatePropertyFrameIndirect,这将创建 tabbed-dialog box。
  • 属性帧对话框在每个 CLSID 上使用 CoCreateInstance 实例化每个属性页的单个实例。 属性帧至少获取每个页面的 IPropertyPage 指针。 此外,框架还会为每个页面创建一个属性页网站对象。 每个网站实现 IPropertyPageSite,此指针将传递给每个页面。 然后,该页面通过此接口指针与网站通信。
  • 每个页面还都了解已为其调用的对象或对象;也就是说,属性帧将对象的 IUnknown指针传递给每个页面。 当指示对对象应用更改时,每个页面都会查询相应的接口指针,并根据需要以任何方式将新的属性值传递给对象。 没有关于这种沟通如何发生的规定。
  • 对象还可以支持每个属性浏览 IPerPropertyBrowsing 接口,允许对象指定在显示属性页时应接收初始焦点的属性,并指定客户端在其自己的用户界面中显示的字符串和值。

下图说明了这些功能:

显示属性表和属性页功能的示意图。

这些接口的定义如下:

interface ISpecifyPropertyPages : IUnknown 
  { 
    HRESULT GetPages([out] CAUUID *pPages); 
  }; 
 
 
interface IPropertyPage : IUnknown 
  { 
    HRESULT SetPageSite([in] IPropertyPageSite *pPageSite); 
    HRESULT Activate([in] HWND hWndParent, [in] LPCRECT prc 
        , [in] BOOL bModal); 
    HRESULT Deactivate(void); 
    HRESULT GetPageInfo([out] PROPPAGEINFO *pPageInfo); 
    HRESULT SetObjects([in] ULONG cObjects 
        , [in, max_is(cObjects)] IUnknown **ppunk); 
    HRESULT Show([in] UINT nCmdShow); 
    HRESULT Move([in] LPCRECT prc); 
    HRESULT IsPageDirty(void); 
    HRESULT Apply(void); 
    HRESULT Help([in] LPCOLESTR pszHelpDir); 
    HRESULT TranslateAccelerator([in] LPMSG pMsg); 
  } 
 
interface IPropertyPageSite : IUnknown 
  { 
    HRESULT OnStatusChange([in] DWORD dwFlags); 
    HRESULT GetLocaleID([out] LCID *pLocaleID); 
    HRESULT GetPageContainer([out] IUnknown **ppUnk); 
    HRESULT TranslateAccelerator([in] LPMSG pMsg); 
  } 
 

ISpecifyPropertyPages::GetPages 方法返回 UUID (GUID) 值的计数数组,其中每个值描述了对象希望显示的属性页的 CLSID。 使用 OleCreatePropertyFrameOleCreatePropertyFrameIndirect 调用属性表的人员 将此数组传递给函数。 请注意,如果调用方希望显示多个对象的属性页,则它只能将所有对象的 CLSID 列表的交集传递给这些函数。 换句话说,调用方只能调用所有对象通用的属性页。

此外,调用方还会向 API 函数传递 IUnknown 指向受影响对象的指针。 这两个 API 函数都会创建属性框架对话框,并为它将加载的每个页面实例化 IPropertyPageSite。 通过此接口,属性页可以:

属性帧实例化每个属性页对象,并获取每个页面 IPropertyPage 接口。 通过此接口,框架通知其页面网站(SetPageSite),检索页面维度和字符串(GetPageInfo),将接口指针传递给受影响的对象(SetObjects),告知页面何时创建和销毁其控件(激活停用), 指示页面显示或重新定位自身(显示移动),指示页面将其当前值应用于受影响的对象(应用),检查 页面的脏状态(IsPageDirty)、调用帮助(帮助),并将击键传递给页面(TranslateAccelerator)。

对象还可以支持按属性浏览,该浏览提供:

  1. 一种方法(通过 IPerPropertyBrowsingIPropertyPage2)指定在首次显示属性表时应向哪个属性页提供初始焦点的属性
  2. 一种方法(通过 IPerPropertyBrowsing)为对象指定预定义值和相应的描述性字符串,这些字符串可以在客户端自己的用户界面中显示属性。

对象可以选择在没有支持 (1) 的情况下支持 (2),例如当对象没有属性表时。

IPropertyPage2IPerPropertyBrowsing 接口的定义如下:

interface IPerPropertyBrowsing : IUnknown 
  { 
    HRESULT GetDisplayString([in] DISPID dispID, [out] BSTR *pbstr); 
    HRESULT MapPropertyToPage([in] DISPID dispID, [out] CLSID *pclsid); 
    HRESULT GetPredefinedStrings([in] DISPID dispID, [out] CALPOLESTR *pcaStringsOut, [out] CADWORD *pcaCookiesOut); 
    HRESULT GetPredefinedValue([in] DISPID dispID, [in] DWORD dwCookie, [out] VARIANT *pvarOut); 
  } 
 
interface IPropertyPage2 : IPropertyPage 
  { 
    HRESULT EditProperty([in] DISPID dispID); 
  } 
 

为了指定对此类功能的支持,该对象实现 IPerPropertyBrowsing。 通过此接口,调用方可以请求实现浏览所需的信息,例如预定义字符串(GetPredefinedStrings)和值(GetPredefinedValue)以及给定属性的显示字符串(GetDisplayString)。

此外,客户端还可以获取属性页的 CLSID,该页允许用户编辑使用 DISPID 标识的给定属性(MapPropertyToPage)。 然后,客户端指示属性帧最初通过将 CLSID 和 DISPID 传递给 OleCreatePropertyFrameIndirect来激活该页面。 该帧首先激活该页面,并通过 IPropertyPage2::EditProperty将 DISPID 传递给页面。 然后,页面将焦点设置为该属性的编辑字段。 这样,客户端就可以从其自己的用户界面中的属性名称跳转到可以作该属性的属性页。

属性页和属性表