Cuadro de diálogo Elemento común

A partir de Windows Vista, el cuadro de diálogo elemento común reemplaza al cuadro de diálogo de archivo común anterior cuando se usa para abrir o guardar un archivo. El cuadro de diálogo elemento común se usa en dos variaciones: el cuadro de diálogo Abrir y el cuadro de diálogo Guardar . Estos dos diálogos comparten la mayor parte de su funcionalidad, pero cada uno tiene sus propios métodos únicos.

Importante

IFileDialog es la API del cuadro de diálogo de archivos moderno (Windows Vista y versiones posteriores). Las funciones GetOpenFileName y GetSaveFileName anteriores son heredadas y no deben usarse en nuevas aplicaciones. Las API heredadas no admiten el espacio de nombres de Shell, la personalización moderna del cuadro de diálogo o las características de metadatos de archivo.

Cuando se usa IFileDialog:

  • Debe llamar a CoInitializeEx antes de llamar a CoCreateInstance para crear el cuadro de diálogo. Utilice COINIT_APARTMENTTHREADED para los hilos de la interfaz de usuario.
  • Use CLSID_FileOpenDialog o CLSID_FileSaveDialog con CLSCTX_INPROC_SERVER.
  • Alternativas modernas: para las aplicaciones para UWP/WinUI, usa Windows. Storage.Pickers. Para las aplicaciones de escritorio de .NET, WPF y WinForms proporcionan OpenFileDialog/SaveFileDialog envoltorios que usan IFileDialog internamente.

Aunque esta versión más reciente se llama cuadro de diálogo común de elementos, en la mayor parte de la documentación se sigue llamando cuadro de diálogo común de archivos. A menos que esté tratando específicamente con una versión anterior de Windows, debe suponer que cualquier mención del cuadro de diálogo archivo común hace referencia a este cuadro de diálogo de elemento común.

Aquí se describen los temas siguientes:

IFileDialog, IFileOpenDialog e IFileSaveDialog

Windows Vista proporciona implementaciones de los cuadros de diálogo Abrir y Guardar: CLSID_FileOpenDialog y CLSID_FileSaveDialog. Estos cuadros de diálogo se muestran aquí.

captura de pantalla del cuadro de diálogo abrir

captura de pantalla del cuadro de diálogo Guardar como

IFileOpenDialog e IFileSaveDialog heredan de IFileDialog y comparten gran parte de su funcionalidad. Además, el cuadro de diálogo Abrir admite IFileOpenDialog y el cuadro de diálogo Guardar admite IFileSaveDialog.

La implementación del cuadro de diálogo de elemento común que se encuentra en Windows Vista proporciona varias ventajas sobre la implementación proporcionada en versiones anteriores:

  • Admite el uso directo del espacio de nombres de Shell a través de IShellItem en lugar de usar rutas de acceso del sistema de archivos.
  • Habilita la personalización simple del cuadro de diálogo, como establecer la etiqueta en el botón Aceptar , sin necesidad de un procedimiento de enlace.
  • Admite una personalización más amplia del cuadro de diálogo mediante la adición de un conjunto de controles controlados por datos que funcionan sin una plantilla de diálogo Win32. Este esquema de personalización libera el proceso de llamada desde el diseño de la interfaz de usuario. Dado que los cambios en el diseño del cuadro de diálogo siguen usando este modelo de datos, la implementación del diálogo no está vinculada a la versión actual específica del cuadro de diálogo.
  • Admite la notificación a la persona que llama de eventos dentro del diálogo, como el cambio de selección o el cambio de tipo de archivo. También permite que el proceso de llamada enlace determinados eventos en el cuadro de diálogo, como el análisis.
  • Presenta nuevas características de cuadro de diálogo, como agregar lugares especificados por el autor de la llamada a la barra Lugares .
  • En el cuadro de diálogo Guardar, los desarrolladores pueden aprovechar las nuevas características de metadatos del shell de Windows Vista.

Además, los desarrolladores pueden optar por implementar las interfaces siguientes:

El cuadro de diálogo Abrir o Guardar devuelve un objeto IShellItem o IShellItemArray al proceso de llamada. A continuación, el autor de la llamada puede usar un objeto IShellItem individual para obtener una ruta de acceso del sistema de archivos o para abrir una secuencia en el elemento para leer o escribir información.

Las marcas y opciones disponibles para los nuevos métodos de diálogo son muy similares a las marcas OFN anteriores que se encuentran en la estructura OPENFILENAME y se usan en GetOpenFileName y GetSaveFileName. Muchos de ellos son exactamente iguales, excepto que comienzan con un prefijo FOS. La lista completa se puede encontrar en los temas IFileDialog::GetOptions e IFileDialog::SetOptions . Los cuadros de diálogo Abrir y Guardar se crean de forma predeterminada con las marcas más comunes. Para el cuadro de diálogo Abrir, es (FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST | FOS_NOCHANGEDIR) y para el cuadro de diálogo Guardar es (FOS_OVERWRITEPROMPT | FOS_NOREADONLYRETURN | FOS_PATHMUSTEXIST | FOS_NOCHANGEDIR).

IFileDialog y sus interfaces descendientes heredan de y extienden IModalWindow. Show toma como único parámetro el identificador de la ventana principal. Si Show devuelve correctamente, hay un resultado válido. Si devuelve HRESULT_FROM_WIN32(ERROR_CANCELLED), significa que el usuario canceló el cuadro de diálogo. También puede devolver legítimamente otro código de error, como E_OUTOFMEMORY.

Ejemplo de uso

En las secciones siguientes se muestra código de ejemplo para una variedad de tareas de diálogo.

La mayor parte del código de ejemplo se encuentra en el Common File Dialog Sample del SDK de Windows.

Uso básico

En el ejemplo siguiente se muestra cómo iniciar un cuadro de diálogo Abrir . En este ejemplo, está restringido a documentos de Microsoft Word.

Note

Varios ejemplos de este tema usan la CDialogEventHandler_CreateInstance función auxiliar para crear una instancia de la implementación de IFileDialogEvents . Para usar esta función en su propio código, copie el código fuente de la CDialogEventHandler_CreateInstance función del ejemplo de cuadro de diálogo de archivos comunes, desde el que se toman todos los ejemplos de este tema.

 

HRESULT BasicFileOpen()
{
    // CoCreate the File Open Dialog object.
    IFileDialog *pfd = NULL;
    HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog, 
                      NULL, 
                      CLSCTX_INPROC_SERVER, 
                      IID_PPV_ARGS(&pfd));
    if (SUCCEEDED(hr))
    {
        // Create an event handling object, and hook it up to the dialog.
        IFileDialogEvents *pfde = NULL;
        hr = CDialogEventHandler_CreateInstance(IID_PPV_ARGS(&pfde));
        if (SUCCEEDED(hr))
        {
            // Hook up the event handler.
            DWORD dwCookie;
            hr = pfd->Advise(pfde, &dwCookie);
            if (SUCCEEDED(hr))
            {
                // Set the options on the dialog.
                DWORD dwFlags;

                // Before setting, always get the options first in order 
                // not to override existing options.
                hr = pfd->GetOptions(&dwFlags);
                if (SUCCEEDED(hr))
                {
                    // In this case, get shell items only for file system items.
                    hr = pfd->SetOptions(dwFlags | FOS_FORCEFILESYSTEM);
                    if (SUCCEEDED(hr))
                    {
                        // Set the file types to display only. 
                        // Notice that this is a 1-based array.
                        hr = pfd->SetFileTypes(ARRAYSIZE(c_rgSaveTypes), c_rgSaveTypes);
                        if (SUCCEEDED(hr))
                        {
                            // Set the selected file type index to Word Docs for this example.
                            hr = pfd->SetFileTypeIndex(INDEX_WORDDOC);
                            if (SUCCEEDED(hr))
                            {
                                // Set the default extension to be ".doc" file.
                                hr = pfd->SetDefaultExtension(L"doc;docx");
                                if (SUCCEEDED(hr))
                                {
                                    // Show the dialog
                                    hr = pfd->Show(NULL);
                                    if (SUCCEEDED(hr))
                                    {
                                        // Obtain the result once the user clicks 
                                        // the 'Open' button.
                                        // The result is an IShellItem object.
                                        IShellItem *psiResult;
                                        hr = pfd->GetResult(&psiResult);
                                        if (SUCCEEDED(hr))
                                        {
                                            // We are just going to print out the 
                                            // name of the file for sample sake.
                                            PWSTR pszFilePath = NULL;
                                            hr = psiResult->GetDisplayName(SIGDN_FILESYSPATH, 
                                                               &pszFilePath);
                                            if (SUCCEEDED(hr))
                                            {
                                                TaskDialog(NULL,
                                                           NULL,
                                                           L"CommonFileDialogApp",
                                                           pszFilePath,
                                                           NULL,
                                                           TDCBF_OK_BUTTON,
                                                           TD_INFORMATION_ICON,
                                                           NULL);
                                                CoTaskMemFree(pszFilePath);
                                            }
                                            psiResult->Release();
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                // Unhook the event handler.
                pfd->Unadvise(dwCookie);
            }
            pfde->Release();
        }
        pfd->Release();
    }
    return hr;
}

Limitar los resultados a los elementos del sistema de archivos

En el ejemplo siguiente, tomado de arriba, se muestra cómo restringir los resultados a los elementos del sistema de archivos. Tenga en cuenta que IFileDialog::SetOptions agrega la nueva marca a un valor obtenido a través de IFileDialog::GetOptions. Esta método es el recomendado.

                // Set the options on the dialog.
                DWORD dwFlags;

                // Before setting, always get the options first in order 
                // not to override existing options.
                hr = pfd->GetOptions(&dwFlags);
                if (SUCCEEDED(hr))
                {
                    // In this case, get shell items only for file system items.
                    hr = pfd->SetOptions(dwFlags | FOS_FORCEFILESYSTEM);

Especificar tipos de archivo para un cuadro de diálogo

Para establecer tipos de archivo específicos que el cuadro de diálogo puede controlar, use el método IFileDialog::SetFileTypes . Ese método acepta una matriz de COMDLG_FILTERSPEC estructuras, cada una de las cuales representa un tipo de archivo.

El mecanismo de extensión predeterminado de un cuadro de diálogo no cambia de GetOpenFileName y GetSaveFileName. La extensión de nombre de archivo que se anexa al texto que el usuario escribe en el cuadro de edición del nombre de archivo se inicializa cuando se abre el cuadro de diálogo. Debe coincidir con el tipo de archivo predeterminado (que se ha seleccionado como se abre el cuadro de diálogo). Si el tipo de archivo predeterminado es "*.*" (todos los archivos), el archivo puede ser una extensión de su elección. Si el usuario elige un tipo de archivo diferente, la extensión se actualiza automáticamente a la primera extensión de nombre de archivo asociada a ese tipo de archivo. Si el usuario elige "*.*" (todos los archivos), la extensión vuelve a su valor original.

En el ejemplo siguiente se muestra cómo se ha hecho anteriormente.

                        // Set the file types to display only. 
                        // Notice that this is a 1-based array.
                        hr = pfd->SetFileTypes(ARRAYSIZE(c_rgSaveTypes), c_rgSaveTypes);
                        if (SUCCEEDED(hr))
                        {
                            // Set the selected file type index to Word Docs for this example.
                            hr = pfd->SetFileTypeIndex(INDEX_WORDDOC);
                            if (SUCCEEDED(hr))
                            {
                                // Set the default extension to be ".doc" file.
                                hr = pfd->SetDefaultExtension(L"doc;docx");

Controlar la carpeta predeterminada

Casi cualquier carpeta del espacio de nombres shell se puede usar como carpeta predeterminada para el cuadro de diálogo (la carpeta presentada cuando el usuario elige abrir o guardar un archivo). Llame a IFileDialog::SetDefaultFolder antes de llamar a Show para hacerlo.

La carpeta predeterminada es la carpeta en la que el cuadro de diálogo se inicia la primera vez que un usuario lo abre desde la aplicación. Después, el cuadro de diálogo se abrirá en la última carpeta que abrió un usuario o la última carpeta que usó para guardar un elemento. Consulte Persistencia de estado para obtener más detalles.

Puede forzar que el cuadro de diálogo siempre muestre la misma carpeta cuando se abra, independientemente de la acción del usuario anterior, llamando a IFileDialog::SetFolder. Sin embargo, no se recomienda hacerlo. Si se llama a SetFolder antes de mostrar el cuadro de diálogo, no se muestra la ubicación más reciente en la que el usuario guardó o desde la que abrió. A menos que haya una razón muy específica para este comportamiento, no es una experiencia de usuario buena o esperada y debe evitarse. En casi todas las instancias, IFileDialog::SetDefaultFolder es el mejor método.

Al guardar un documento por primera vez en el cuadro de diálogo Guardar , debe seguir las mismas directrices para determinar la carpeta inicial que hizo en el cuadro de diálogo Abrir . Si el usuario está editando un documento existente anteriormente, abra el cuadro de diálogo en la carpeta donde se almacena ese documento y rellene el cuadro de edición con el nombre de ese documento. Llame a IFileSaveDialog::SetSaveAsItem con el elemento actual antes de llamar a Show.

Agregar elementos a la barra de lugares

En el ejemplo siguiente se muestra la adición de elementos a la barra Lugares :

HRESULT AddItemsToCommonPlaces()
{
    // CoCreate the File Open Dialog object.
    IFileDialog *pfd = NULL;
    HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog, 
                      NULL, 
                      CLSCTX_INPROC_SERVER, 
                      IID_PPV_ARGS(&pfd));
    if (SUCCEEDED(hr))
    {
        // Always use known folders instead of hard-coding physical file paths.
        // In this case we are using Public Music KnownFolder.
        IKnownFolderManager *pkfm = NULL;
        hr = CoCreateInstance(CLSID_KnownFolderManager, 
                      NULL, 
                      CLSCTX_INPROC_SERVER, 
                      IID_PPV_ARGS(&pkfm));
        if (SUCCEEDED(hr))
        {
            // Get the known folder.
            IKnownFolder *pKnownFolder = NULL;
            hr = pkfm->GetFolder(FOLDERID_PublicMusic, &pKnownFolder);
            if (SUCCEEDED(hr))
            {
                // File Dialog APIs need an IShellItem that represents the location.
                IShellItem *psi = NULL;
                hr = pKnownFolder->GetShellItem(0, IID_PPV_ARGS(&psi));
                if (SUCCEEDED(hr))
                {
                    // Add the place to the bottom of default list in Common File Dialog.
                    hr = pfd->AddPlace(psi, FDAP_BOTTOM);
                    if (SUCCEEDED(hr))
                    {
                        // Show the File Dialog.
                        hr = pfd->Show(NULL);
                        if (SUCCEEDED(hr))
                        {
                            //
                            // You can add your own code here to handle the results.
                            //
                        }
                    }
                    psi->Release();
                }
                pKnownFolder->Release();
            }
            pkfm->Release();
        }
        pfd->Release();
    }
    return hr;
}

Persistencia de estado

Antes de Windows Vista, un estado, como la última carpeta visitada, se guardó por proceso. Sin embargo, esa información se usó independientemente de la acción concreta. Por ejemplo, una aplicación de edición de vídeo presentaría la misma carpeta en el cuadro de diálogo Representar como que lo haría en el cuadro de diálogo Importar medios . En Windows Vista, se puede ser más específico mediante el uso de GUID. Para asignar un GUID al cuadro de diálogo, llame a iFileDialog::SetClientGuid.

Funcionalidades de selección múltiple

La funcionalidad multiselección está disponible en el cuadro de diálogo Abrir mediante el método GetResults , como se muestra aquí.

HRESULT MultiselectInvoke()
{
    IFileOpenDialog *pfd;
    
    // CoCreate the dialog object.
    HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog, 
                                  NULL, 
                                  CLSCTX_INPROC_SERVER, 
                                  IID_PPV_ARGS(&pfd));

    if (SUCCEEDED(hr))
    {
        DWORD dwOptions;
        // Specify multiselect.
        hr = pfd->GetOptions(&dwOptions);
        
        if (SUCCEEDED(hr))
        {
            hr = pfd->SetOptions(dwOptions | FOS_ALLOWMULTISELECT);
        }

        if (SUCCEEDED(hr))
        {
            // Show the Open dialog.
            hr = pfd->Show(NULL);

            if (SUCCEEDED(hr))
            {
                // Obtain the result of the user interaction.
                IShellItemArray *psiaResults;
                hr = pfd->GetResults(&psiaResults);
                
                if (SUCCEEDED(hr))
                {
                    //
                    // You can add your own code here to handle the results.
                    //
                    psiaResults->Release();
                }
            }
        }
        pfd->Release();
    }
    return hr;
}

Escuchar eventos desde el cuadro de diálogo

Un proceso de llamada puede registrar una interfaz IFileDialogEvents con el cuadro de diálogo mediante los métodos IFileDialog::Advise e IFileDialog::Unadvise como se muestra aquí.

Esto se toma del ejemplo De uso básico .

        // Create an event handling object, and hook it up to the dialog.
        IFileDialogEvents *pfde = NULL;
        hr = CDialogEventHandler_CreateInstance(IID_PPV_ARGS(&pfde));
        if (SUCCEEDED(hr))
        {
            // Hook up the event handler.
            DWORD dwCookie;
            hr = pfd->Advise(pfde, &dwCookie);

La mayor parte del procesamiento del diálogo se colocaría aquí.

                // Unhook the event handler.
                pfd->Unadvise(dwCookie);
            }
            pfde->Release();
        }
        pfd->Release();
    }
    return hr;
}

El proceso de llamada puede usar eventos para la notificación cuando el usuario cambia la carpeta, el tipo de archivo o la selección. Estos eventos son especialmente útiles cuando el proceso de llamada ha agregado controles al cuadro de diálogo (vea Personalizar el cuadro de diálogo) y debe cambiar el estado de esos controles en reacción a estos eventos. Útil en todos los casos es la capacidad del proceso de llamada para proporcionar código personalizado para tratar situaciones como compartir infracciones, sobrescribir archivos o determinar si un archivo es válido antes de que se cierre el cuadro de diálogo. Algunos de esos casos se describen en esta sección.

OnFileOk

Se llama a este método después de que el usuario elija un elemento, justo antes de que se cierre el cuadro de diálogo. A continuación, la aplicación puede llamar a IFileDialog::GetResult o IFileOpenDialog::GetResults como se haría una vez cerrado el cuadro de diálogo. Si el elemento elegido es aceptable, pueden devolver S_OK. De lo contrario, devuelven S_FALSE y muestran la interfaz de usuario que indica al usuario por qué el elemento elegido no es válido. Si se devuelve S_FALSE, el cuadro de diálogo no se cierra.

El proceso de llamada puede usar el identificador de ventana del propio diálogo como elemento primario de la interfaz de usuario. Ese identificador se puede obtener llamando primero a IOleWindow::QueryInterface y, a continuación, llamando a IOleWindow::GetWindow con el identificador tal como se muestra en este ejemplo.

HRESULT CDialogEventHandler::OnFileOk(IFileDialog *pfd) 
{ 
    IShellItem *psiResult;
    HRESULT hr = pfd->GetResult(&psiResult);
    
    if (SUCCEEDED(hr))
    {
        SFGAOF attributes;
        hr = psiResult->GetAttributes(SFGAO_COMPRESSED, &attributes);
    
        if (SUCCEEDED(hr))
        {
            if (attributes & SFGAO_COMPRESSED)
            {
                // Accept the file.
                hr = S_OK;
            }
            else
            {
                // Refuse the file.
                hr = S_FALSE;
                
                _DisplayMessage(pfd, L"Not a compressed file.");
            }
        }
        psiResult->Release();
    }
    return hr;
};

HRESULT CDialogEventHandler::_DisplayMessage(IFileDialog *pfd, PCWSTR pszMessage)
{
    IOleWindow *pWindow;
    HRESULT hr = pfd->QueryInterface(IID_PPV_ARGS(&pWindow));
    
    if (SUCCEEDED(hr))
    {
        HWND hwndDialog;
        hr = pWindow->GetWindow(&hwndDialog);
    
        if (SUCCEEDED(hr))
        {
            MessageBox(hwndDialog, pszMessage, L"An error has occurred", MB_OK);
        }
        pWindow->Release();
    }
    return hr;
}

OnShareViolation y OnOverwrite

Si el usuario decide sobrescribir un archivo en el cuadro de diálogo Guardar , o si un archivo que se va a guardar o reemplazar está en uso y no se puede escribir en (una infracción de uso compartido), la aplicación puede proporcionar funcionalidad personalizada para invalidar el comportamiento predeterminado del cuadro de diálogo. De forma predeterminada, al sobrescribir un archivo, el cuadro de diálogo muestra un mensaje que permite al usuario comprobar esta acción. Para las infracciones de uso compartido, de forma predeterminada, el cuadro de diálogo muestra un mensaje de error, no se cierra y se requiere que el usuario realice otra opción. El proceso de llamada puede invalidar estos valores predeterminados y mostrar su propia interfaz de usuario si lo desea. Se puede indicar al cuadro de diálogo que rechace el archivo y permanezca abierto o acepte el archivo y cierre correctamente.

Personalización del cuadro de diálogo

Se puede agregar una variedad de controles al cuadro de diálogo sin proporcionar una plantilla de diálogo Win32. Estos controles incluyen botones de comando, cuadros combinados, cuadros de edición, casillas de verificación, listas de botones de opción, grupos, separadores y controles de texto estático. Llame a QueryInterface en el objeto dialog (IFileDialog, IFileOpenDialog o IFileSaveDialog) para obtener un puntero IFileDialogCustomize . Use esa interfaz para agregar controles. Cada control tiene un identificador proporcionado por el autor de la llamada asociado, así como un estado visible y habilitado que el proceso de llamada puede establecer. Algunos controles, como PushButton, también tienen texto asociado a ellos.

Se pueden agregar varios controles a un "grupo visual" que se mueve como una sola unidad en el diseño del cuadro de diálogo. Los grupos pueden tener una etiqueta asociada a ellos.

Los controles solo se pueden agregar antes de que se muestre el cuadro de diálogo. Sin embargo, una vez que se muestra el cuadro de diálogo, los controles se pueden ocultar o mostrar como desee, quizás en respuesta a la acción del usuario. En los ejemplos siguientes se muestra cómo agregar una lista de botones de radio al cuadro de diálogo.

// Controls
#define CONTROL_GROUP           2000
#define CONTROL_RADIOBUTTONLIST 2
#define CONTROL_RADIOBUTTON1    1
#define CONTROL_RADIOBUTTON2    2       // It is OK for this to have the same ID
                    // as CONTROL_RADIOBUTTONLIST, because it 
                    // is a child control under CONTROL_RADIOBUTTONLIST


// This code snippet demonstrates how to add custom controls in the Common File Dialog.
HRESULT AddCustomControls()
{
    // CoCreate the File Open Dialog object.
    IFileDialog *pfd = NULL;
    HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog, 
                                  NULL, 
                                  CLSCTX_INPROC_SERVER, 
                                  IID_PPV_ARGS(&pfd));
    if (SUCCEEDED(hr))
    {
        // Create an event handling object, and hook it up to the dialog.
        IFileDialogEvents   *pfde       = NULL;
        DWORD               dwCookie    = 0;
        hr = CDialogEventHandler_CreateInstance(IID_PPV_ARGS(&pfde));
        if (SUCCEEDED(hr))
        {
            // Hook up the event handler.
            hr = pfd->Advise(pfde, &dwCookie);
            if (SUCCEEDED(hr))
            {
                // Set up a Customization.
                IFileDialogCustomize *pfdc = NULL;
                hr = pfd->QueryInterface(IID_PPV_ARGS(&pfdc));
                if (SUCCEEDED(hr))
                {
                    // Create a Visual Group.
                    hr = pfdc->StartVisualGroup(CONTROL_GROUP, L"Sample Group");
                    if (SUCCEEDED(hr))
                    {
                        // Add a radio-button list.
                        hr = pfdc->AddRadioButtonList(CONTROL_RADIOBUTTONLIST);
                        if (SUCCEEDED(hr))
                        {
                            // Set the state of the added radio-button list.
                            hr = pfdc->SetControlState(CONTROL_RADIOBUTTONLIST, 
                                               CDCS_VISIBLE | CDCS_ENABLED);
                            if (SUCCEEDED(hr))
                            {
                                // Add individual buttons to the radio-button list.
                                hr = pfdc->AddControlItem(CONTROL_RADIOBUTTONLIST,
                                                          CONTROL_RADIOBUTTON1,
                                                          L"Change Title to ABC");
                                if (SUCCEEDED(hr))
                                {
                                    hr = pfdc->AddControlItem(CONTROL_RADIOBUTTONLIST,
                                                              CONTROL_RADIOBUTTON2,
                                                              L"Change Title to XYZ");
                                    if (SUCCEEDED(hr))
                                    {
                                        // Set the default selection to option 1.
                                        hr = pfdc->SetSelectedControlItem(CONTROL_RADIOBUTTONLIST,
                                                                          CONTROL_RADIOBUTTON1);
                                    }
                                }
                            }
                        }
                        // End the visual group.
                        pfdc->EndVisualGroup();
                    }
                    pfdc->Release();
                }

                if (FAILED(hr))
                {
                    // Unadvise here in case we encounter failures 
                    // before we get a chance to show the dialog.
                    pfd->Unadvise(dwCookie);
                }
            }
            pfde->Release();
        }

        if (SUCCEEDED(hr))
        {
            // Now show the dialog.
            hr = pfd->Show(NULL);
            if (SUCCEEDED(hr))
            {
                //
                // You can add your own code here to handle the results.
                //
            }
            // Unhook the event handler.
            pfd->Unadvise(dwCookie);
        }
        pfd->Release();
    }
    return hr;
}

Agregar opciones al botón Aceptar

De forma similar, se pueden agregar opciones a los botones Abrir o Guardar , que son el botón Aceptar para sus respectivos tipos de diálogo. Las opciones son accesibles a través de un cuadro de lista desplegable adjunto al botón. El primer elemento de la lista se convierte en el texto del botón. En el ejemplo siguiente se muestra cómo proporcionar un botón Abrir con dos posibilidades: "Abrir" y "Abrir como solo lectura".

// OpenChoices options
#define OPENCHOICES 0
#define OPEN 0
#define OPEN_AS_READONLY 1


HRESULT AddOpenChoices()
{
    // CoCreate the File Open Dialog object.
    IFileDialog *pfd = NULL;
    HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog, 
                      NULL, 
                      CLSCTX_INPROC_SERVER, 
                      IID_PPV_ARGS(&pfd));
    if (SUCCEEDED(hr))
    {
        // Create an event handling object, and hook it up to the dialog.
        IFileDialogEvents   *pfde       = NULL;
        DWORD               dwCookie    = 0;
        hr = CDialogEventHandler_CreateInstance(IID_PPV_ARGS(&pfde));
        if (SUCCEEDED(hr))
        {
            // Hook up the event handler.
            hr = pfd->Advise(pfde, &dwCookie);
            if (SUCCEEDED(hr))
            {
                // Set up a Customization.
                IFileDialogCustomize *pfdc = NULL;
                hr = pfd->QueryInterface(IID_PPV_ARGS(&pfdc));
                if (SUCCEEDED(hr))
                {
                    hr = pfdc->EnableOpenDropDown(OPENCHOICES);
                    if (SUCCEEDED(hr))
                    {
                        hr = pfdc->AddControlItem(OPENCHOICES, OPEN, L"&Open");
                    }                    
                    if (SUCCEEDED(hr))
                    {
                        hr = pfdc->AddControlItem(OPENCHOICES, 
                                                OPEN_AS_READONLY, 
                                                L"Open as &read-only");
                    }
                    if (SUCCEEDED(hr))
                    {
                        pfd->Show(NULL);
                    }
                }
                pfdc->Release();
            }
            pfd->Unadvise(dwCookie);
        }
        pfde->Release();
    }
    pfd->Release();
    return hr;
}

La opción del usuario se puede comprobar después de que el cuadro de diálogo regrese del método Show, igual que haría con un ComboBox, o bien puede comprobarse durante el procesamiento de IFileDialogEvents::OnFileOk.

Respuesta a eventos en controles agregados

El controlador de eventos proporcionado por el proceso de llamada puede implementar IFileDialogControlEvents además de IFileDialogEvents. IFileDialogControlEvents permite que el proceso de llamada reaccione a estos eventos:

  • PushButton en el que se hace clic
  • El estado de checkButton ha cambiado
  • Elemento seleccionado en un menú, ComboBox o lista RadioButton
  • Controlar la activación. Esto se envía cuando un menú está a punto de mostrar una lista desplegable, en caso de que el proceso de llamada quiera cambiar los elementos de la lista.

Ejemplos completos

A continuación se muestran ejemplos de C++ descargables del kit de desarrollo de software (SDK) de Windows que muestran el uso y la interacción con el cuadro de diálogo elemento común.

IID_PPV_ARGS