Condividi tramite


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:

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:

Screenshot of a dialog resource in the visual editor.

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.

Vedi anche

Pagine delle proprietà
Esempio ATLPages