Beispiel: Implementieren einer Eigenschaftenseite
Der ATL-Eigenschaftenseiten-Assistent ist in Visual Studio 2019 und höher nicht verfügbar.
Dieses Beispiel zeigt, wie Sie eine Eigenschaftenseite erstellen, auf der Eigenschaften der Document Classes-Schnittstelle angezeigt werden (und auf der Sie die Eigenschaften ändern können).
Das Beispiel basiert auf dem ATLPages-Beispiel.
In diesem Beispiel führen Sie folgende Aktionen aus:
Hinzufügen der Klasse für ATL-Eigenschaftenseiten mithilfe des Dialogfelds „Klasse hinzufügen“ und des ATL-Eigenschaftenseiten-Assistenten.
Bearbeiten der Dialogfeldressource durch Hinzufügen neuer Steuerelemente für interessante Eigenschaften der
Document
-Schnittstelle.Hinzufügen von Meldungshandlern, um die Eigenschaftenseite über Änderungen zu informieren, die von Benutzern vorgenommen wurden.
Hinzufügen einiger
#import
-Anweisungen und einer typedef im Abschnitt Housekeeping.Überschreiben von „IPropertyPageImpl::SetObjects“, um die Objekte zu überprüfen, die an die Eigenschaftenseite übergeben werden.
Überschreiben von „IPropertyPageImpl::Activate“, um die Schnittstelle der Eigenschaftenseite zu initialisieren.
Überschreiben von IPropertyPageImpl::Apply, um das Objekt mit den neuesten Eigenschaftswerten zu aktualisieren.
Anzeigen der Eigenschaftenseite durch Erstellen eines einfachen Hilfsobjekts.
Erstellen eines Makros, das die Eigenschaftenseite testet.
Hinzufügen der ATL-Eigenschaftsseitenklasse
Zunächst erstellen Sie ein neues ATL-Projekt für einen DLL-Server namens ATLPages7
. Danach verwenden Sie den ATL-Eigenschaftenseiten-Assistenten, um eine Eigenschaftenseite zu generieren. Weisen Sie der Eigenschaftenseite den KurznamenDocProperties zu, und wechseln Sie dann zur Seite Zeichenfolgen, um für die Eigenschaftenseite spezifische Elemente festzulegen, wie in der unten stehenden Tabelle gezeigt.
Element | Wert |
---|---|
Titel | TextDocument |
Dokumentzeichenfolge | VCUE TextDocument Properties |
Hilfedatei | <leer> |
Die Werte, die Sie auf dieser Seite des Assistenten festlegen, werden an den Container der Eigenschaftenseite zurückgegeben, wenn dieser IPropertyPage::GetPageInfo
aufruft. Was danach mit den Zeichenfolgen geschieht, hängt vom Container ab, aber in der Regel werden sie verwendet, um Ihre Seite für den Benutzer zu identifizieren. Der Titel wird üblicherweise auf einer Registerkarte über Ihrer Seite angezeigt, die Dokumentzeichenfolge in einer Statusleiste oder QuickInfo (allerdings verwendet der Frame für Standardeigenschaften diese Zeichenfolge gar nicht).
Hinweis
Die Zeichenfolgen, die Sie hier festlegen, werden vom Assistenten als Zeichenfolgenressourcen in Ihrem Projekt gespeichert. Sie können diese Zeichenfolgen ganz einfach mit dem Ressourcen-Editor bearbeiten, wenn Sie diese Informationen ändern müssen, nachdem der Code für Ihre Seite generiert wurde.
Klicken Sie auf OK, damit der Assistent Ihre Eigenschaftenseite generiert.
Bearbeiten der Dialogressource
Nach dem Generieren der Eigenschaftenseite müssen Sie der Dialogfeldressource, die Ihre Seite repräsentiert, einige Steuerelemente hinzufügen. Fügen Sie ein Bearbeitungsfeld, ein statisches Textsteuerelement und ein Kontrollkästchen hinzu, und legen Sie die IDs wie im Folgenden gezeigt fest:
Mit diesen Steuerelementen werden der Dateiname und der schreibgeschützte Status des Dokuments angezeigt.
Hinweis
Die Dialogfeldressource enthält weder einen Frame noch Befehlsschaltflächen und weist auch nicht Registerkartenformat auf, das Sie möglicherweise erwartet haben. Diese Features werden durch einen Eigenschaftenseitenframe bereitgestellt, z.B. durch den, der durch den Aufruf von OleCreatePropertyFrame erstellt wird.
Hinzufügen von Nachrichtenhandlern
Wenn die Steuerelemente vorhanden sind, können Sie Meldungshandler hinzufügen, um den geänderten Status der Seite zu aktualisieren, wenn sich der Wert eines der Steuerelemente ändert:
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;
}
Dieser Code reagiert auf Änderungen am Bearbeitungssteuerelement oder Kontrollkästchen durch Aufruf von IPropertyPageImpl::SetDirty, wodurch die Site der Seite darüber informiert wird, dass die Seite sich geändert hat. In der Regel reagiert die Site der Seite durch Aktivieren oder Deaktivieren einer Anwenden-Schaltfläche im Frame der Eigenschaftenseite.
Hinweis
Auf Ihren eigenen Eigenschaftenseiten müssen Sie möglicherweise nachverfolgen, welche Eigenschaften genau vom Benutzer geändert wurden, um zu vermeiden, dass Eigenschaften aktualisiert werden, die sich nicht geändert haben. Dieses Beispiel implementiert diesen Code, indem die ursprünglichen Eigenschaftswerte nachverfolgt und mit den aktuellen Werten der Benutzeroberfläche verglichen werden, wenn die Änderungen angewendet werden sollen.
Housekeeping
Fügen Sie jetzt einige #import
-Anweisungen zu „DocProperties.h“ hinzu, sodass der Compiler die Document
-Schnittstelle kennt:
// 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
Sie müssen auch auf die IPropertyPageImpl
Basisklasse verweisen. Fügen Sie der Klasse Folgendes typedef
hinzu CDocProperties
:
typedef IPropertyPageImpl<CDocProperties> PPGBaseClass;
Überschreiben von IPropertyPageImpl::SetObjects
Die erste IPropertyPageImpl
-Methode, die Sie überschreiben müssen, ist SetObjects. Hier fügen Sie Code hinzu, um zu überprüfen, ob nur ein einzelnes Objekt übergeben wurde und dass es die Document
-Schnittstelle unterstützt, die Sie erwarten:
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;
}
Hinweis
Es ist sinnvoll, für diese Seite nur ein einzelnes Objekt zu unterstützen, da Sie dem Benutzer erlauben, den Dateinamen des Objekts festzulegen – und an jedem Speicherort kann nur eine einzige Daten vorhanden sein.
Überschreiben von IPropertyPageImpl::Activate
Der nächste Schritt besteht darin, die Eigenschaftenseite mit den Eigenschaftswerten des zugrunde liegenden Objekts zu initialisieren, wenn die erste Seite erstellt wird.
In diesem Fall sollten Sie die folgenden Member zur Klasse hinzufügen, da Sie auch die anfänglichen Eigenschaftswerte verwenden, um Vergleiche durchzuführen, wenn Benutzer der Seite ihre Änderungen anwenden:
CComBSTR m_bstrFullName; // The original name
VARIANT_BOOL m_bReadOnly; // The original read-only state
Die Implementierung der Basisklasse der Activate-Methode ist für das Erstellen des Dialogfelds und der zugehörigen Steuerelemente zuständig, daher können Sie diese Methode überschreiben und nach dem Aufruf der Basisklasse Ihre eigene Initialisierung hinzufügen:
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;
}
Dieser Code verwendet die COM-Methoden der Document
-Schnittstelle, um die Eigenschaften abzurufen, die Sie interessieren. Dann verwendet der Code die Win32-API-Wrapper, die von CDialogImpl und den zugehörigen Basisklassen bereitgestellt werden, um dem Benutzer die Eigenschaftswerte anzuzeigen.
Überschreiben von IPropertyPageImpl::Apply
Wenn Benutzer ihre Änderungen an den Objekten anwenden möchten, ruft die Site der Eigenschaftenseite die Methode Apply auf. Hier erfolgt jetzt das Gegenteil des Codes in Activate
: Während Activate
Werte aus dem Objekt akzeptiert und an die Steuerelemente auf der Eigenschaftenseite übermittelt hat, akzeptiert Apply
Werte aus den Steuerelementen auf der Eigenschaftenseite und übermittelt sie an das Objekt.
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;
}
Hinweis
Die Prüfung auf m_bDirty am Anfang dieser Implementierung ist eine anfängliche Prüfung, um unnötige Aktualisierungen der Objekte zu vermeiden, wenn Apply
mehr als einmal aufgerufen wird. Es gibt auch Prüfungen für jeden der Eigenschaftswerte, um sicherzustellen, dass nur Änderungen zu einem Methodenaufruf von Document
führen.
Hinweis
Document
macht FullName
als schreibgeschützte Eigenschaft verfügbar. Um den Dateinamen des Dokuments basierend auf Änderungen zu aktualisieren, die an der Eigenschaftenseite vorgenommen wurden, müssen Sie die Save
-Methode verwenden, um die Datei unter einem anderen Namen zu speichern. Daher sollte sich der Code auf einer Eigenschaftenseite nicht auf das Abrufen und Festlegen von Eigenschaften beschränken.
Anzeigen der Eigenschaftenseite
Um diese Seite anzuzeigen, müssen Sie ein einfaches Hilfsobjekt erstellen. Das Hilfsobjekt bietet eine Methode, die die OleCreatePropertyFrame
-API zum Anzeigen einer einzelnen Seite vereinfacht, die mit einem einzelnen Objekt verknüpft ist. Dieses Hilfsobjekt ist so konzipiert, dass es mit Visual Basic verwendet werden kann.
Verwenden Sie das Dialogfeld „Klasse hinzufügen“ und den ATL-Assistenten für einfache Objekte, um eine neue Klasse zu generieren, und verwenden Sie Helper
als Kurznamen. Nach dem Erstellen fügen Sie eine Methode hinzu, wie in der unten stehenden Tabelle gezeigt.
Element | Wert |
---|---|
Methodenname | ShowPage |
Parameter | [in] BSTR bstrCaption, [in] BSTR bstrID, [in] IUnknown* pUnk |
Der bstrCaption-Parameter ist die Beschriftung, die als Titel des Dialogfelds angezeigt werden soll. Der bstrID-Parameter ist eine Zeichenfolge, die entweder eine CLSID oder eine ProgID der anzuzeigenden Eigenschaftenseite repräsentiert. Der pUnk-Parameter ist der IUnknown
-Zeiger des Objekts, dessen Eigenschaften durch die Eigenschaftenseite konfiguriert werden.
Implementieren Sie die unten gezeigte Methode:
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
);
}
Erstellen eines Makros
Nachdem Sie das Projekt erstellt haben, können Sie die Eigenschaftenseite und das Hilfsobjekt mit einem einfachen Makro testen, das Sie in der Visual Studio-Entwicklungsumgebung erstellen und ausführen. Dieses Makro erstellt ein Hilfsobjekt, und ruft dann die ShowPage
-Methode auf; dabei verwendet das Makro die ProgID der Eigenschaftenseite DocProperties und den IUnknown
-Zeiger des derzeit im Visual Studio-Editor aktiven Dokuments. Hier sehen Sie den für dieses Makro erforderlichen Code:
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
Wenn Sie dieses Makro ausführen, wird die Eigenschaftenseite mit dem Dateinamen und dem schreibgeschützten Status des derzeit aktiven Textdokuments angezeigt. Der schreibgeschützte Status des Dokuments spiegelt nur die Möglichkeit wider, in der Entwicklungsumgebung in das Dokument zu schreiben, und wirkt sich nicht auf das Schreibschutzattribut der Datei auf dem Datenträger aus.