示例:实现属性页

此示例演示如何根据显示的属性页(允许您更改) 文档选件类 界面的属性。此接口在visual studio的 公共环境对象模型示例 显示文档(尽管您将要创建的属性页不关心对象其行为来自何处,只要它们支持正确的接口)。

此示例基于 ATLPages示例

若要完成此示例,您需要:

  • 使用"添加选件类对话框和ATL属性页向导的添加ATL属性页选件类。

  • 编辑对话框资源 通过添加 Document 接口有趣的属性的新控件。

  • 保留属性页网站用户所做的通知添加消息处理程序 更改。

  • 添加一些 #import 语句和一个typedef在 管理 部分。

  • 验证对象的重写IPropertyPageImpl::SetObjects 传递给属性页。

  • 初始化属性页的接口的重写IPropertyPageImpl::Activate。

  • 更新使用最新的属性值的对象的重写IPropertyPageImpl::Apply。

  • 显示属性页 通过创建一个简单的帮助器对象。

  • 将测试属性页的创建宏。

添加ATL属性页选件类

首先,创建一个名为 ATLPages7的DLL服务器的新ATL项目。现在使用 ATL属性页向导 生成属性页。为属性页 DocProperties 然后切换 短名称Strings 页设置属性页特定项目。下表所示。

标题

TextDocument

文档字符串

VCUE TextDocument属性

Helpfile

<blank>

值在此向导页设置将返回到属性页容器,在调用 IPropertyPage::GetPageInfo。依赖于容器,但是,的内容之后发生在字符串它们通常用于标识您的页给用户。前缀通常出现在页上的选项,并且文档字符串在状态栏或工具提示会显示(尽管标准属性框架根本不使用此字符串)。

说明说明

字符串您在此处设置在您的项目中,在字符串资源由向导。您可以轻松地编辑这些字符串使用资源编辑器,如果需要更改此信息,页面中的代码生成之后。

单击让 向导生成的属性页。

编辑对话框资源

既然的属性页中生成的,则需要添加几个控件来表示该页的对话框资源。添加一个编辑框、静态文本控件和一个复选框并将其ID如下所示:

Visual Studio 编辑对话框资源

这些控件将用于显示文档及其只读状态的文件名。

说明说明

对话框资源不包括一个帧或命令按钮,也不会有可能需要的选项卡式外观。这些函数按一个属性页提供例如调用创建该 OleCreatePropertyFrame

添加消息处理程序

在控件中,可以添加消息处理程序更新页面的已更新状态,在控件更改之一的值:

BEGIN_MSG_MAP(CDocProperties)
   COMMAND_HANDLER(IDC_NAME, EN_CHANGE, OnUIChange)
   COMMAND_HANDLER(IDC_READONLY, BN_CLICKED, OnUIChange)
   CHAIN_MSG_MAP(IPropertyPageImpl<CDocProperties>)
END_MSG_MAP()

   // Respond to changes in the UI to update the dirty status of the page
   LRESULT OnUIChange(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
   {
      wNotifyCode; wID; hWndCtl; bHandled;
      SetDirty(true);
      return 0;
   }

此代码响应调用到编辑控件或复选框更改 IPropertyPageImpl::SetDirty,通知页网站页面已更改。通常页网站将通过启用或禁用了属性页的一个 Apply 按钮响应。

说明说明

在属性页,特性由用户修改的可能需要准确记录,以便可以避免更新未更改的属性。通过跟踪原始属性值及其代码与用户界面的当前值进行比较的本示例实现,当为时应用更改。

管理

现在添加有两个 #import 语句添加到DocProperties.h,让编译器知道 Document 接口:

// MSO.dll
#import <libid:2DF8D04C-5BFA-101B-BDE5-00AA0044DE52> version("2.2") \
   rename("RGB", "Rgb")   \
   rename("DocumentProperties", "documentproperties")   \
   rename("ReplaceText", "replaceText")   \
   rename("FindText", "findText")   \
   rename("GetObject", "getObject")   \
   raw_interfaces_only

// dte.olb
#import <libid:80CC9F66-E7D8-4DDD-85B6-D9E6CD0E93E2> \
   inject_statement("using namespace Office;")   \
   rename("ReplaceText", "replaceText")   \
   rename("FindText", "findText")   \
   rename("GetObject", "getObject")   \
   rename("SearchPath", "searchPath")   \
   raw_interfaces_only

您还需要引用 IPropertyPageImpl 基类;添加以下 typedef 到 CDocProperties 选件类:

typedef IPropertyPageImpl<CDocProperties> PPGBaseClass;

重写的IPropertyPageImpl::SetObjects

您需要重写的第一个 IPropertyPageImpl 方法是 SetObjects。您将添加代码只检查单个对象已通过,它支持您所期望的 Document 接口:

STDMETHOD(SetObjects)(ULONG nObjects, IUnknown** ppUnk)
{
   HRESULT hr = E_INVALIDARG;
   if (nObjects == 1)
   {
      CComQIPtr<EnvDTE::Document> pDoc(ppUnk[0]);
      if (pDoc)
         hr = PPGBaseClass::SetObjects(nObjects, ppUnk);
   }
   return hr;
}
说明说明

很有意义支持此页的一个对象,因为您将允许用户设置对象的文件名—只有一个文件可在任何一位置。

重写的IPropertyPageImpl::Activate

当页首次创建后,下一步是初始化与基础对象的属性值的属性页。

在这种情况下应添加以下成员添加到选件类,因为您用于比较也使用初始属性值,当页面的用户应用这些更改时:

CComBSTR m_bstrFullName;  // The original name
VARIANT_BOOL m_bReadOnly; // The original read-only state

激活 方法的基类来实现用于创建对话框及其控件负责,因此,您可以重写方法并在调用基类之后添加您的初始化:

STDMETHOD(Activate)(HWND hWndParent, LPCRECT prc, BOOL bModal)
{
   // If we don't have any objects, this method should not be called
   // Note that OleCreatePropertyFrame will call Activate even if
   // a call to SetObjects fails, so this check is required
   if (!m_ppUnk)
      return E_UNEXPECTED;

   // Use Activate to update the property page's UI with information
   // obtained from the objects in the m_ppUnk array

   // We update the page to display the Name and ReadOnly properties
   // of the document

   // Call the base class
   HRESULT hr = PPGBaseClass::Activate(hWndParent, prc, bModal);
   if (FAILED(hr))
      return hr;

   // Get the EnvDTE::Document pointer
   CComQIPtr<EnvDTE::Document> pDoc(m_ppUnk[0]);
   if (!pDoc)
      return E_UNEXPECTED;

   // Get the FullName property
   hr = pDoc->get_FullName(&m_bstrFullName);
   if (FAILED(hr))
      return hr;

   // Set the text box so that the user can see the document name
   USES_CONVERSION;
   SetDlgItemText(IDC_NAME, CW2CT(m_bstrFullName));

   // Get the ReadOnly property
   m_bReadOnly = VARIANT_FALSE;
   hr = pDoc->get_ReadOnly(&m_bReadOnly);
   if (FAILED(hr))
      return hr;

   // Set the check box so that the user can see the document's read-only status
   CheckDlgButton(IDC_READONLY, m_bReadOnly ? BST_CHECKED : BST_UNCHECKED);

   return hr;
}

此代码使用 Document 接口访问的COM方法属性感兴趣。然后使用 CDialogImpl 及其基类提供的Win32 API包装显示属性值给用户。

重写的IPropertyPageImpl::Apply

当用户要将它们应用于对象的更改,属性页网站将调用 适用 方法。这是若要执行代码的取消的位置在 Activate 的),而 Activate 采取了从对象的值并使它们在属性页,Apply 将控件的值在属性页并推送到对象。

STDMETHOD(Apply)(void)
{
   // If we don't have any objects, this method should not be called
   if (!m_ppUnk)
      return E_UNEXPECTED;

   // Use Apply to validate the user's settings and update the objects'
   // properties

   // Check whether we need to update the object
   // Quite important since standard property frame calls Apply
   // when it doesn't need to
   if (!m_bDirty)
      return S_OK;

   HRESULT hr = E_UNEXPECTED;

   // Get a pointer to the document
   CComQIPtr<EnvDTE::Document> pDoc(m_ppUnk[0]);
   if (!pDoc)
      return hr;

   // Get the read-only setting
   VARIANT_BOOL bReadOnly = IsDlgButtonChecked(IDC_READONLY) ? VARIANT_TRUE : VARIANT_FALSE;

   // Get the file name
   CComBSTR bstrName;
   if (!GetDlgItemText(IDC_NAME, bstrName.m_str))
      return E_FAIL;

   // Set the read-only property
   if (bReadOnly != m_bReadOnly)
   {
      hr = pDoc->put_ReadOnly(bReadOnly);
      if (FAILED(hr))
         return hr;
   }

   // Save the document
   if (bstrName != m_bstrFullName)
   {
      EnvDTE::vsSaveStatus status;
      hr = pDoc->Save(bstrName, &status);
      if (FAILED(hr))
         return hr;
   }

   // Clear the dirty status of the property page
   SetDirty(false);

   return S_OK;
}
说明说明

m_bDirty 的检查此实现中开始时避免对象的不必要的更新的初始检查是否 Apply 多次调用。还需要检查确保每个的属性值只会更改导致方法调用 Document

说明说明

Document 公开 FullName 作为只读属性。若要更新基于更改文档的文件名对属性页,必须使用 保存 方法保存具有不同名称的文件。因此,在属性页中的代码不必限制自己到获取或设置属性。

显示属性页

若要显示此页,您需要创建一个简单的帮助器对象。帮助器对象将提供简化显示的单页 OleCreatePropertyFrame API连接到一个对象的方法。此帮助器将模型,以便可以从Visual Basic使用。

使用 添加选件类对话框ATL简单对象向导 生成新选件类和使用 Helper 作为其短名称。一旦创建,添加一个方法根据下表所示。

方法名

ShowPage

参数

[in] BSTR bstrCaption, [in] BSTR bstrID, [in] IUnknown* pUnk

bstrCaption 参数是作为对话框的标题中显示的声明中。bstrID 参数是表示CLSID或属性页的ProgID的字符串显示在中。pUnk 参数是属性。属性页配置对象的 IUnknown 指针。

实现方法如下所示:

STDMETHODIMP CHelper::ShowPage(BSTR bstrCaption, BSTR bstrID, IUnknown* pUnk)
{
   if (!pUnk)
      return E_INVALIDARG;

   // First, assume bstrID is a string representing the CLSID 
   CLSID theCLSID = {0};
   HRESULT hr = CLSIDFromString(bstrID, &theCLSID);
   if (FAILED(hr))
   {
      // Now assume bstrID is a ProgID
      hr = CLSIDFromProgID(bstrID, &theCLSID);
      if (FAILED(hr))
         return hr;
   }

   // Use the system-supplied property frame
   return OleCreatePropertyFrame(
      GetActiveWindow(),   // Parent window of the property frame
      0,           // Horizontal position of the property frame
      0,           // Vertical position of the property frame
      bstrCaption, // Property frame caption
      1,           // Number of objects
      &pUnk,       // Array of IUnknown pointers for objects
      1,           // Number of property pages
      &theCLSID,   // Array of CLSIDs for property pages
      NULL,        // Locale identifier
      0,           // Reserved - 0
      NULL         // Reserved - 0
      );
}

创建宏

一旦生成项目,可以测试属性页和帮助器对象使用一个简单的宏您在Visual Studio开发环境中创建和运行。此宏将创建一个帮助器对象,然后调用其 ShowPage 方法使用 DocProperties 属性页和文档的 IUnknown 指针的ProgID当前活动在Visual Studio编辑器。用于此宏需要的代码如下所示:

Imports EnvDTE
Imports System.Diagnostics

Public Module AtlPages

    Public Sub Test()
        Dim Helper
        Helper = CreateObject("ATLPages7.Helper.1")

        On Error Resume Next
        Helper.ShowPage( _
            ActiveDocument.Name, _
            "ATLPages7Lib.DocumentProperties.1", _
            DTE.ActiveDocument _
            )
    End Sub

End Module

当您运行此宏,将显示显示文件名的属性页,并且当前活动的只读状态文本文档。文档的只读状态在开发环境中仅反映能够写入文档;它不影响磁盘上该文件的只读特性的。

请参见

概念

ATL COM属性页

ATLPages示例