Esempio: implementazione di una pagina delle proprietà
La Creazione guidata pagina delle proprietà ATL non è disponibile in Visual Studio 2019 e versioni successive.
Questo esempio mostra come creare una pagina delle proprietà che permette di visualizzare (e modificare) le proprietà dell'interfaccia Classi documento.
Questo esempio è basato sull'esempio ATLPages.
Per completare questo esempio, è necessario eseguire queste operazioni:
Aggiungere la classe della pagina delle proprietà ATL usando la finestra di dialogo Aggiungi classe nella Creazione guidata pagina delle proprietà ATL.
Modificare la risorsa finestra di dialogo aggiungendo nuovi controlli per le proprietà interessanti dell'interfaccia
Document
.Aggiungere gestori di messaggi per mantenere il sito della pagina delle proprietà al corrente delle modifiche apportate dall'utente.
Aggiungere alcune istruzioni
#import
e un typedef nella sezione Gestione.Eseguire l'override di IPropertyPageImpl::SetObjects per convalidare gli oggetti passati alla pagina delle proprietà.
Eseguire l'override di IPropertyPageImpl::Activate per inizializzare l'interfaccia della pagina delle proprietà.
Eseguire l'override di IPropertyPageImpl::Apply per aggiornare l'oggetto con i valori più recenti delle proprietà.
Visualizzare la pagina delle proprietà creando un semplice oggetto helper.
Creare una macro che testerà la pagina delle proprietà.
Aggiunta della classe della pagina delle proprietà ATL
Prima di tutto, creare un nuovo progetto ATL per un server DLL denominato ATLPages7
. Usare ora la Creazione guidata pagina delle proprietà ATL per generare una pagina delle proprietà. In Nome breve assegnare alla pagina delle proprietà il nome DocProperties, quindi passare alla pagina Stringhe per impostare gli elementi specifici della pagina delle proprietà come mostrato nella tabella seguente.
Articolo | valore |
---|---|
Title | TextDocument |
Doc String | Proprietà VCUE TextDocument |
Helpfile | <blank> |
I valori impostati in questa pagina della procedura guidata verranno restituiti al contenitore della pagina delle proprietà quando chiama IPropertyPage::GetPageInfo
. Ciò che succede alle stringhe dopo questo momento dipende dal contenitore, ma in genere vengono usate per identificare la pagina per l'utente. Il titolo verrà visualizzato in genere in una scheda sopra la pagina e la stringa doc può essere visualizzata in una barra di stato o una descrizione comando (anche se la finestra delle proprietà standard non usa affatto questa stringa).
Nota
Le stringhe impostate qui vengono archiviate come risorse stringa nel progetto dalla procedura guidata. È possibile modificare facilmente queste stringhe usando l'editor di risorse, se è necessario modificare queste informazioni dopo la generazione del codice per la pagina.
Fare clic su OK in modo che la procedura guidata generi la pagina delle proprietà.
Modifica della risorsa finestra di dialogo
Ora che è stata generata la pagina delle proprietà, è necessario aggiungere alcuni controlli alla risorsa finestra di dialogo che rappresenta la pagina. Aggiungere una casella di modifica, un controllo di testo statico e una casella di controllo e impostarne gli ID, come mostrato di seguito:
Questi controlli verranno usati per visualizzare il nome file del documento e il relativo stato di sola lettura.
Nota
La risorsa finestra di dialogo non include un frame o comandi di pulsanti, né ha l'aspetto a schede che ci si potrebbe aspettare. Queste funzionalità vengono fornite da una finestra della pagina delle proprietà, ad esempio quella creata chiamando OleCreatePropertyFrame.
Aggiunta di gestori di messaggi
Dopo aver predisposto i controlli, è possibile aggiungere gestori di messaggi per aggiornare lo stato modificato ma non salvato della pagina quando il valore di uno dei controlli cambia:
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;
}
Questo codice risponde alle modifiche apportate al controllo di modifica o alla casella di controllo chiamando IPropertyPageImpl::SetDirty, che indica al sito della pagina che la pagina è stata modificata. In genere il sito della pagina risponderà abilitando o disabilitando un pulsante Applica nella finestra della pagina proprietà.
Nota
Nelle pagine delle proprietà può essere necessario tenere traccia esattamente delle proprietà modificate dall'utente, in modo da evitare l'aggiornamento di proprietà che non sono state modificate. Questo esempio implementa il codice per tenere traccia dei valori originali delle proprietà e confrontarli con i valori correnti dell'interfaccia utente al momento di applicare le modifiche.
Gestione
Aggiungere ora un paio di istruzioni #import
a DocProperties.h in modo che il compilatore riconosca l'interfaccia 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
È anche necessario fare riferimento alla IPropertyPageImpl
classe di base. Aggiungere quanto segue typedef
alla CDocProperties
classe :
typedef IPropertyPageImpl<CDocProperties> PPGBaseClass;
Override di IPropertyPageImpl::SetObjects
Il primo metodo IPropertyPageImpl
di cui è necessario eseguire l'override è SetObjects. Qui verrà aggiunto il codice per verificare che sia stato passato un solo oggetto e che questo supporti l'interfaccia Document
prevista:
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;
}
Nota
È logico supportare un solo oggetto per questa pagina perché si permetterà all'utente di impostare il nome file dell'oggetto e può essere presente un solo file in ognuna delle posizioni.
Override di IPropertyPageImpl::Activate
Il passaggio successivo consiste nell'inizializzare la pagina delle proprietà con valori di proprietà dell'oggetto sottostante, quando la pagina viene creata per la prima volta.
In questo caso, è necessario aggiungere i membri seguenti alla classe, in quanto si useranno anche i valori di proprietà iniziali per il confronto quando gli utenti della pagina applicheranno le proprie modifiche:
CComBSTR m_bstrFullName; // The original name
VARIANT_BOOL m_bReadOnly; // The original read-only state
L'implementazione della classe base del metodo Activate è responsabile della creazione della finestra di dialogo e dei relativi controlli e di conseguenza è possibile eseguire l'override di questo metodo e aggiungere la propria inizializzazione dopo la chiamata della classe base:
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;
}
Questo codice usa i metodi COM dell'interfaccia Document
per ottenere le proprietà desiderate. Usa quindi i wrapper API Win32 forniti da CDialogImpl e le relative classi base per visualizzare i valori delle proprietà all'utente.
Override di IPropertyPageImpl::Apply
Quando gli utenti vogliono applicare modifiche agli oggetti, il sito della pagina delle proprietà chiama il metodo Apply. È qui che è necessario eseguire l'operazione opposta del codice in Activate
: mentre Activate
ha usato i valori dall'oggetto per eseguirne il push nei controlli nella pagina delle proprietà, Apply
usa i valori dai controlli nella pagina delle proprietà per eseguirne il push nell'oggetto.
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;
}
Nota
Il controllo rispetto a m_bDirty all'inizio di questa implementazione è un controllo iniziale per evitare aggiornamenti non necessari degli oggetti se Apply
viene chiamato più volte. Sono inoltre disponibili controlli riguardo a ognuno dei valori di proprietà per garantire che solo le modifiche generino una chiamata del metodo a Document
.
Nota
Document
espone FullName
come proprietà di sola lettura. Per aggiornare il nome file del documento in base alle modifiche apportate alla pagina delle proprietà, è necessario usare il metodo Save
per salvare il file con un nome diverso. Di conseguenza, il codice in una pagina delle proprietà non deve limitarsi a ottenere o impostare le proprietà.
Visualizzazione della pagina delle proprietà
Per visualizzare questa pagina, è necessario creare un semplice oggetto helper. L'oggetto helper fornirà un metodo che semplifica l'API OleCreatePropertyFrame
per la visualizzazione di una singola pagina connessa a un singolo oggetto. Questo helper verrà progettato perché possa essere usato da Visual Basic.
Usare la finestra di dialogo Aggiungi classe e la Creazione guidata oggetto semplice ATL per generare una nuova classe e usare Helper
come nome breve. Una volta creata la classe, aggiungere un metodo come mostrato nella tabella seguente.
Articolo | Valore |
---|---|
Nome metodo | ShowPage |
Parametri | [in] BSTR bstrCaption, [in] BSTR bstrID, [in] IUnknown* pUnk |
Il parametro bstrCaption è la didascalia da visualizzare come titolo della finestra di dialogo. Il parametro bstrID è una stringa che rappresenta un valore CLSID o ProgID della pagina delle proprietà da visualizzare. Il parametro pUnk sarà il puntatore IUnknown
dell'oggetto le cui proprietà verranno configurate dalla pagina delle proprietà.
Implementare il metodo come mostrato di seguito:
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
);
}
Creazione di una macro
Dopo avere compilato il progetto, è possibile testare la pagina delle proprietà e l'oggetto helper tramite una semplice macro che può essere creata ed eseguita nell'ambiente di sviluppo di Visual Studio. Questa macro creerà un oggetto helper, quindi chiamerà il relativo metodo ShowPage
tramite il valore ProgID della pagina delle proprietà DocProperties e il puntatore IUnknown
del documento attualmente attivo nell'editor di Visual Studio. Il codice necessario per questa macro viene mostrato di seguito:
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
Quando si esegue questa macro, verrà visualizzata la pagina delle proprietà che mostra il nome file e lo stato di sola lettura del documento di testo attualmente attivo. Lo stato di sola lettura del documento riflette solo la possibilità di scrivere nel documento nell'ambiente di sviluppo, ma non influisce sull'attributo di sola lettura del file su disco.