Вы можете создать другое окно, полностью черное, поверх окна, которое вы хотите затемнить, и установить непрозрачность черного окна с помощью SetLayeredWindowAttributes . Конечно, он не обязательно должен быть черным, но я думаю, что это лучший цвет для затемнения.
РЕДАКТИРОВАТЬ: я взломал пример - но учтите, что я не разработчик MFC, я обычно использую Windows API напрямую. Кажется, все работает хорошо, хотя.
Здесь - это пастин. Не стесняйтесь добавлять постепенные изменения и т. Д. Самостоятельно. Также обратите внимание, что это затемняет весь экран, вам придется изменить размер моего окна затемнения, если вы не хотите этого поведения. Смотрите комментарии к коду.
/**********************************************************************************************
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, и я оставил код в «грубом» состоянии (небольшая обработка ошибок, не очень надежная), чтобы дать вам кое-что еще. :) Во всяком случае, вот один из способов сделать это, который я считаю достаточно чистым:
Чтобы использовать его, сделайте, чтобы ваше главное окно содержало окно диммера
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();
В качестве альтернативы, я думаю, вы могли бы поместить этот код (Show()
/ Hide()
вызовы) в конструктор и деструктор модального диалога, если вы хотите сохранить код там. Если вам нужен «scope» -dim, как в примере, который вы опубликовали, этот код должен был бы войти в конструктор и деструктор класса CDimWnd, и вам понадобится что-то вроде статической переменной-члена, чтобы гарантировать, что только один диммер выполняется одновременно (если вы не хотите использовать глобальную переменную).
Для окна диммера - я сделал это:
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. Я определенно не тот человек, чтобы думать об этом, хотя:)