T类,大概了解了ST类透明按钮的制作方法,对于MFC的函数调用机制仍然不是很了解,
本文待修改。
其实透明按钮并不是透明的,只是在第一次绘制按钮时将按钮矩形区域(CRect)的图片保存了起来,
并且在按钮重绘时再将保存的图片显示出来的过程,下面给出一个简单的处理方法:
(加粗部分为添加的代码)
(1)重载CButton类:
class CMyButton : public CButton
(2)为CMyButton类添加成员变量:
CDC m_dcBk;
CBitmap m_bmpBk;
CBitmap* m_pbmpOldBk;
int m_drawCount;
(3)在类向导中重载函数:
1、void CMyButton::PreSubclassWindow()
{
// TODO: 在此添加专用代码和/或调用基类
ModifyStyle(0, BS_OWNERDRAW);//设置按钮类得自绘风格
CButton::PreSubclassWindow();
}
2、void CmyButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
// TODO: 添加您的代码以绘制指定项
CDC *pDC = this ->GetDC();//获取DC指针
CClientDC clDC(GetParent());//获取父窗口客户DC
CRect rect;//用于保存本Button在父窗口中的区域
CRect rect1;//用于保存父窗口的区域
GetClientRect(rect);
GetWindowRect(rect1);
GetParent()->ScreenToClient(rect1);
if (m_dcBk.m_hDC == NULL && m_drawCount >= 1)//注意:m_drawCount是我自己添加的变量
//因为每次初始化时DrawItem函数都会绘制两次,这点现在我也不是很明白,所以让本段代码在第
//二次之后(包含)再进行绘制操作
{
m_dcBk.CreateCompatibleDC(&clDC);
m_bmpBk.CreateCompatibleBitmap(&clDC, rect.Width(), rect.Height());//注意:这里的
//函数调用保存了按钮矩形区的图像,用于重绘时调用
m_pbmpOldBk = m_dcBk.SelectObject(&m_bmpBk);
m_dcBk.BitBlt(0, 0, rect.Width(), rect.Height(), &clDC, rect1.left, rect1.top, SRCCOPY);
} // if
else
{
m_drawCount++;
}
pDC->BitBlt(0, 0, rect.Width(), rect.Height(), &m_dcBk, 0, 0, SRCCOPY);
}//至此,背景图像的保存和重绘已经完成了
下面变可以根据自己的喜好重载函数
void CMyButton::OnMouseMove(UINT nFlags, CPoint point)
void CMyButton::OnLButtonDown(UINT nFlags, CPoint point)
void CMyButton::OnLButtonUp(UINT nFlags, CPoint point)
-------------------------------------------------------
整理一下自己学习自绘透明按钮的实现和代码;在各个搜索网站上游走发现子类化自绘按钮的资料很多,但是大多年头很久远了。
问题整理:
1.子类化自绘控件背景透明如何达成,
1.1.主窗口ON_WM_CTLCOLOR()消息设置背景透明(不易于扩展与维护);
HBRUSH CMFCApplication2Dlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr = CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor);
// TODO: 在此更改 DC 的任何特性
if (pWnd->GetDlgCtrlID() == IDC_CLOSE || pWnd->GetDlgCtrlID() == IDC_MINI) //获取控件ID
{
pDC->SetBkMode(TRANSPARENT); //设置背景透明
return HBRUSH(GetStockObject(NULL_BRUSH)); //返回透明画刷
}
// TODO: 如果默认的不是所需画笔,则返回另一个画笔
return hbr;
}
2021年4月3日更新:
在主对话框中设置透明可以避免因贴图大小与窗口大小不一致导致的背景移位!
1.2.在子类中先贴背景在贴前景(易于扩展与维护);
void CloseButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
Gdiplus::Graphics graphics(lpDrawItemStruct->hDC); //GDI+初始化
CRect rect;
GetClientRect(rect); //获取控件大小
CWnd* pParent = (CWnd*)GetParent(); //获取主窗口指针
CPoint pt(0, 0);
MapWindowPoints(pParent, &pt, 1); //转换客户端坐标点
//TRACE(L"pt:%d,%d,%d,%d\n", pt.x, pt.y, rect.Width(), rect.Height());
Image bak(L"./res/mian.png", FALSE); //加载背景图片;可以是外部传入
graphics.DrawImage(&bak, 0, 0, pt.x, pt.y, rect.Width(), rect.Height(), UnitPixel); //在控件位置贴背景图片
//下面就是做控件形态与控件前景贴图,可以参与混合贴图
}
2021年4月3日更新:
注意:背景图必须与窗口大小一致,如不一致将导致控件背景与窗口背景移位!
2.绘制图像闪烁(闪烁的解决办法就是内存贴图,这是最好的解决办法,网上方法很多)
3.自绘按钮无法在PreSubclassWindow()或PreCreateWindow()中设置控件圆角或其他形态,必须在DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)设置(常见的坑)。
4.自绘异形控件在使用Invalidate(FALSE);必须是FALSE,否则形态会改变为初始形态。无论你在任何子类中使用此函数都必须为FALSE。