[置顶] 关于控件的背景透明

世界上总是有些东西让你不得不用,因为别人都在用,比如钱和OLE

世界上总有些东西让你很不想用,因为用起来很纠结,比如钱和OLE

世界上总有问题时让你废寝忘食,以期解决它,还是他妈的钱和OLE

开发一个背景透明的控件,有很多方法了,比如很多用MFC的会重载对话框的OnCtlColor来透明子控件,效果不错

但是这种方法仅限于MFC,即便扩展到其他C++编译平台,都可能不适用,更不用说转移到其他语言了

我写一个小程序,当然一个小程序投入越少越好,尤其是时间,首选是VB,画两下就OK,又不用装.NETFX,不用装其他运行库,不用装B

然而VB要实现透明,比如文本框,星爷说了,"哪里不舒服都要吃药,没别的办法可行啊",只能子类化,用Windows API

但是子类化又给VB带来一个问题,当程序异常或直接用End语句退出时,the process will crash,然后Dong!插插插指令引用叉叉叉内存不能为Read...

如果你想设TextBox1.BackStyle = 0,不好意思,微软说了,我不穿透明的迷你裙,我是传统女性...

看样子我们只能自己创造一个性感的了(it looks like we need to create a sexy one of own!)

ATL(ActiveX Template Library)是面向COM的,做界面还是MFC简单,于是(有些场所即便污秽不堪,为了生活还是要去啊)

用MFC设计一个控件,相对来说可以引用大部分MFC透明对话框控件的方法了,但是我们的主角换了,现在控件说:"我才是家长!"

我们先看看前任,就是MFC中透明一个子控件的方法吧.

1.给控件绘制位图背景

// CEditExCtrl message handlers

HBRUSH CEditExCtrl::CtlColor(CDC* pDC, UINT nCtlColor) 
{
	//设置背景透明
	pDC->SetBkMode(TRANSPARENT);

	//设置编辑框中字体颜色
	pDC->SetTextColor(RGB(0xff, 0x0f, 0x0f));

	//返回空笔刷
	return m_brHollow;
}

void CEditExCtrl::OnLButtonUp(UINT nFlags, CPoint point) 
{
	Invalidate();
	CEdit::OnLButtonUp(nFlags, point);
}

void CEditExCtrl::OnChange() 
{
	CWnd::Invalidate();
}

BOOL CEditExCtrl::OnEraseBkgnd(CDC* pDC) 
{
	BITMAP bmp;
	m_bmp.GetBitmap(&bmp);
	m_currBmp = &m_bmp;
	CDC dcMem;
	dcMem.CreateCompatibleDC(pDC);
	CBitmap* pOldBitmap = dcMem.SelectObject(m_currBmp);
	pDC->BitBlt(0, 0, bmp.bmWidth, bmp.bmHeight, &dcMem, 0, 0, SRCCOPY);
	dcMem.SelectObject(pOldBitmap);

    return TRUE; 
}

仅仅搞定这三个地方,运行时随便你怎么扭腰了,在初始化的时候赋值m_brHollow为空画刷,给m_bmp加载一副位图即可.

也可以在运行时动态改变背景位图,这个简单了,API和很多OLE接口都能做到

2.动态绘制父窗口的背景,要改变的只是在擦除背景的时候,动态的把父窗口的背景画上去

BOOL CEditExCtrl::OnEraseBkgnd(CDC* pDC) 
{
	/*
	BITMAP bmp;
	m_bmp.GetBitmap(&bmp);
	m_currBmp = &m_bmp;
	CDC dcMem;
	dcMem.CreateCompatibleDC(pDC);
	CBitmap* pOldBitmap = dcMem.SelectObject(m_currBmp);
	pDC->BitBlt(0, 0, bmp.bmWidth, bmp.bmHeight, &dcMem, 0, 0, SRCCOPY);
	dcMem.SelectObject(pOldBitmap);
	*/
	HWND cWnd = CWnd::GetSafeHwnd();//子窗口句柄
	CWnd *pWnd = CWnd::GetParent();//父窗口句柄
	RECT cRect;
	//CWnd::GetClientRect(&cRect);
	CWnd::GetWindowRect(&cRect);
	HBITMAP bitmap = CreateCompatibleBitmap(pDC->GetSafeHdc(), cRect.right - cRect.left, cRect.bottom - cRect.top);
	CDC memDC;
	memDC.CreateCompatibleDC(NULL);
	//HGDIOBJ
	if(m_bmp.m_hObject)
	{
		m_bmp.DeleteObject();
	}
	m_bmp.FromHandle(bitmap);
	CBitmap *oldBitmap = memDC.SelectObject(&m_bmp); //此处可以调用SetClipRect()等函数来限制绘制范围
	pWnd->SendMessage(WM_ERASEBKGND, (WPARAM)memDC.GetSafeHdc(), 0);
	pWnd->SendMessage(WM_PAINT, (WPARAM)memDC.GetSafeHdc(), 0); //至此memDC上已经保存了父窗口的背景内容
	//用户可以调用BitBlt(...)等函数拷贝memDC的内容到子窗口的某个区域,这样就达到了透明效果;
	pWnd->ScreenToClient(&cRect);
	pDC->BitBlt(0, 0, cRect.right - cRect.left, cRect.bottom - cRect.top, &memDC, cRect.left, cRect.top, SRCCOPY);
	memDC.SelectObject(oldBitmap);
	memDC.DeleteDC();
	//DeleteObject(bitmap); 
	OutputDebugString("::OnEraseBkgnd()");

    return TRUE; 
}

看起来很简单,但是对于EDIT会带来一个问题,EDIT在内容变更的时候,会出现文字重影,而且选中部分的背景色也不会消除

难道我们要自己重绘?当然可以,不过重绘很麻烦,可以投机取巧用CEdit的接口,但是我没试过

那么我们可以不可以返回一个位图画刷呢?当然可以,在CtlColor中除了设置透明等外,加上代码:

	CBrush cbr;
	CWnd *pWnd;
	RECT rc;
	CWnd::GetWindowRect(&rc);
	pWnd = CWnd::GetParent();
	pWnd->ScreenToClient(&rc);
	cbr.CreatePatternBrush(&m_bmp);
	pDC->SetBrushOrg(rc.left, rc.top);	//SetBurshOrgEx
	return cbr;


SetBrushOrg是调整画刷的坐标,无论调不调整,都会发现,背景被搞成白色了,至少我的情况是这样,可能是我的图片是白色开始的

看来返回画刷的方法,不太可行,至少想玩一夜Q的朋友(我的意思是想贪便宜,简单处理的朋友,你懂的)是没很难了

怎么办,难道我就要在这里hang myself?观察发现窗体隐藏再显示的时候就正常了,那么隐藏再显示?No,No,No,那样你的Edit中文本被清空了

直接重绘不就完了吗?

void CEditExCtrl::OnChange() 
{
	RECT rc;
	CWnd *pWnd = CWnd::GetParent();
	CWnd::GetWindowRect(&rc);
	//CWnd::Invalidate();
	pWnd->InvalidateRect(&rc);	// 输入没问题,选中或者覆盖还是重影
	//pWnd->Invalidate();	// 绝对可以,但是效率低
}


那么至于用哪种方法,就看你自己的爱好了,人是你的,你想怎么玩就怎么玩,回归C/C++本性

现在我们放到自己设计的控件中来,设计总从COleControl派生的,而COleControl是从CWnd派生,且总是有WS_CHILD标识的

只需要注意的是,不要重载OnCtlColor,而是CtlColor。CtlColor?没听说过,在哪里啊?

子窗口(控件)在绘制的时候,会向父窗口发送WM_CTLCOLOR消息,父窗口为了子窗口能自己处理又反射一条WM_CTLCOLOR_REFLECT回来

映射这条消息,就是CtlColor,也就是MFC里面那个样式,但是AppWizard里面没有这条消息,要手动映射:

在.cpp文件中

BEGIN_MESSAGE_MAP(CEditExCtrl, CEdit)
	//{{AFX_MSG_MAP(CEditExCtrl)
	ON_WM_CTLCOLOR_REFLECT()
	ON_WM_LBUTTONUP()
	ON_CONTROL_REFLECT(EN_CHANGE, OnChange)
	//}}AFX_MSG_MAP
	ON_WM_ERASEBKGND()
END_MESSAGE_MAP()


在.h文件中

	//{{AFX_MSG(CEditExCtrl)
	afx_msg HBRUSH CtlColor(CDC* pDC, UINT nCtlColor);
	afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
	afx_msg void OnChange();
	//}}AFX_MSG
	afx_msg BOOL OnEraseBkgnd(CDC* pDC);	

	DECLARE_MESSAGE_MAP()


那么效果呢?有图有真相,无图娘娘腔..



vb中:

Private Sub Form_Load()
    TextBox1.Picture = Image1.Picture
End Sub


当然啦,如果你不是Edit而是纯粹的覆盖背景,你可以用Picture,加载缝合的图片,如果你的PictureBox之类的是移动的,需要动态绘制,那也简单

Private Sub Picture1_KeyDown(KeyCode As Integer, Shift As Integer)
    If KeyCode = vbKeyLeft Then
        Picture1.Left = Picture1.Left - 1
        Picture1.Refresh
    ElseIf KeyCode = vbKeyRight Then
        Picture1.Left = Picture1.Left + 1
        Picture1.Refresh
    ElseIf KeyCode = vbKeyUp Then
        Picture1.Top = Picture1.Top - 1
        Picture1.Refresh
    ElseIf KeyCode = vbKeyDown Then
        Picture1.Top = Picture1.Top + 1
        Picture1.Refresh
    End If
End Sub

Private Sub Picture1_Paint()
    If Me.Picture.Handle = 0 Then
        Exit Sub
    End If
    Picture1.PaintPicture Me.Picture, 0, 0, Picture1.Width, Picture1.Height, Picture1.Left, Picture1.Top, Picture1.Width, Picture1.Height, vbSrcCopy
End Sub


 

原文地址:https://www.cnblogs.com/keanuyaoo/p/3329117.html