Contoh: Menerapkan Halaman Properti

Catatan

Pustaka Templat Aktif (ATL) terus didukung. Namun, kami tidak lagi menambahkan fitur atau memperbarui dokumentasi.

Wizard ATL Property Page tidak tersedia di Visual Studio 2019 dan yang lebih baru.

Contoh ini menunjukkan cara membuat halaman properti yang menampilkan (dan memungkinkan Anda mengubah) properti antarmuka Kelas Dokumen.

Contohnya didasarkan pada sampel ATLPages.

Untuk menyelesaikan contoh ini, Anda akan:

Menambahkan Kelas Halaman Properti ATL

Pertama, buat proyek ATL baru untuk server DLL yang disebut ATLPages7. Sekarang gunakan ATL Property Page Wizard untuk membuat halaman properti. Beri halaman properti Nama PendekDocProperties lalu beralih ke halaman Strings untuk mengatur item spesifik halaman properti seperti yang ditunjukkan pada tabel di bawah ini.

Barang Nilai
Judul Dokumen Teks
String Dokumen Properti VCUE TextDocument
Helpfile <Kosong>

Nilai yang Anda tetapkan di halaman panduan ini akan dikembalikan ke kontainer halaman properti saat memanggil IPropertyPage::GetPageInfo. Apa yang terjadi pada string setelah itu tergantung pada kontainer, tetapi biasanya mereka akan digunakan untuk mengidentifikasi halaman Anda kepada pengguna. Judul biasanya akan muncul di tab di atas halaman Anda dan String Doc dapat ditampilkan di bilah status atau Tips Alat (meskipun kerangka properti standar tidak menggunakan string ini sama sekali).

Catatan

String yang Anda tetapkan di sini disimpan sebagai sumber daya string dalam proyek Anda oleh wizard. Anda dapat dengan mudah mengedit string ini menggunakan editor sumber daya jika Anda perlu mengubah informasi ini setelah kode untuk halaman Anda dibuat.

Klik OK agar wizard membuat halaman properti Anda.

Mengedit Sumber Daya Dialog

Sekarang setelah halaman properti Anda dibuat, Anda harus menambahkan beberapa kontrol ke sumber daya dialog yang mewakili halaman Anda. Tambahkan kotak edit, kontrol teks statis, dan kotak centang dan atur ID mereka seperti yang ditunjukkan di bawah ini:

Cuplikan layar sumber daya dialog di editor visual.

Kontrol ini akan digunakan untuk menampilkan nama file dokumen dan status baca-saja.

Catatan

Sumber daya dialog tidak menyertakan tombol bingkai atau perintah, juga tidak memiliki tampilan bertab yang mungkin Anda harapkan. Fitur-fitur ini disediakan oleh bingkai halaman properti seperti yang dibuat dengan memanggil OleCreatePropertyFrame.

Menambahkan Pengelola Pesan

Dengan kontrol yang sudah disiapkan, Anda dapat menambahkan handler pesan untuk memperbarui status kotor halaman saat nilai salah satu kontrol berubah.

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

Kode ini merespons perubahan yang dibuat pada kontrol edit atau kotak centang dengan memanggil IPropertyPageImpl::SetDirty, yang menginformasikan situs halaman bahwa halaman telah berubah. Biasanya halaman akan merespons dengan mengaktifkan atau menonaktifkan tombol Terapkan di bingkai halaman properti.

Catatan

Di halaman properti Anda sendiri, Anda mungkin perlu melacak dengan tepat properti mana yang telah diubah oleh pengguna sehingga Anda dapat menghindari pembaruan properti yang belum diubah. Contoh ini mengimplementasikan kode tersebut dengan melacak nilai properti asli dan membandingkannya dengan nilai saat ini dari UI saat waktunya untuk menerapkan perubahan.

Pemeliharaan

Sekarang tambahkan beberapa #import pernyataan ke DocProperties.h sehingga pengkompilasi tahu tentang Document antarmuka:

// 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

Anda juga harus merujuk ke IPropertyPageImpl kelas dasar; tambahkan yang berikut typedef ke CDocProperties kelas :

typedef IPropertyPageImpl<CDocProperties> PPGBaseClass;

Mengganti IPropertyPageImpl::SetObjects

Metode pertama IPropertyPageImpl yang perlu Anda ambil alih adalah SetObjects. Di sini Anda akan menambahkan kode untuk memastikan bahwa hanya satu objek yang diteruskan dan mendukung antarmuka Document yang Anda harapkan:

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

Catatan

Masuk akal untuk hanya mendukung satu objek untuk halaman ini karena Anda akan mengizinkan pengguna untuk mengatur nama file objek - hanya satu file yang dapat ada di satu lokasi.

Mengesampingkan IPropertyPageImpl::Activate

Langkah selanjutnya adalah menginisialisasi halaman properti dengan nilai properti objek dasar saat halaman pertama kali dibuat.

Dalam hal ini, Anda harus menambahkan anggota berikut ke kelas karena Anda juga akan menggunakan nilai properti awal untuk perbandingan saat pengguna halaman menerapkan perubahan mereka:

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

Implementasi kelas dasar metode Aktifkan bertanggung jawab untuk membuat kotak dialog dan kontrolnya, sehingga Anda dapat mengambil alih metode ini dan menambahkan inisialisasi Anda sendiri setelah memanggil kelas dasar:

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

Kode ini menggunakan metode COM antarmuka Document untuk mendapatkan properti yang Anda minati. Kemudian menggunakan pembungkus API Win32 yang disediakan oleh CDialogImpl dan kelas dasarnya untuk menampilkan nilai properti kepada pengguna.

Menggantikan IPropertyPageImpl::Apply

Ketika pengguna ingin menerapkan perubahan mereka ke objek, situs halaman properti akan memanggil metode Terapkan . Ini adalah tempat untuk melakukan kebalikan kode di Activate — sedangkan Activate memasukkan nilai dari objek dan menempatkannya ke kontrol pada halaman properti, Apply memasukkan nilai dari kontrol pada halaman properti dan menempatkannya ke objek.

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

Catatan

Pemeriksaan terhadap m_bDirty di awal implementasi ini adalah pemeriksaan awal untuk menghindari pembaruan objek yang tidak perlu jika Apply dipanggil lebih dari sekali. Pemeriksaan juga dilakukan terhadap setiap nilai properti untuk memastikan bahwa hanya perubahan yang menyebabkan panggilan metode ke Document.

Catatan

Document FullName mengekspos sebagai properti baca-saja. Untuk memperbarui nama file dokumen berdasarkan perubahan yang dilakukan pada halaman properti, Anda harus menggunakan Save metode untuk menyimpan file dengan nama yang berbeda. Dengan demikian, kode di halaman properti tidak perlu membatasi dirinya untuk mendapatkan atau mengatur properti.

Menampilkan Halaman Properti

Untuk menampilkan halaman ini, Anda perlu membuat objek pembantu sederhana. Objek pembantu akan menyediakan metode yang menyederhanakan OleCreatePropertyFrame API untuk menampilkan satu halaman yang terhubung ke satu objek. Pembantu ini akan dirancang sehingga dapat digunakan dari Visual Basic.

Gunakan kotak dialog Tambahkan Kelas dan Wizard Objek Sederhana ATL untuk menghasilkan kelas baru dan gunakan Helper sebagai nama pendeknya. Setelah dibuat, tambahkan metode seperti yang ditunjukkan pada tabel di bawah ini.

Barang Nilai
Nama Metode ShowPage
Parameter [in] BSTR bstrCaption, [in] BSTR bstrID, [in] IUnknown* pUnk

Parameter bstrCaption adalah keterangan yang akan ditampilkan sebagai judul kotak dialog. Parameter bstrID adalah string yang mewakili CLSID atau ProgID dari halaman properti yang akan ditampilkan. Parameter pUnk akan menjadi IUnknown pointer dari objek yang propertinya akan dikonfigurasi oleh halaman properti.

Terapkan metode seperti yang ditunjukkan di bawah ini:

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

Membuat Makro

Setelah membuat proyek, Anda dapat menguji halaman properti dan objek pembantu menggunakan makro sederhana yang dapat Anda buat dan jalankan di lingkungan pengembangan Visual Studio. Makro ini akan membuat objek pembantu, lalu memanggil metodenya ShowPage menggunakan ProgID dari halaman properti DocProperties dan penunjuk IUnknown dokumen yang saat ini aktif di editor Visual Studio. Kode yang Anda butuhkan untuk makro ini ditunjukkan di bawah ini:

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

Saat Anda menjalankan makro ini, halaman properti akan ditampilkan memperlihatkan nama file dan status baca-saja dari dokumen teks yang saat ini aktif. Status baca-saja dari dokumen hanya mencerminkan kemampuan untuk menulis ke dokumen di lingkungan pengembangan; ini tidak memengaruhi atribut baca-saja dari file pada disk.

Lihat juga

Halaman Properti
Sampel ATLPages