2010-10-31 7 views
5

Tengo una aplicación MFC bastante estándar que consiste en una ventana principal y ocasionalmente muestra cuadros de diálogo modales. Como todos sabemos, no se puede hacer nada fuera de un diálogo modal hasta que se cierre.MFC: ventana principal atenuada al mostrar el cuadro de diálogo modal

Por lo tanto, una buena característica de interfaz de usuario es "dim" el resto de la ventana principal detrás de la ventana de diálogo, para indicar visualmente no se puede utilizar hasta que haya terminado con el diálogo modal. Algunas aplicaciones web y aplicaciones java/mac hacen esto, pero nunca lo he visto en una aplicación tradicional C++/MFC. Me gustaría intentarlo, incluso si no es habitual en la plataforma.

¿Cómo se puede hacer esto? Tengo varios cuadros de diálogo modales en la aplicación, que se utilizan en este patrón:

// pMainFrame is available as a pointer to the CWnd of the main window 
CMyDialog dialog; 
dialog.DoModal(); // invoke modal dialog; returns after dialog closed 

hay una manera fácil de tener la ventana atenuado antes de cualquier DoModal() y restaurada después? Estoy usando Visual Studio 2010 en caso de que el MFC actualizado tenga alguna característica que pueda ayudar.

Editar: He publicado una solución basada en la respuesta de Oystein, pero estoy comenzando una recompensa en caso de que alguien pueda mejorarla, especialmente con un fundido de entrada/salida gradual.

+0

En un primer momento, lo que está atenuado? ¿Puedes proporcionar una captura de pantalla del comportamiento deseado? –

+0

Encontré esto en Google Images, es una página web que oscurece el resto de la página alrededor de un diálogo: http://www.irritatedvowel.com/pub/blog/UsingBlurBehindDialogsinSilverlight3Beta_13306/image.png - así, pero oscureciendo la ventana principal de una aplicación mientras el diálogo está activo. – AshleysBrain

Respuesta

14

Puede crear otra ventana, completamente negra, en la parte superior de la ventana que desea atenuar, y establecer la opacidad de la ventana negra con SetLayeredWindowAttributes. No tiene que ser negro, por supuesto, pero supongo que es el mejor color de atenuación.

EDIT: hackeado un ejemplo - pero tenga en cuenta que no soy un desarrollador de MFC, que suelen utilizar la API de Windows directamente. Parece que funciona bien, sin embargo. Here es un pastebin. Siéntase libre de agregar fade-ins, etc. usted mismo. También tenga en cuenta que esto atenúa toda la pantalla, tendrá que cambiar el tamaño de mi ventana de regulación si no desea este comportamiento. Ver los comentarios del código.

/********************************************************************************************** 

    MFC screen dim test 
     :: oystein   :: November 2010 

    Creates a simple window - click it to toggle whether a translucent black "dimmer" window 
    is shown. The dimmer-window covers the entire screen, but the taskbar ("superbar" in 
    Windows 7) will jump on top of it if clicked - it seems. Simple suggestions to fix that 
    are welcome. 

    Should work on Windows 2000 and later. 

    Disclaimer: This is my first MFC program ever, so if anything seems wrong, it probably is. 
    I have previously only coded with pure Win32 API, and hacked this together using online 
    tutorials. Code provided "as-is" with no guarantees - I can not be held responsible for 
    anything bad that happens if you run this program. 

***********************************************************************************************/ 

#include "stdafx.h" 

#undef WINVER 
#define WINVER 0x500 // Windows 2000 & above, because of layered windows 


// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
// 
//      Black window used to dim everything else 
// 
class CDimWnd : public CFrameWnd 
{    
public: 
    CDimWnd() 
    { 
     // Get screen res into rect 
     RECT rc; 
     GetDesktopWindow()->GetWindowRect(&rc); 

     CreateEx(WS_EX_LAYERED |  // Layered window for translucency 
       WS_EX_TRANSPARENT | // Click through 
       WS_EX_TOPMOST |  // Always on top 
       WS_EX_TOOLWINDOW,  // Do not appear in taskbar & similar 
       NULL, TEXT(""), 
       WS_POPUP,    // No frame/borders - though there is 
             // still some border left - we'll remove 
             // it with regions 

       0, 0, rc.right + 10, rc.bottom + 10, // Make the window 10px larger 
                 // than screen resolution in both 
                 // directions - it is still positioned 
                 // at 0,0 
       NULL, NULL); 

     // Grab a part of the window the size of the desktop - but 5px into it 
     // Because the window is larger than the desktop res, the borders are removed 
     CRgn rgn;       
     rgn.CreateRectRgn(rc.left + 5, rc.top + 5, rc.right + 5, rc.bottom + 5); 
     SetWindowRgn((HRGN)rgn, FALSE); 
     rgn.Detach();        

     // We have to reposition window - (0,0) of window has not changed 
     SetWindowPos(NULL, -5, -5, 0, 0, SWP_NOSIZE | SWP_NOZORDER);   

     // This is where we set the opacity of the window: 0-255 
     SetLayeredWindowAttributes(RGB(0,0,0), 150, LWA_ALPHA);      
    } 
    void Close() 
    { 
     CFrameWnd::OnClose(); 
    } 
    BOOL CDimWnd::OnEraseBkgnd(CDC* pDC); // Set BKG color 
    DECLARE_MESSAGE_MAP() 
}; 

BOOL CDimWnd::OnEraseBkgnd(CDC* pDC) 
{ 
    // Set brush to desired background color 
    CBrush backBrush(RGB(0, 0, 0)); 

    // Save old brush 
    CBrush* pOldBrush = pDC->SelectObject(&backBrush); 

    CRect rect; 
    pDC->GetClipBox(&rect);  // Erase the area needed 

    pDC->PatBlt(rect.left, rect.top, rect.Width(), rect.Height(), PATCOPY); 
    pDC->SelectObject(pOldBrush); 
    return TRUE; 
} 

BEGIN_MESSAGE_MAP(CDimWnd, CFrameWnd) 
    ON_WM_ERASEBKGND() 
END_MESSAGE_MAP() 

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 


// Global variable - is screen dimmed? 
bool g_bIsDimmed = false; 


// The main window 
class CMainWnd : public CFrameWnd 
{  
    // Contains a CDimWnd - I'm not sure if this is the "MFC way" of doing things 
    CDimWnd dimmer; 

public: 
    CMainWnd() 
    { 
     Create(NULL, TEXT("Screen dimmer - Press left mouse button on window to toggle"), 
      WS_OVERLAPPEDWINDOW, CRect(50, 50, 400, 250)); 
    } 
    // Left mouse button toggles dimming 
    afx_msg void OnLButtonDown(UINT Flags, CPoint Point) 
    { 
     if(!g_bIsDimmed) 
     { 
      dimmer.ShowWindow(SW_SHOW); 
      dimmer.BringWindowToTop();   
      g_bIsDimmed = true; 
     } 
     else 
     {   
      dimmer.ShowWindow(SW_HIDE);  
      g_bIsDimmed = false; 
     } 
    } 
    DECLARE_MESSAGE_MAP() 
}; 

BEGIN_MESSAGE_MAP(CMainWnd, CFrameWnd) 
    ON_WM_LBUTTONDOWN() 
END_MESSAGE_MAP() 


// The app 
class CApp : public CWinApp 
{ 
public:   
    virtual BOOL InitInstance(); 
}; 

BOOL CApp::InitInstance() 
{    
    m_pMainWnd = new CMainWnd();    
    m_pMainWnd->ShowWindow(m_nCmdShow);   
    m_pMainWnd->UpdateWindow();   
    return TRUE; 
} 

CApp HelloApp; 

ACTUALIZACIÓN:

Pirateé juntos algo más de código para usted, para manejar el desvanecimiento. Todavía no soy un desarrollador de MFC, y dejé el código en un estado "rudo" (poco manejo de errores, no muy robusto) para darte algo que hacer también. :) De todos modos, aquí hay una manera de hacerlo, que creo que es bastante limpio:

Para usarlo, hacer que su ventana principal contiene una ventana regulador

class CMainFrm : public CFrameWnd 
{  
    CDimWnd* dimmer; 

public: 
    CMainFrm() 
    { 
     // constructor code here ... 
     dimmer = new CDimWnd();   
    } 

// rest of class ... 

}; 

A continuación, se puede utilizar, por ejemplo, de esta manera:

dimmer->Show(); 
MessageBox(TEXT("Hello world")); 
dimmer->Hide(); 

Alternativamente Creo que se puede poner este código (Show()/Hide() llamadas) en el constructor y el destructor del diálogo modal, si se quiere mantener el código allí. Si desea un "scope" -dim, como en el ejemplo que publicó, este código debería ir en el constructor & destructor de la clase CDimWnd, y necesitaría algo así como una variable de miembro estática para asegurar que solo un dimmer es ejecutándose a la vez (a menos que quiera usar una variable global).

Por la ventana dimmer - Hice esto:

CDimWnd.h

#define TARGET_OPACITY 70 // Target opacity 0-255 for dimmed window 
#define FADE_TIME 20  // Time between each fade step in milliseconds 
#define FADE_STEP 5  // How much to add to/remove from opacity each fade step 
#define ID_FADE_TIMER 1 

// Call Show() and Hide() to fade in/fade out dimmer. 
// Creates the dimmer window in constructor. 
class CDimWnd : public CFrameWnd 
{   
    bool m_isDimming;  

public: 
    CDimWnd(); 
    void Show(); 
    void Hide();    

protected: 
    BOOL OnEraseBkgnd(CDC* pDC); 
    void OnTimer(UINT_PTR nIDEvent); 
    DECLARE_MESSAGE_MAP() 
}; 

CDimWnd.cpp

#include "stdafx.h" 
#include "CDimWnd.h" 
#include "MainFrm.h" 

BEGIN_MESSAGE_MAP(CDimWnd, CFrameWnd) 
    ON_WM_ERASEBKGND() 
END_MESSAGE_MAP() 

CDimWnd::CDimWnd() 
{ 
    // Get the main frame of the application which we want to dim. 
    CMainFrame* pParent = theApp.pMainFrame; 

    // Don't do anything if the main frame doesn't appear to be there 
    if (pParent != NULL) 
    { 
     // Get the client area of the window to dim. 
     CRect rc; 
     pParent->GetClientRect(&rc); 
     pParent->ClientToScreen(&rc);  // convert to screen coordinates 

     // Do some fudging to fit the client area exactly. 
     // Other applications may not need this if the above client area fits already. 
     rc.top += GetSystemMetrics(SM_CYFRAME); 
     rc.top += GetSystemMetrics(SM_CYCAPTION);   // MFC feature pack seems to include caption in client area 
     rc.left -= GetSystemMetrics(SM_CXBORDER); 
     rc.right += GetSystemMetrics(SM_CXBORDER) + 1; 
     rc.bottom += GetSystemMetrics(SM_CYBORDER) + 1; 

     // Create a layered window for transparency, with no caption/border. 
     CreateEx(WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW, NULL, TEXT(""), 
      WS_POPUP, rc.left, rc.top, rc.Width(), rc.Height(), 
      pParent->GetSafeHwnd(), NULL); 
    } 
} 


void CDimWnd::Show() 
{ 
    // If we are not already dimming, go for it 
    if(!m_isDimming) 
    { 
     // Bring in front of main window. 
     BringWindowToTop(); 

     // Set opacity to 0 
     SetLayeredWindowAttributes(RGB(0,0,0), 0, LWA_ALPHA); 

     // Show the dimmer window 
     ShowWindow(SW_SHOW); 

     // Create timer - the rest is handled in OnTimer() function 
     SetTimer(ID_FADE_TIMER, FADE_TIME, NULL); 
    } 
} 


void CDimWnd::Hide() 
{ 
    // If we are dimming, go for it 
    if(m_isDimming) 
    { 
     // Create timer - the rest is handled in OnTimer() function 
     SetTimer(ID_FADE_TIMER, FADE_TIME, NULL); 
    } 
} 


void CDimWnd::OnTimer(UINT_PTR nIDEvent) 
{ 
    static int fade = 0; 

    if(nIDEvent == ID_FADE_TIMER) 
    { 
     // We are dimming => we want to fade out 
     if(m_isDimming) 
     { 
      if(fade < 0) 
      { 
       // Fading finished - hide window completely, update status & destroy timer 
       fade = 0; 
       ShowWindow(SW_HIDE); 
       KillTimer(nIDEvent); 
       m_isDimming = false; 
      } 
      else 
      { 
       // Set window opacity & update fade counter 
       SetLayeredWindowAttributes(RGB(0,0,0), fade, LWA_ALPHA); 
       fade -= FADE_STEP; 
      } 
     } 
     else 
     // fade in 
     { 
      if(fade > TARGET_OPACITY) 
      { 
       // Fading finished - destroy timer & update status 

       fade = TARGET_OPACITY; // but first, let's be accurate. 
       SetLayeredWindowAttributes(RGB(0,0,0), fade, LWA_ALPHA); 

       KillTimer(nIDEvent); 
       m_isDimming = true;    
      } 
      else 
      { 
       // Set window opacity & update fade counter 
       SetLayeredWindowAttributes(RGB(0,0,0), fade, LWA_ALPHA); 
       fade += FADE_STEP; 
      } 
     } 
    } 
} 


BOOL CDimWnd::OnEraseBkgnd(CDC* pDC) 
{ 
    // Fill with black 
    CBrush backBrush(RGB(0, 0, 0)); 
    CBrush* pOldBrush = pDC->SelectObject(&backBrush); 

    CRect rect; 
    pDC->GetClipBox(&rect);  // Erase the area needed 
    pDC->PatBlt(rect.left, rect.top, rect.Width(), rect.Height(), PATCOPY); 

    pDC->SelectObject(pOldBrush); 
    return TRUE; 
} 

bien. Como dije, esto fue lanzado bastante rápido y está en un estado difícil, pero debería darle algún código para trabajar, y una idea general de cómo (creo) los temporizadores se usan en MFC. Definitivamente no soy la persona adecuada para pensar en eso, aunque :)

+0

* Normalmente utilizo Windows API directamente * - Muestra;) En MFC, la creación de ventanas no va al constructor, sino que está separada. De todos modos, +1 jsut para toser una muestra. – peterchen

+0

En realidad, es útil poner la inicialización en el constructor para este caso específico; entonces tiene un "atenuador de ventana con el objetivo" que solo requiere una sola línea que declare un 'CDimWnd' para atenuar un alcance. – AshleysBrain

3

He aceptado la respuesta de oystein, ya que me llevó a la solución, pero pensé en publicar mis modificaciones. Tuve que modificarlo un poco para que funcione para mí, por lo que podría ser útil para otra persona.

Para el registro, el oscurecimiento funciona bien, pero no se ve tan natural como esperaba. En una aplicación que frecuentemente abre diálogos, la atenuación se vuelve molesta en su regularidad de encender y apagar la ventana principal. Para poner en peligro, he hecho el oscurecimiento bastante sutil (alrededor del 25% de opacidad) que resalta suavemente el diálogo activo; el oscurecimiento instantáneo todavía distrae un poco, pero no estoy seguro de cómo desvanecerse o desvanecerse suavemente, especialmente cuando se mira con el objetivo.

Además, no soy un experto en UI, pero el oscurecimiento me dio una especie de impresión de que el diálogo estaba menos relacionado con el contenido de la ventana detrás de él. Esto lo hizo sentir un poco desapegado de lo que estaba trabajando en la aplicación, a pesar de que los diálogos están manipulando directamente ese contenido. Esta podría ser otra distracción.

aquí está de todos modos:

CDimWnd.h

// Dim the application main window over a scope. Creates dimmer window in constructor. 
class CDimWnd : public CFrameWnd 
{    
public: 
    CDimWnd(); 
    BOOL OnEraseBkgnd(CDC* pDC); 

    ~CDimWnd(); 

protected: 
    DECLARE_MESSAGE_MAP() 
}; 

CDimWnd.cpp

#include "stdafx.h" 
#include "CDimWnd.h" 
#include "MainFrm.h" 

BEGIN_MESSAGE_MAP(CDimWnd, CFrameWnd) 
    ON_WM_ERASEBKGND() 
END_MESSAGE_MAP() 

// For preventing two dimmer windows ever appearing 
bool is_dimmer_active = false; 

CDimWnd::CDimWnd() 
{ 
    // Get the main frame of the application which we want to dim. 
    CMainFrame* pParent = theApp.pMainFrame; 

    // Don't do anything if the main frame doesn't appear to be there, 
    // or if there is already dimming happening. 
    if (pParent != NULL && !is_dimmer_active) 
    { 
     // Get the client area of the window to dim. 
     CRect rc; 
     pParent->GetClientRect(&rc); 
     pParent->ClientToScreen(&rc);  // convert to screen coordinates 

     // Do some fudging to fit the client area exactly. 
     // Other applications may not need this if the above client area fits already. 
     rc.top += GetSystemMetrics(SM_CYFRAME); 
     rc.top += GetSystemMetrics(SM_CYCAPTION);   // MFC feature pack seems to include caption in client area 
     rc.left -= GetSystemMetrics(SM_CXBORDER); 
     rc.right += GetSystemMetrics(SM_CXBORDER) + 1; 
     rc.bottom += GetSystemMetrics(SM_CYBORDER) + 1; 

     // Create a layered window for transparency, with no caption/border. 
     CreateEx(WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW, NULL, TEXT(""), 
      WS_POPUP, rc.left, rc.top, rc.Width(), rc.Height(), 
      pParent->GetSafeHwnd(), NULL); 

     // Bring in front of main window. 
     BringWindowToTop(); 

     // Apply 25% opacity 
     SetLayeredWindowAttributes(RGB(0,0,0), 64, LWA_ALPHA); 

     // Show the dimmer window 
     ShowWindow(SW_SHOW); 

     is_dimmer_active = true; 
    } 
} 

CDimWnd::~CDimWnd() 
{ 
    is_dimmer_active = false; 
} 

BOOL CDimWnd::OnEraseBkgnd(CDC* pDC) 
{ 
    // Fill with black 
    CBrush backBrush(RGB(0, 0, 0)); 
    CBrush* pOldBrush = pDC->SelectObject(&backBrush); 

    CRect rect; 
    pDC->GetClipBox(&rect);  // Erase the area needed 
    pDC->PatBlt(rect.left, rect.top, rect.Width(), rect.Height(), PATCOPY); 

    pDC->SelectObject(pOldBrush); 
    return TRUE; 
} 

uso es muy simple: porque CDimWnd crea a sí mismo en su constructor, todo lo que necesita hacer es agregar CDimWnd dimmer como miembro de la clase de diálogo, y atenúa automáticamente la ventana principal, sin importar desde dónde llame el diálogo.

También se puede utilizar dentro de un ámbito para atenuar los cuadros de diálogo modal del sistema:

{ 
    CDimWnd dimmer; 
    MessageBox(...); 
} 
+0

Para desvanecer, puede hacer que un temporizador en el constructor/destructor aumente/reduzca gradualmente la opacidad. A menos que quiera un hilo separado haciendo esto, el fundido debería ser bastante rápido. – Oystein

+0

Eso es lo que ocurre, el fundido no debe retrasar la aparición del diálogo. ¿Es posible que suceda al mismo tiempo que aparece el cuadro de diálogo modal con su transición de fundido de Aero? ¿Seguirá funcionando un temporizador si va un diálogo modal? – AshleysBrain

+0

Descartar el último comentario. Si registra un temporizador de Windows, recibirá un evento tic. Si realiza el desvanecimiento en el controlador de eventos, se desvanecerá independientemente de otros diálogos y no retrasará la ejecución de nada. – Oystein

2

no pude resistir hacerlo.

Es su código con temporizadores adicionales e implementó un fundido de entrada/salida. También cambié para usar gris medio en lugar de negro para el bloque oscurecedor.

Puede ajustar las constantes que controlan el fundido para hacerlo más suave aumentando la duración o aumentando la velocidad. Experimento me muestra que una tasa de 10 Hz es suave para mí, pero tu caso es distinto

// DimWnd.h : header file 
#pragma once 

class CDimWnd : public CFrameWnd 
{ 
public: 
    CDimWnd(class CWnd * pParent); 
    virtual ~CDimWnd(); 
    BOOL OnEraseBkgnd(CDC* pDC); 
    int opacity, opacity_increment; 
protected: 
    DECLARE_MESSAGE_MAP() 

public: 
    afx_msg void OnTimer(UINT_PTR nIDEvent); 
    void fadeOut(); 
}; 

// DimWnd.cpp : implementation file 
// 

#include "stdafx.h" 
#include "dimmer.h" 
#include "DimWnd.h" 
#include "MainFrm.h" 
#include <math.h> 

const int TIMER_ID = 111; 

// For preventing two dimmer windows ever appearing 
bool is_dimmer_active = false; 

// constants to control the fade. 
int ticks_per_second = 1000; // ms 
int start_opacity  = 44; // 20% 
int max_opacity  = 220; // 0->255 
double fade_in_duration = 4; // seconds to fade in (appear) 
double fade_out_duration = 0.2; // seconds to fade out (disappear) 
int rate    = 100; // Timer rate (ms 


CDimWnd::CDimWnd(CWnd * pParent) 
{ 
    // Don't do anything if the main frame doesn't appear to be there, 
    // or if there is already dimming happening. 
    if (pParent != NULL && !is_dimmer_active) 
    { 
     // Get the client area of the window to dim. 
     CRect rc; 
     pParent->GetClientRect(&rc); 
     pParent->ClientToScreen(&rc);  // convert to screen coordinates 

     // Do some fudging to fit the client area exactly. 
     // Other applications may not need this if the above client area fits already. 
     rc.top += GetSystemMetrics(SM_CYFRAME); 
     rc.top += GetSystemMetrics(SM_CYCAPTION);   // MFC feature pack seems to include caption in client area 
     rc.left -= GetSystemMetrics(SM_CXBORDER); 
     rc.right += GetSystemMetrics(SM_CXBORDER) + 1; 
     rc.bottom += GetSystemMetrics(SM_CYBORDER) + 1; 

     // Create a layered window for transparency, with no caption/border. 
     CreateEx(WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW, NULL, TEXT(""), 
      WS_POPUP, rc.left, rc.top, rc.Width(), rc.Height(), 
      pParent->GetSafeHwnd(), NULL); 

     // Bring in front of main window. 
     BringWindowToTop(); 

     // Show the dimmer window 
     ShowWindow(SW_SHOW); 


     double increment_per_second = ((max_opacity - start_opacity)/fade_in_duration); 
     opacity_increment = ceil( increment_per_second/(ticks_per_second/rate)) ; 

     is_dimmer_active = true; 
     opacity = start_opacity; 

     SetLayeredWindowAttributes(RGB(0,0,0), opacity, LWA_ALPHA); 

     SetTimer(TIMER_ID, rate,NULL); 

    } 
} 

CDimWnd::~CDimWnd() 
{ 
    fadeOut(); // fade the window out rather than just disappearing. 
    is_dimmer_active = false; 
} 

void CDimWnd::fadeOut() 
{ 
    // can't use timers as may be in the process of being destroyed so make it quick.. 

    double increment_per_second = ((opacity - start_opacity)/fade_out_duration); 
    opacity_increment = ceil( increment_per_second/(ticks_per_second/rate)) ; 

    while(opacity > start_opacity) 
    { 
     opacity -= opacity_increment; 
     SetLayeredWindowAttributes(RGB(0,0,0), opacity, LWA_ALPHA); 
     Sleep(100); 
    } 
} 

BOOL CDimWnd::OnEraseBkgnd(CDC* pDC) 
{ 
    // Fill with midgray 
    CBrush backBrush(RGB(128,128,128)); 
    CBrush* pOldBrush = pDC->SelectObject(&backBrush); 

    CRect rect; 
    pDC->GetClipBox(&rect);  // Erase the area needed 
    pDC->PatBlt(rect.left, rect.top, rect.Width(), rect.Height(), PATCOPY); 

    pDC->SelectObject(pOldBrush); 
    return TRUE; 
} 

BEGIN_MESSAGE_MAP(CDimWnd, CFrameWnd) 
    ON_WM_ERASEBKGND() 
    ON_WM_TIMER() 
END_MESSAGE_MAP() 

void CDimWnd::OnTimer(UINT_PTR nIDEvent) 
{ 
    if (opacity >= max_opacity) 
    { 
     // stop the timer when fade in finished. 
     KillTimer(TIMER_ID); 
     return; 
    } 

    opacity += opacity_increment; 
    SetLayeredWindowAttributes(RGB(0,0,0), opacity, LWA_ALPHA); 

    CFrameWnd::OnTimer(nIDEvent); 
} 
+0

Parece ser una forma limpia de hacerlo, siempre y cuando solo necesite el modelo de alcance, y no necesite un largo fundido de salida. Es más simple que mi versión, seguro :) – Oystein

Cuestiones relacionadas