共用方式為


範例:實作屬性頁

這個範例顯示如何建立會顯示的屬性頁 (並允許變更) 文件類別 介面的屬性。 這個介面是由 Visual Studio 中的資料 通用環境物件模型範例 公開 (雖然您所建立的屬性頁不在乎物件來操作來源,只要支援正確的介面)。

這個範例會根據 ATLPages 範例

若要完成這個範例,您預期:

  • 使用加入類別對話方塊和 ATL 屬性頁精靈的加入 ATL 屬性頁類別 。

  • 編輯對話方塊資源 將 文件 介面的有趣的屬性的新控制項。

  • 將屬性頁面網站使用者所做的將訊息處理常式 被告知的變更。

  • 加入一些 #import 陳述式和一個 typedef 在 環境維護 部分。

  • 驗證物件的覆寫 IPropertyPageImpl::SetObjects 傳遞至屬性頁。

  • 初始化屬性頁之介面的覆寫 IPropertyPageImpl::Activate 。

  • 更新和最新的屬性值之物件的覆寫 IPropertyPageImpl::Apply 。

  • 顯示屬性頁 會引導您建立簡單的 Helper 物件。

  • 將測試屬性頁的建立巨集 。

加入 ATL 屬性頁類別

首先,請建立名為 ATLPages7DLL 的伺服器上建立新的 ATL 專案。 現在使用 ATL 屬性頁精靈 產生屬性頁。 將 屬性頁 DocProperties [簡短名稱] 然後切換至 字串 網頁對集合屬性頁指定項目如下表所示。

項目

標題

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,告知控制網站頁面已變更。 通常頁面網站會透過啟用或停用 屬性頁上方塊中 套用 按鈕回應。

注意事項注意事項

在您的屬性頁,屬性可由使用者修改的可能需要精確地記錄,好讓您可以避免更新未變更的屬性。藉由記錄屬性值及其程式碼與 UI 的目前值比較的範例實作,當為時套用變更。

環境維護

現在加入兩個 #import 陳述式加入至 DocProperties.h,讓編譯器知道 文件 介面:

// 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。 您將加入程式碼來檢查只能有一個傳遞的物件,而且支援您預期的 文件 介面:

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

啟動 方法的基底類別實作會建立對話方塊和控制項負責,因此,您可以覆寫這個方法與呼叫基底類別 (Base Class) 之後將初始化:

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;
}

這個程式碼會使用 文件 介面的 COM 方法取得屬性您所要的。 然後會使用 CDialogImpl 及其基底類別所提供的 Win32 API 包裝函式會顯示屬性值給使用者。

覆寫 IPropertyPageImpl::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 檢查這個實作的開頭是避免不必要的物件更新的初始檢查 套用 是否已多次呼叫。也會檢查以確保每個的屬性值變更只會使方法呼叫 文件

注意事項注意事項

文件FullName 公開為唯讀屬性。若要更新根據變更的文件的檔名對屬性頁,您必須使用 [儲存] 方法儲存具有不同名稱的檔案。因此, 屬性頁上的程式碼不需要自我限制為取得或設定屬性。

顯示屬性頁

若要顯示這個頁面,您必須建立一個簡單的 Helper 物件。 Helper 物件會提供簡化的單一頁面上顯示 OleCreatePropertyFrame API 連接至單一物件的方法。 這個 Helper 將所設計,以便從 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
      );
}

建立巨集

一旦建立了專案,您可以測試屬性頁和 Helper 物件使用簡單巨集可以在 Visual Studio 開發環境中建置和執行。 這個巨集就會建立 Helper 物件,然後呼叫其方法 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

當您執行此巨集,將只會顯示目前使用中的 Word 文件的檔案名稱和唯讀狀態的屬性頁。 文件的唯讀狀態只反映了文件撰寫在開發環境中;它不會影響檔案的唯讀屬性在磁碟上。

請參閱

概念

ATL COM 屬性頁

ATLPages 範例