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


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


     

  • 相关阅读:
    创建SSIS包—ETL中典型的数据清洗
    SSIS中的容器和数据流—数据转换(Transformations)
    创建SSIS包—循环和动态package
    SSIS中的容器和数据流—数据转换(Transformations)续
    创建SSIS包—建立端到端的package
    SQL点滴9—SQL Server中的事务处理以及SSIS中的内建事务
    SSIS中的容器和数据流—调试工具数据视图
    SQL点滴11—重置win7登录密码影响SQL登录
    SQL点滴10—使用with语句来写一个稍微复杂sql语句,附加和子查询的性能对比
    SSIS中的容器和数据流—举例说明数据转换任务
  • 原文地址:https://www.cnblogs.com/keanuyaoo/p/3329117.html
Copyright © 2020-2023  润新知