Udostępnij przez


Przykład: Implementowanie strony właściwości

Kreator strony właściwości ATL nie jest dostępny w programie Visual Studio 2019 i nowszych wersjach.

W tym przykładzie pokazano, jak utworzyć stronę właściwości wyświetlającą (i umożliwiającą zmianę) właściwości interfejsu Klasy dokumentów.

Przykład jest oparty na przykładzie ATLPages.

Aby ukończyć ten przykład, wykonasz następujące czynności:

Dodawanie klasy strony właściwości ATL

Najpierw utwórz nowy projekt ATL dla serwera DLL o nazwie ATLPages7. Teraz użyj Kreatora strony właściwości ATL, aby wygenerować stronę właściwości. Nadaj stronie właściwości krótką nazwę dokumentu, a następnie przejdź do strony Ciągi, aby ustawić elementy specyficzne dla właściwości, jak pokazano w poniższej tabeli.

Towar Wartość
Tytuł Dokument tekstowy
Ciąg dokumentu Właściwości dokumentu tekstowego VCUE
Plik pomocy <puste>

Wartości ustawione na tej stronie kreatora zostaną zwrócone do kontenera strony właściwości po wywołaniu IPropertyPage::GetPageInfoelementu . Co się stanie z ciągami po tym, zależy od kontenera, ale zazwyczaj będą one używane do identyfikowania strony dla użytkownika. Tytuł będzie zwykle wyświetlany na karcie nad stroną, a ciąg dokumentu może być wyświetlany na pasku stanu lub w etykietce narzędzia (chociaż standardowa ramka właściwości w ogóle nie używa tego ciągu).

Uwaga

Ciągi ustawione w tym miejscu są przechowywane jako zasoby ciągów w projekcie przez kreatora. Możesz łatwo edytować te ciągi przy użyciu edytora zasobów, jeśli chcesz zmienić te informacje po wygenerowaniu kodu dla strony.

Kliknij przycisk OK , aby kreator wygenerował stronę właściwości.

Edytowanie zasobu okna dialogowego

Po wygenerowaniu strony właściwości należy dodać kilka kontrolek do zasobu okna dialogowego reprezentującego stronę. Dodaj pole edycji, kontrolkę tekstu statycznego oraz pole wyboru i ustaw ich identyfikatory, jak pokazano poniżej:

Zrzut ekranu przedstawiający zasób okna dialogowego w edytorze wizualizacji.

Te kontrolki będą używane do wyświetlania nazwy pliku dokumentu i stanu tylko do odczytu.

Uwaga

Zasób okna dialogowego nie zawiera przycisków ramek ani poleceń ani nie ma na nim karty wyglądu, którego można się spodziewać. Te funkcje są udostępniane przez ramkę strony właściwości, taką jak ta utworzona przez wywołanie elementu OleCreatePropertyFrame.

Dodawanie programów obsługi komunikatów

Za pomocą kontrolek można dodawać programy obsługi komunikatów w celu zaktualizowania stanu zanieczyszczonej strony, gdy wartość jednej z kontrolek ulegnie zmianie:

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

Ten kod odpowiada na zmiany wprowadzone w kontrolce edycji lub pole wyboru, wywołując metodę IPropertyPageImpl::SetDirty, która informuje witrynę strony o zmianie. Zazwyczaj witryna strony odpowiada, włączając lub wyłączając przycisk Zastosuj na ramce strony właściwości.

Uwaga

Na własnych stronach właściwości może być konieczne śledzenie dokładnie, które właściwości zostały zmienione przez użytkownika, aby uniknąć aktualizowania właściwości, które nie zostały zmienione. Ten przykład implementuje ten kod, śledząc oryginalne wartości właściwości i porównując je z bieżącymi wartościami z interfejsu użytkownika, gdy nadszedł czas na zastosowanie zmian.

Konserwacja

Teraz dodaj kilka instrukcji #import do pliku DocProperties.h, aby kompilator wiedział o interfejsie 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

Należy również odwołać się do klasy bazowej IPropertyPageImpltypedef. Dodaj następujące CDocProperties elementy do klasy:

typedef IPropertyPageImpl<CDocProperties> PPGBaseClass;

Zastępowanie obiektu IPropertyPageImpl::SetObjects

Pierwszą IPropertyPageImpl metodą, którą należy zastąpić, jest SetObjects. W tym miejscu dodasz kod, aby sprawdzić, czy przekazano tylko jeden obiekt i czy obsługuje Document oczekiwany interfejs:

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

Uwaga

Warto obsługiwać tylko jeden obiekt dla tej strony, ponieważ pozwolisz użytkownikowi ustawić nazwę pliku obiektu — tylko jeden plik może istnieć w dowolnej lokalizacji.

Zastępowanie elementu IPropertyPageImpl::Activate

Następnym krokiem jest zainicjowanie strony właściwości przy użyciu wartości właściwości obiektu bazowego po pierwszym utworzeniu strony.

W takim przypadku należy dodać następujące elementy członkowskie do klasy, ponieważ użyjesz również początkowych wartości właściwości do porównania, gdy użytkownicy strony zastosują swoje zmiany:

CComBSTR m_bstrFullName;  // The original name
VARIANT_BOOL m_bReadOnly; // The original read-only state

Implementacja klasy bazowej metody Activate jest odpowiedzialna za utworzenie okna dialogowego i jego kontrolek, dzięki czemu można zastąpić tę metodę i dodać własną inicjację po wywołaniu klasy bazowej:

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

Ten kod używa metod COM interfejsu Document , aby uzyskać interesujące Cię właściwości. Następnie używa otoek interfejsu API Win32 dostarczonego przez aplikację CDialogImpl i jej klas bazowych, aby wyświetlić wartości właściwości użytkownikowi.

Zastępowanie elementu IPropertyPageImpl::Apply

Gdy użytkownicy chcą zastosować zmiany do obiektów, witryna strony właściwości wywoła metodę Apply . Jest to miejsce, w których należy wykonać odwrotność kodu Activate — podczas gdy Activate pobierało wartości z obiektu i wypychało je do kontrolek na stronie właściwości, Apply pobiera wartości z kontrolek na stronie właściwości i wypycha je do obiektu.

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

Uwaga

Sprawdzanie względem m_bDirty na początku tej implementacji jest początkowym sprawdzaniem, aby uniknąć niepotrzebnych aktualizacji obiektów, jeśli Apply jest wywoływany więcej niż raz. Istnieją również kontrole względem każdej wartości właściwości, aby upewnić się, że tylko zmiany powodują wywołanie metody do Documentklasy .

Uwaga

Document uwidacznia FullName jako właściwość tylko do odczytu. Aby zaktualizować nazwę pliku dokumentu na podstawie zmian wprowadzonych na stronie właściwości, należy użyć Save metody w celu zapisania pliku pod inną nazwą. W związku z tym kod na stronie właściwości nie musi ograniczać się do pobierania lub ustawiania właściwości.

Wyświetlanie strony właściwości

Aby wyświetlić tę stronę, należy utworzyć prosty obiekt pomocnika. Obiekt pomocnika udostępnia metodę, która upraszcza OleCreatePropertyFrame interfejs API do wyświetlania pojedynczej strony połączonej z pojedynczym obiektem. Ten pomocnik zostanie zaprojektowany tak, aby mógł być używany z poziomu języka Visual Basic.

Użyj okna dialogowego Dodawanie klasy i Kreatora prostego obiektu ATL, aby wygenerować nową klasę i użyć Helper jej jako krótkiej nazwy. Po utworzeniu dodaj metodę, jak pokazano w poniższej tabeli.

Towar Wartość
Nazwa metody ShowPage
Parametry [in] BSTR bstrCaption, [in] BSTR bstrID, [in] IUnknown* pUnk

Parametr bstrCaption to podpis, który ma być wyświetlany jako tytuł okna dialogowego. Parametr bstrID to ciąg reprezentujący identyfikator CLSID lub identyfikator ProgID strony właściwości do wyświetlenia. Parametr pUnk będzie wskaźnikiem IUnknown obiektu, którego właściwości zostaną skonfigurowane przez stronę właściwości.

Zaimplementuj metodę, jak pokazano poniżej:

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

Tworzenie makra

Po skompilowania projektu możesz przetestować stronę właściwości i obiekt pomocnika przy użyciu prostego makra, które można utworzyć i uruchomić w środowisku projektowym programu Visual Studio. To makro utworzy obiekt pomocnika, a następnie wywoła metodę ShowPage przy użyciu identyfikatora ProgID strony właściwości DocProperties i IUnknown wskaźnika dokumentu aktualnie aktywnego w edytorze programu Visual Studio. Poniższy kod potrzebny do tego makra:

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

Po uruchomieniu tego makra zostanie wyświetlona strona właściwości przedstawiająca nazwę pliku i stan tylko do odczytu aktualnie aktywnego dokumentu tekstowego. Stan tylko do odczytu dokumentu odzwierciedla możliwość zapisu w dokumencie w środowisku projektowym; nie ma wpływu na atrybut tylko do odczytu pliku na dysku.

Zobacz też

Strony właściwości
Przykład ATLPages