範例:實作屬性頁
這個範例顯示如何建立會顯示的屬性頁 (並允許變更) 文件類別 介面的屬性。 這個介面是由 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 如下所示:
這些控制項會用來顯示文件和它的唯讀狀態的檔案名稱。
注意事項 |
---|
對話方塊資源不包含框架或命令按鈕,也不會有您可能預期的索引標籤的外觀。這些功能都是由一個屬性頁方塊提供例如呼叫所建立的執行個體 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 文件的檔案名稱和唯讀狀態的屬性頁。 文件的唯讀狀態只反映了文件撰寫在開發環境中;它不會影響檔案的唯讀屬性在磁碟上。