设置窗口分层透明逐渐消失要用到一个函数:SetLayeredWindowAttributes
此函数用于设置分层窗口透明度,常和UpdateLayeredWindow 函数结合使用。
先看看该函数的原型和参数:
C++申明:
BOOLSetLayeredWindowAttributes(
HWNDhwnd, // 指定分层窗口句柄
COLORREFcrKey, // 指定需要透明的背景颜色值,可用RGB()宏
BYTEbAlpha, // 设置透明度,0表示完全透明,255表示不透明
DWORDdwFlags // 透明方式
);
其中,dwFlags参数可取以下值:
LWA_ALPHA时:crKey参数无效,bAlpha参数有效;
LWA_COLORKEY:窗体中的所有颜色为crKey的地方将变为透明,bAlpha参数无效。其常量值为1。
LWA_ALPHA| LWA_COLORKEY:crKey的地方将变为全透明,而其它地方根据bAlpha参数确定透明度。
注:要使使窗体拥有透明效果,首先要有WS_EX_LAYERED扩展属性,方法可以在CreateWindowEx时指定,也可以SetWindowLong动态设置。如下代码可设置分层窗口:
LONG t =GetWindowLong(m_hWnd, GWL_EXSTYLE);
t |=WS_EX_LAYERED;
SetWindowLong(m_hWnd,GWL_EXSTYLE, t);
一般可以再初始化函数中,即OnInitDialog函数中设置eg:
Void XXX:: OnInitDialog
{
UIDialog::OnInitDialog();
SetWindowLong(this->GetSafeHwnd(),GWL_EXSTYLE,GetWindowLong(this->GetSafeHwnd(),GWL_EXSTYLE)|WS_EX_LAYERED);
}
这里还有一点需要注意: 设置时当透明度变化后,如果鼠标移动到对话框上时,应该透明度变回来,窗口不消失,这时,如果整个窗口被子控件充满的话(如下图1所示的),则不会响应鼠标移动WM_MOUSEMOVE消息。如果要响应需要控件类父类的OnMouseMove函数中post消息通知父窗口就会响应。图1
实现该功能的主要代码为:(一)
xxx.h文件
class COnlineTipDlg : public CDialog
{
public:
<span style="white-space:pre"> </span>COnlineTipDlgCOnlineTipDlg(int nVehicleId, CWnd* pParent = NULL); // 标准构造函数
<span style="white-space:pre"> </span>virtual ~COnlineTipDlg();
protected:
virtual BOOL OnInitDialog();
HANDLE m_hDisappear;
public:
int m_nDisAppearCount;
CRITICAL_SECTION m_csDisAppearCount;
BOOL m_bMouseHover;
BOOL m_bMouseTrack;
static DWORD WINAPI AutoDisappear(LPVOID pParam);
void ResetTimeInterVal();
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
afx_msg void OnMouseLeave();
};
xxx.cpp文件
#define DISAPPEARTIME (10 * 1000)
#define DISAP_TOTALTIMES 50
#define TOTAL_ALPHA 255
// COnlineTip 对话框
COnlineTipDlg::COnlineTipDlg(int nVehicleId, CWnd* pParent /*=NULL*/)
: UIDialog(COnlineTipDlg::IDD, pParent)
,m_bMouseHover(FALSE)
, m_bMouseTrack(TRUE)
, m_hDisappear(NULL)
, m_nDisAppearCount(0)
{
InitializeCriticalSection(&m_csDisAppearCount); //锁
}
COnlineTipDlg::~COnlineTipDlg()
{
}
BOOL COnlineTipDlg::OnInitDialog()
{
UIDialog::OnInitDialog();
......
/*一个简单处理方法是设置定时器,半分钟后关闭(隐藏)
SetTimer(TIME_ID_VEHICLE_STATE, TIME_ELASPE_VEHICLE_STATE, NULL);或者将此设置透明窗口放在定时器中完成即可*/
m_nDisAppearCount = DISAP_TOTALTIMES;
SetWindowLong(this->GetSafeHwnd(),GWL_EXSTYLE,
GetWindowLong(this->GetSafeHwnd(),GWL_EXSTYLE) | WS_EX_LAYERED); //要使使窗体拥有透明效果,必须要有WS_EX_LAYERED扩展属性
m_hDisappear = CreateThread(NULL, 0, AutoDisappear, this, 0, NULL); //起线程专门干这件事
return TRUE;
}
DWORD WINAPI COnlineTipDlg::AutoDisappear(LPVOID pParam)
{
COnlineTipDlg* pMan = (COnlineTipDlg*)pParam;
if (!pMan)
{
return -1;
}
BYTE btAlpha = TOTAL_ALPHA;
float fDisappear = 1;
int nTimeCount = DISAPPEARTIME;
int nInterval = nTimeCount / pMan->m_nDisAppearCount;
while(pMan->m_nDisAppearCount != 0)
{
if (pMan->m_bMouseHover)
{
pMan->ResetTimeInterVal();
}
else
{
CGuard guard(&pMan->m_csDisAppearCount);
-- pMan->m_nDisAppearCount;
}
fDisappear = pMan->m_nDisAppearCount * 1.0 / DISAP_TOTALTIMES;
btAlpha = TOTAL_ALPHA * fDisappear;
::SetLayeredWindowAttributes(pMan->GetSafeHwnd(), 0, btAlpha, LWA_ALPHA); //此函数用于设置分层窗口透明度,常和 UpdateLayeredWindow 函数结合使用
Sleep(nInterval);
}
return 0;
}
void COnlineTipDlg::ResetTimeInterVal()
{
CGuard guard(&m_csDisAppearCount);
m_nDisAppearCount = DISAP_TOTALTIMES;
}
void COnlineTipDlg::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
if (m_bMouseTrack) // 若允许 追踪,则。
{
TRACKMOUSEEVENT csTME;
csTME.cbSize = sizeof (csTME);
csTME.dwFlags = TME_LEAVE|TME_HOVER;
csTME.hwndTrack = m_hWnd ;// 指定要 追踪 的窗口
csTME.dwHoverTime = 10; //
::_TrackMouseEvent (&csTME); // 开启 Windows 的 WM_MOUSELEAVE , WM_MOUSEHOVER 事件
m_bMouseTrack=FALSE ; // 若已经追踪 ,则不在追踪
}
//SetCursor(LoadCursor(NULL,IDC_HAND)); //设置鼠标样式为小手
ResetTimeInterVal();
m_bMouseHover = TRUE;
UIDialog::OnMouseMove(nFlags, point);
/*这里有一点需要注意:这里的对话框中被一个列表全部塞满,因为子控件不响应WM_MOUSEMOVE消息,所以需要在父类的OnMouseMove
函数中post消息通知父窗口就会响应*/
}
void COnlineTipDlg::OnMouseLeave()
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
m_bMouseHover = FALSE;
m_bMouseTrack = TRUE;
UIDialog::OnMouseLeave();
}
(二) 以上代码是开启了新的线程。但根据我的项目环境,窗口全透明之后会有一个透明的对话框边界 留在界面上,这时可以将对话框在透明度变为全透明后将其隐藏掉。如果开了线程的不太好做,所以可以在定时器中实现,具体做法,待敞口透明度变为0之后调用showWindow(SW_HIDE) 将其隐藏。主要代码还用以上代码修改:在自己的逻辑中的适当位置设置定时器(可在OnInitDialog中设置),然后响应Ontimer函数;
void COnlineTipDlg::OnTimer(UINT_PTR nIDEvent)
{
switch(nIDEvent)
{
case TIME_ID_VEHICLE_STATE:
if (m_bMouseHover) //鼠标悬浮则不透明
{
m_nDisAppearCount = 255;
}
else
{
m_nDisAppearCount -= 3; //发现这里如果-4 减不到0 ,所以消失又会弹出来,最好用百分比,上面代码可以直接复用即可
}
SetLayeredWindowAttributes(0, m_nDisAppearCount, LWA_ALPHA);
if (m_nDisAppearCount == 0)
{
ShowWindow(SW_HIDE);
}
break;
}
UIDialog::OnTimer(nIDEvent);
}
还需要将以上OnMouseMove函数中的父窗口改为子控件的句柄即修改这行代码:
<span style="white-space:pre"> </span>csTME.hwndTrack = m_ctrlListOnlineTip.GetSafeHwnd() ;
因为穿够哦位置原因,最好对消息进行过滤,如下:
BOOL COnlineTipDlg::PreTranslateMessage(MSG* pMsg) //PreTranslateMessage是消息在送给TranslateMessage函数之前被调用.
{
if (pMsg->hwnd == m_ctrlListOnlineTip.GetSafeHwnd())
{
switch(pMsg->message)
{
case WM_MOUSELEAVE:
{
OnMouseLeave();
}
break;
case WM_MOUSEHOVER:
{
OnMouseHover(pMsg->wParam, pMsg->lParam);
}
break;
default:
break;
}
}
return UIDialog::PreTranslateMessage(pMsg);
}
完美解决!!!
大致就这些!!希望对大家有用!!