Uso de Windows

Los ejemplos de esta sección describen cómo realizar las siguientes tareas:

Creación de una ventana principal

La primera ventana que crea una aplicación suele ser la ventana principal. La ventana principal se crea mediante la función CreateWindowEx, especificando la clase de ventana, el nombre de la ventana, los estilos de ventana, el tamaño, la posición, el identificador de menú, el identificador de instancia y los datos de creación. Una ventana principal pertenece a una clase de ventana definida por la aplicación, por lo que debe registrar la clase de ventana y proporcionar un procedimiento de ventana para la clase antes de crear la ventana principal.

La mayoría de las aplicaciones suelen utilizar el estilo WS_OVERLAPPEDWINDOW para crear la ventana principal. Este estilo proporciona a la ventana una barra de título, un menú de ventana, un borde de tamaño y botones de minimizar y maximizar. La función CreateWindowEx devuelve un identificador que identifica la ventana de forma única.

El siguiente ejemplo crea una ventana principal perteneciente a una clase de ventana definida por la aplicación. El nombre de la ventana, Ventana principal, aparecerá en la barra de título de la ventana. Combinando los estilos WS_VSCROLL y WS_HSCROLL con el estilo WS_OVERLAPPEDWINDOW, la aplicación crea una ventana principal con barras de desplazamiento horizontal y vertical además de los componentes proporcionados por el estilo WS_OVERLAPPEDWINDOW. Las cuatro apariciones de la constante CW_USEDEFAULT fijan el tamaño y la posición iniciales de la ventana a los valores por defecto definidos por el sistema. Especificando NULL en lugar de un identificador de menú, la ventana tendrá el menú definido para la clase ventana.

HINSTANCE hinst; 
HWND hwndMain; 
 
// Create the main window. 
 
hwndMain = CreateWindowEx( 
    0,                      // no extended styles           
    "MainWClass",           // class name                   
    "Main Window",          // window name                  
    WS_OVERLAPPEDWINDOW |   // overlapped window            
             WS_HSCROLL |   // horizontal scroll bar        
             WS_VSCROLL,    // vertical scroll bar          
    CW_USEDEFAULT,          // default horizontal position  
    CW_USEDEFAULT,          // default vertical position    
    CW_USEDEFAULT,          // default width                
    CW_USEDEFAULT,          // default height               
    (HWND) NULL,            // no parent or owner window    
    (HMENU) NULL,           // class menu used              
    hinst,                  // instance handle              
    NULL);                  // no window creation data      
 
if (!hwndMain) 
    return FALSE; 
 
// Show the window using the flag specified by the program 
// that started the application, and send the application 
// a WM_PAINT message. 
 
ShowWindow(hwndMain, SW_SHOWDEFAULT); 
UpdateWindow(hwndMain); 

Observe que el ejemplo anterior llama a la función ShowWindow después de crear la ventana principal. Esto se hace porque el sistema no muestra automáticamente la ventana principal después de crearla. Al pasar la marca SW_SHOWDEFAULT a ShowWindow, la aplicación permite al programa que inició la aplicación establecer el estado inicial de la ventana principal. La función UpdateWindow envía a la ventana su primer mensaje WM_PAINT.

Creación, enumeración y dimensionamiento de ventanas secundarias

Puede dividir el área cliente de una ventana en diferentes áreas funcionales utilizando ventanas secundarias. Crear una ventana secundaria es como crear una ventana principal: se utiliza la función CreateWindowEx. Para crear una ventana de una clase de ventana definida por la aplicación, debe registrar la clase de ventana y proporcionar un procedimiento de ventana antes de crear la ventana secundaria. Debe dar a la ventana secundaria el estilo WS_CHILD y especificar una ventana padre para la ventana secundaria cuando la cree.

El siguiente ejemplo divide el área cliente de la ventana principal de una aplicación en tres áreas funcionales mediante la creación de tres ventanas secundarias de igual tamaño. Cada ventana secundaria tiene la misma altura que el área cliente de la ventana principal, pero cada una tiene un tercio de su anchura. La ventana principal crea las ventanas secundarias en respuesta al mensaje WM_CREATE, que la ventana principal recibe durante su propio proceso de creación de ventanas. Como cada ventana secundaria tiene el estilo WS_BORDER, cada una tiene un borde de línea fina. Además, como el estilo WS_VISIBLE no está especificado, cada ventana secundaria está inicialmente oculta. Observe también que a cada ventana secundaria se le asigna un identificador de ventana secundaria.

La ventana principal dimensiona y posiciona las ventanas secundarias en respuesta al mensaje WM_SIZE, que la ventana principal recibe cuando cambia su tamaño. En respuesta a WM_SIZE, la ventana principal recupera las dimensiones de su área cliente utilizando la función GetClientRect y luego pasa las dimensiones a la función EnumChildWindows. EnumChildWindows pasa el handle de cada ventana secundaria, a su vez, a la función de callback EnumChildProc definida por la aplicación. Esta función dimensiona y posiciona cada ventana secundaria llamando a la función MoveWindow; el tamaño y la posición se basan en las dimensiones del área cliente de la ventana principal y el identificador de la ventana secundaria. Después, EnumChildProc llama a la función ShowWindow para hacer visible la ventana.

#define ID_FIRSTCHILD  100 
#define ID_SECONDCHILD 101 
#define ID_THIRDCHILD  102 
 
LONG APIENTRY MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 
{ 
    RECT rcClient; 
    int i; 
 
    switch(uMsg) 
    { 
        case WM_CREATE: // creating main window  
 
            // Create three invisible child windows. 

            for (i = 0; i < 3; i++) 
            { 
                CreateWindowEx(0, 
                               "ChildWClass", 
                               (LPCTSTR) NULL, 
                               WS_CHILD | WS_BORDER, 
                               0,0,0,0, 
                               hwnd, 
                               (HMENU) (int) (ID_FIRSTCHILD + i), 
                               hinst, 
                               NULL); 
            }
 
            return 0; 
 
        case WM_SIZE:   // main window changed size 
 
            // Get the dimensions of the main window's client 
            // area, and enumerate the child windows. Pass the 
            // dimensions to the child windows during enumeration. 
 
            GetClientRect(hwnd, &rcClient); 
            EnumChildWindows(hwnd, EnumChildProc, (LPARAM) &rcClient); 
            return 0; 

        // Process other messages. 
    } 
    return DefWindowProc(hwnd, uMsg, wParam, lParam); 
} 
 
BOOL CALLBACK EnumChildProc(HWND hwndChild, LPARAM lParam) 
{ 
    LPRECT rcParent; 
    int i, idChild; 
 
    // Retrieve the child-window identifier. Use it to set the 
    // position of the child window. 
 
    idChild = GetWindowLong(hwndChild, GWL_ID); 
 
    if (idChild == ID_FIRSTCHILD) 
        i = 0; 
    else if (idChild == ID_SECONDCHILD) 
        i = 1; 
    else 
        i = 2; 
 
    // Size and position the child window.  
 
    rcParent = (LPRECT) lParam; 
    MoveWindow(hwndChild, 
               (rcParent->right / 3) * i, 
               0, 
               rcParent->right / 3, 
               rcParent->bottom, 
               TRUE); 
 
    // Make sure the child window is visible. 
 
    ShowWindow(hwndChild, SW_SHOW); 
 
    return TRUE;
}

Destrucción de una ventana

Puede utilizar la función DestroyWindow para destruir una ventana. Normalmente, una aplicación envía el mensaje WM_CLOSE antes de destruir una ventana, dando a la ventana la oportunidad de pedir confirmación al usuario antes de que la ventana sea destruida. Una ventana que incluye un menú de ventana recibe automáticamente el mensaje WM_CLOSE cuando el usuario pulsa Cerrar desde el menú de ventana. Si el usuario confirma que la ventana debe ser destruida, la aplicación llama a DestroyWindow. El sistema envía el mensaje WM_DESTROY a la ventana después de eliminarla de la pantalla. En respuesta a WM_DESTROY, la ventana guarda sus datos y libera los recursos que tenía asignados. Una ventana principal concluye su procesamiento de WM_DESTROY llamando a la función PostQuitMessage para salir de la aplicación.

El siguiente ejemplo muestra cómo solicitar la confirmación del usuario antes de destruir una ventana. En respuesta a WM_CLOSE, se muestra un cuadro de diálogo que contiene botones , No y Cancelar. Si el usuario pulsa , se llama a DestroyWindow en caso contrario, la ventana no se destruye. Como la aplicación está manejando el mensaje WM_CLOSE, se devuelve 0 en todos los casos. Como la ventana que se destruye es una ventana principal, el ejemplo llama a PostQuitMessage en respuesta a WM_DESTROY.

case WM_CLOSE: 
 
    // Create the message box. If the user clicks 
    // the Yes button, destroy the main window. 
 
    if (MessageBox(hwnd, szConfirm, szAppName, MB_YESNOCANCEL) == IDYES) 
        DestroyWindow(hwndMain); 
    return 0; 
 
case WM_DESTROY: 
 
    // Post the WM_QUIT message to 
    // quit the application terminate. 
 
    PostQuitMessage(0); 
    return 0;

Uso de ventanas en capas

Para que un cuadro de diálogo aparezca como una ventana translúcida, primero cree el cuadro de diálogo como de costumbre. Luego, en WM_INITDIALOG, establezca el bit de capas del estilo extendido de la ventana y llame a SetLayeredWindowAttributes con el valor alfa deseado. El código podría tener este aspecto:

// Set WS_EX_LAYERED on this window 
SetWindowLong(hwnd, 
              GWL_EXSTYLE, 
              GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED);

// Make this window 70% alpha
SetLayeredWindowAttributes(hwnd, 0, (255 * 70) / 100, LWA_ALPHA);

Tenga en cuenta que el tercer parámetro de SetLayeredWindowAttributes es un valor que va de 0 a 255, con 0 haciendo la ventana completamente transparente y 255 haciéndola completamente opaca. Este parámetro imita la más versátil BLENDFUNCTION de la función AlphaBlend.

Para que esta ventana vuelva a ser completamente opaca, elimine el bit WS_EX_LAYERED llamando a SetWindowLong y luego pida a la ventana que vuelva a pintar. Se desea eliminar el bit para que el sistema sepa que puede liberar algo de memoria asociada con la estratificación y la redirección. El código podría tener este aspecto:

// Remove WS_EX_LAYERED from this window styles
SetWindowLong(hwnd, 
              GWL_EXSTYLE,
              GetWindowLong(hwnd, GWL_EXSTYLE) & ~WS_EX_LAYERED);

// Ask the window and its children to repaint
RedrawWindow(hwnd, 
             NULL, 
             NULL, 
             RDW_ERASE | RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN);

Con el fin de utilizar las ventanas hijo en capas, la aplicación tiene que declararse compatible con Windows 8 en el manifiesto.

Para Windows 10/11, se puede incluir este fragmento de compatibilidad en su app.manifest para que sea compatible con Windows 10:

...
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
    <application>
      <!-- Windows 10 GUID -->
      <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
    </application>
</compatibility>
...

Más información sobre la modificación del manifiesto de la aplicación se puede leer aquí: Manifiestos de aplicaciones