世界上总是有些东西让你不得不用,因为别人都在用,比如钱和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