MFC - 显示模态对话框时,调暗主窗口 [英] MFC - dim main window when showing modal dialog

查看:374
本文介绍了MFC - 显示模态对话框时,调暗主窗口的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个相当标准的MFC应用程序,包括一个主窗口,偶尔提出模态对话框。因为我们都知道一个模态对话框之外什么都不能做,除非它被关闭。

I have a fairly standard MFC application that consists of a main window, and occasionally brings up modal dialogs. As we all know nothing can be done outside a modal dialog until it is closed.

因此,一个不错的UI功能是昏暗主窗口的其余部分对话框,直观地指示你不能使用它,直到你完成了模态对话框。一些web应用程序和java / mac应用程序做到这一点,但我从来没有看到它在传统的C ++ / MFC应用程序。我想尝试一下,即使这个平台是不寻常的。

Therefore, a nice UI feature is to "dim" the rest of the main window behind the dialog, to visually indicate you can't use it until you're done with the modal dialog. Some web apps and java/mac apps do this, but I've never seen it done in a traditional C++/MFC application. I'd like to give it a try, even if it's unusual for the platform.

如何做到这一点?我在应用程序中有几个模式对话框,用于此模式:

How can this be done? I have several modal dialogs in the application, used in this pattern:

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

有一个简单的方法让窗口在任何DoModal之后?我使用Visual Studio 2010,以便更新的MFC具有任何可能有帮助的功能。

Is there an easy way to have the window dimmed before any DoModal() and restored afterwards? I'm using Visual Studio 2010 in case the updated MFC has any features that might help.

编辑:我发布了一个基于oystein的答案的解决方案,

推荐答案

您可以在要调暗的窗口的顶部创建另一个完全黑色的窗口,然后使用SetLayeredWindowAttributes 。它不一定是黑色的,当然,但我想这是最好的调光颜色。

You can create another window, completely black, on top of the window you want to dim, and set the black window's opacity with SetLayeredWindowAttributes. It doesn't have to be black, of course, but I guess that's the best dimming color.

编辑:我一起黑客一个例子 - 但注意,我不是一个MFC开发人员,我通常直接使用Windows API。它似乎工作好,虽然。
这里是一个pastebin。随意添加淡入等你自己。还要注意,这使整个屏幕变暗,如果你不想要这种行为,你必须调整我的调光窗口。查看代码注释。

I hacked together an example - but note that I am not an MFC developer, I usually use the Windows API directly. It seems to work okay, though. Here is a pastebin. Feel free to add fade-ins etc. yourself. Also note that this dims the entire screen, you'll have to resize my dimming-window if you don't want this behaviour. See code comments.

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

    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;






UPDATE:

我为您添加了一些代码,以处理衰落。我仍然没有MFC开发,我离开的代码在一个粗糙状态(小错误处理,不是很强大)给你一些也做。 :)无论如何,这里有一种方法,我认为是相当干净:

I hacked together some more code for you, to handle the fading. I'm still no MFC dev, and I left the code in a "rough" state (little error handling, not very robust) to give you something to do too. :) Anyway, here's one way to do it, that I think is fairly clean:

要使用它,使你的主窗口包含一个更暗的窗口

To use it, make your main window contain a dimmer window

class CMainFrm : public CFrameWnd
{     
    CDimWnd* dimmer; 

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

// rest of class ...

};  

像这样:

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



< c $ c> / 隐藏()调用)在模态对话框的构造函数和析构函数,如果你想保持代码。如果你想要一个scope-dim,就像你发布的例子,这个代码将不得不进入构造函数& CDimWnd类的析构函数,你需要一个类似静态成员变量来确保一次只运行一个调光器(除非你想使用全局变量)。

Alternatively I guess you could put this code (Show()/Hide() calls) in the constructor and destructor of the modal dialog, if you want to keep the code there. If you want a "scope"-dim, like in the example you posted, this code would have to go in the constructor & destructor of the CDimWnd class, and you would need something like a static member variable to ensure that only one dimmer is running at a time (unless you want to use a global variable).

对于调光器窗口 - 我这样做:

For the dimmer window - I did this:

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

好的。正如我所说,这是相当快地扔在一起,并处于粗糙的状态,但它应该给你一些工作的代码,以及如何(我认为)计时器在MFC中使用的一般想法。我绝对不是正确的人来思考一下,虽然:)

Okay. As I said, this was thrown together fairly quickly and is in a rough state, but it should give you some code to work from, and a general idea of how (I think) timers are used in MFC. I am definitely not the right person to think anything about that, though :)

这篇关于MFC - 显示模态对话框时,调暗主窗口的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆