透明控件的通用解决方案
一种非常简单、集成的方法,使诸如按钮、滑块控件和进度控件等控件在对话框中具有透明效果。
引言
在UI开发中,我们通常需要实现一些漂亮的特效,而使某些控件具有透明效果是我们经常遇到的问题。 在本文中,我介绍了一种实现控件透明度的方法。 源代码包含基于 Win32 API 的类 CTransparentHelper
,它可以在 MFC、ATL、WTL、Win32 应用程序中使用,也可能在其他框架中使用。 我开发了一个新版本,因为我在工作中使用了它,所以如果您有任何建议、错误报告或问题,请发送给我。 此外,您还可以访问 我的技术博客 以获取更多信息。
背景
我编写此代码是因为我需要一些透明控件,例如按钮、滑块控件和进度控件。 我发现当父窗口移动或控件移动时,在线上的一些方法无效;当我们希望在可调整大小的对话框中使用透明控件时,这是一个问题。 因此,我编写了 CTransparentHelper
; 当调用 MoveWindow
时,您可以获得平滑的透明效果,并且该类适用于所有控件。
特点
- 没有框架依赖。
- 可用于不同类型的控件。
- 可用于多层透明。
- 在现有代码中使用简单。
使用代码
在介绍如何使用源代码之前,我建议使用内存 DC 来存储父对话框的背景 DC。 这对于子控件的透明度很方便,并且还提高了绘图效率。 有关详细信息,请参阅源代码。
包含 “TransparentHelper.h”
在需要透明的控件的文件中包含 “TransparentHelper.h”。 并添加一个类型为 CTransparentHelper
的对象。
#pragma once #include "TransparentHelper.h" // CSliderCtrlEx class CSliderCtrlEx : public CSliderCtrl { ……… CTransparentHelper m_objTrans; }
初始化 CTransparentHelper 对象
void CSliderCtrlEx::PreSubclassWindow() { // TODO: Add your specialized code here and/or call the base class ……. CSliderCtrl::PreSubclassWindow(); m_objTrans.Install( GetSafeHwnd()); …… }
在您需要时调用 CTransparentHelper 的 TransparentBk 函数
BOOL CSliderCtrlEx::OnSliderDrawChannel( CDC* pDC, CRect& rect, UINT nState) { …… if ( m_objTrans.IsValid() ) { m_objTrans.TransparentBk( pDC->GetSafeHdc(), GetSafeHwnd()); } ……… return TRUE; }
将代码添加到控件的父窗口
有时,父窗口是一个对话框。 我需要处理消息 WM_TRANSPARENT_BK
,该消息从透明控件发送,以便获取 DC。
LRESULT CTransparentControlDlg::OnTransaprentBk( WPARAM wParam, LPARAM lParam) { HDC hdc = ( HDC)wParam; HWND hwnd = ( HWND)lParam; CTransparentHelper::OnTranparentControl( m_pMemDC->GetSafeHdc(), (WPARAM)hdc, (LPARAM)hwnd); return TRUE; }
备注:m_pMemDC
是对话框的内存 DC,当对话框的大小改变时,它将会改变。
当背景改变时通知子控件
当对话框的内存 DC 改变时,它必须通知带有透明标签的子控件。
void CTransparentControlDlg::BuildBkDC() { //rebuild the background dc ........ //when the parent dialog's background is rebuild, //notify the child which has an transparent tag. CTransparentHelper::NotifyTransparentChild( GetSafeHwnd()); }
消息 WM_NOTIFY_TRANSPARENT
当背景改变时,透明控件需要处理由父控件发送的消息 WM_NOTIFY_TRANSPARENT
。
LRESULT CSliderCtrlEx::OnNotifyTransparent( WPARAM wParam, LPARAM lParam) { if ( ::IsWindowEnabled( GetSafeHwnd())) { ::EnableWindow( GetSafeHwnd(),FALSE); ::EnableWindow( GetSafeHwnd(),TRUE); } else { ::EnableWindow( GetSafeHwnd(),TRUE); ::EnableWindow( GetSafeHwnd(),FALSE); } //This operation is for the repaint of slider control, //because Invalidate cann't bring the NM_CUSTOMDRAW message. //M..., this may not the best method to solve the problem. //If you have other method, please tell me. return TRUE; }
备注:对于某些绘图方法下的某些控件(例如,NM_CUSTOMDRAW
),Invalidate
不会引起真正的重绘。 因此,我添加了 WM_NOTIFY_TRANSAPRENT
消息以使其兼容。 如果透明控件在调用 Invalidate
后会自行重绘,则它不需要处理该消息。