Example: Implementing a Property Page
Este ejemplo muestra cómo compilar una página de propiedades que muestre (y permite cambiar) las propiedades de la interfaz de Clases de documento . Esta interfaz es expuesta por los documentos en Ejemplos del modelo de objetos de entorno común de Visual Studio (aunque la página de propiedades que creará no cuidará de donde los objetos que manipulan alcanzado mientras admiten la interfaz correcta).
el ejemplo se basa en Ejemplo ATLPages.
Para completar este ejemplo, debe:
Agregue la clase de la página de propiedades ATL mediante el cuadro de diálogo agregar clase y el Asistente para páginas de propiedades ATL.
Edite el recurso de cuadro de diálogo agregando nuevos controles para las propiedades interesantes de la interfaz de Documento .
Agregue controladores de mensajes para mantener el sitio de la página de propiedades a los cambios realizados por el usuario.
Agregue algunas instrucciones de #import y una definición en la sección de mantenimiento .
Reemplazo IPropertyPageImpl:: SetObjects para validar los objetos que se pasan a la página de propiedades.
Reemplazo IPropertyPageImpl:: Activar para inicializar la interfaz de la página de propiedades.
Reemplazo IPropertyPageImpl:: Aplicar para actualizar el objeto con los últimos valores de propiedad.
Abra la página de propiedades creando un objeto simple auxiliares.
cree una macro que va a la página de propiedades.
Agregar una clase de la página de propiedades ATL
Primero, cree un nuevo proyecto ATL para un servidor de una DLL denominada ATLPages7. ahora utilice Asistente para páginas de propiedades ATL para generar una página de propiedades. Dé a la página de propiedades nombre corto de DocProperties después a Cadenas la página para establecer elementos propiedad-página-específicos tal y como se muestra en la siguiente tabla.
Elemento |
Valor |
---|---|
Título |
TextDocument |
Cadena de Documento |
propiedades de VCUE TextDocument |
Helpfile |
<espacio en blanco> |
Los valores que establece en esta página del asistente devolverán al contenedor de la página de propiedades cuando llama a IPropertyPage:: GetPageInfo. Lo que sucede con las cadenas después que depende del contenedor, pero se utilizan normalmente para identificar la página al usuario. El título aparecerá normalmente en una ficha a la página y la cadena de Documento se puede mostrar en una barra de estado o una información sobre herramientas (aunque el cuadro estándar de la propiedad no utiliza esta cadena en absoluto).
Nota
Las cadenas que el asistente establece aquí almacena como recursos de cadena en el proyecto.Es fácil modificar estas cadenas utilizando el editor de recursos si necesita cambiar esta información después del código de la página se ha generado.
Haga clic Aceptar para que el asistente genere la página de propiedades.
Editar el recurso de cuadro de diálogo
Ahora que se ha generado la página de propiedades, necesitará agregar controles al recurso de cuadro de diálogo que representa la página. Agregue un cuadro de edición, un control de texto estático, y una casilla y establezca su id. como se muestra a continuación:
Estos controles se utilicen para mostrar el nombre de archivo de documento y su estado de sólo lectura.
Nota
El recurso de cuadro de diálogo no incluye un marco o los botones de comando, ni tampoco tabular tengan que puede haber esperado.Estas características son proporcionadas por un marco de página de propiedades como el que se creó llamando a OleCreatePropertyFrame.
Controladores de mensajes de suma
Con el nombre de los controles, puede agregar controladores de mensajes para actualizar el estado modificado de la página cuando el valor de cualquiera de los cambios de controles:
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;
}
Este código responde a los cambios realizados en el control de edición o la casilla llamando a IPropertyPageImpl:: SetDirty, que informa al sitio de la página que la página ha cambiado. El sitio de la página responderá normalmente habilitar o deshabilitar un botón de Aplicar en el cuadro de la página de propiedades.
Nota
En poseer las páginas de propiedades, puede ser necesario realizar un seguimiento de exacto que las propiedades han sido modificadas por el usuario para poder impedir actualizar las propiedades que no se han cambiado.Este ejemplo aplica ese código un seguimiento de los valores de propiedad originales y comparándolos con los valores actuales de la interfaz de usuario cuando llega el momento de aplicar los cambios.
mantenimiento
Ahora agregue un par de instrucciones de #import a DocProperties.h de modo que el compilador sepa sobre la interfaz de Documento :
// 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
También necesitará hacer referencia a la clase base de IPropertyPageImpl ; agregue typedef siguiente a la clase de CDocProperties :
typedef IPropertyPageImpl<CDocProperties> PPGBaseClass;
Reemplazar IPropertyPageImpl:: SetObjects
El primer método de IPropertyPageImpl que necesita invalidar es SetObjects. A continuación agregará código para comprobar que solo se ha pasado un objeto único y admite la interfaz de Documento que espera:
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
Tiene sentido de admitir un solo objeto de esta página porque permitirá que el usuario establezca el nombre de archivo de objeto (solo un archivo puede existir en una ubicación.
Reemplazar IPropertyPageImpl:: Activar
El paso siguiente es inicializar la página de propiedades con los valores de propiedad del objeto subyacente cuando la página se crea por primera vez.
En este caso debe agregar los miembros siguientes a la clase puesto que también utilizará los valores de propiedad iniciales para la comparación cuando se aplican los usuarios de la página los cambios:
CComBSTR m_bstrFullName; // The original name
VARIANT_BOOL m_bReadOnly; // The original read-only state
La implementación de la clase base del método de Activar es responsable de crear el cuadro de diálogo y sus controles, lo que puede invalidar este método y agregarlo dispone de inicialización después de llamar a la clase 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;
}
Este código utiliza los métodos COM de la interfaz de Documento para obtener las propiedades de interés. Utilice los contenedores de la API Win32 proporcionados por CDialogImpl y sus clases base para mostrar los valores de propiedad al usuario.
Reemplazar IPropertyPageImpl:: Aplicar
Cuando los usuarios desean aplicar los cambios en los objetos, el sitio de la página de propiedades llamará al método de Aplicar . Éste es el lugar para hacer el inverso del código en Activar — mientras Activar tardó valores de objeto y los insertó en los controles de la página de propiedades, Aplicar toma los valores de los controles en la página de propiedades y los inserta en el objeto.
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
La comprobación en m_bDirty al principio de esta implementación es una comprobación inicial para evitar las actualizaciones innecesarias de objetos si Aplicar se llama más de una vez.Hay también comprueba en cada uno de los valores de propiedad para asegurarse de que solo los cambios da lugar a una llamada al método a Documento.
Nota
Expone Fullname deDocumento como propiedad de sólo lectura.Para actualizar el nombre de archivo de documento basado en los cambios realizados en la página de propiedades, hay que utilizar el método de Guardar para guardar el archivo con un nombre diferente.Así, el código de una página de propiedades no tiene que limitarse a obtener o establecer propiedades.
Mostrar la página de propiedades
Para mostrar esta página, debe crear un objeto simple auxiliares. El objeto auxiliar proporcionará un método que simplifica OleCreatePropertyFrame API para mostrar una única página conectada a un único objeto. Esta aplicación auxiliar se diseñó para que se pueda utilizar de Visual Basic.
Utilice agregue el cuadro de diálogo de la clase y Asistente para objetos simples ATL para generar una nueva clase y utilizar Helper como su nombre corto. Una vez creado, agregue un método como se muestra en la siguiente tabla.
Elemento |
Valor |
---|---|
Nombre del método |
ShowPage |
Parámetros |
[in] BSTR bstrCaption, [in] BSTR bstrID, [in] IUnknown* pUnk |
El parámetro de bstrCaption es la leyenda que se mostrará como título del cuadro de diálogo. El parámetro de bstrID es una cadena que representa el CLSID o ProgID de la página de propiedades para mostrar. El parámetro de pUnk será el puntero de IUnknown de objeto cuyas propiedades se configurarán por la página de propiedades.
Implemente el método como se muestra a continuación:
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
);
}
crear una macro
Una vez compilado el proyecto, puede probar la página de propiedades y el objeto auxiliar mediante una macro simple que puede crear y ejecutar en el entorno de desarrollo de Visual Studio. Esta macro creará un objeto auxiliar, después llama a su método de ShowPage mediante ProgID de la página de propiedades de DocProperties y el puntero de IUnknown de documento activo actualmente en el editor de Visual Studio. El código que necesita para esta macro se muestra a continuación:
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
Al ejecutar esta macro, la página de propiedades se mostrará que muestra el nombre de archivo y el estado de sólo lectura actualmente de documento de texto activo. El estado de sólo lectura del documento sólo refleja la capacidad para escribir el documento en el entorno de desarrollo; no afecta al atributo de sólo lectura del archivo en el disco.