MFC 自绘按钮划过或点击会出现闪烁问题,求助精通GDI高手解决

鼠标划进按钮再划出再划进,速度快一点或慢一点各种尝试,会发现闪炼现象(即中间会一个白色底短时间内出现),请做过相关开发的大神们帮解决

如下是一个vs2010中建立的测试工程,打包在下面链接。
链接: https://pan.baidu.com/s/1jnL4f6qapbUBP3od4WoQRw 密码: 1jqk

(ps: C币是不是给多了还是不够,C友们不要担心问题难,本人因为没做过贴图这块学习和开发,把重心放在别的地方,奉行拿来主义,这工程中的按钮自绘类我就是从别的C友博客中直接拷贝来的,我想在人家博客下面问,不知道何年马月才可能被回复,人家也不一定愿意去再看这问题,这个按钮闪烁问题解决了,我就谢谢各位了,可惜CSDN没法散币给帮助的大家)

(再PS:这闪烁问题我尝试解决,但是能力不够,我觉得问题最可能在 SelfControlPublic.cpp 这个类中,大楖是没有使用到双缓冲,但是不知道怎么改,我只想问题再点解决,所以提供下我的猜测)

(再再PS:下面的zqbnqsdsmd网友,我不需要这种贴一大段代码,我要修改好的工程或者一段能解决的方法,只采纳能解决我问题的答案,你这样贴,和我百度搜索各种尝试有啥区别)

17个回答

void CPngButton::PaintParent()
{
CRect rect;
GetWindowRect(&rect);
Invalidate();
//GetParent()->ScreenToClient(&rect);
//GetParent()->InvalidateRect(&rect); //这里会导致闪烁,只能用双缓冲
}
我把这个函数改了一下,移动进去时候不会闪了,不知老兄是否还有时间看看啊,其实我这里有完整的一个很好用的图片按钮,如果老兄有兴趣,加个联系方式我发给你,你觉得满意也好结贴

evolay
老爸我爱你 解决了,非常感谢热心的前辈。
大约 2 年之前 回复

给dialog加上 WS_CLIPCHILDREN .
这样, 父窗口重画的时候, 子窗口可以不用重画,直接把子窗口的东西clip过来.

WS_CLIPCHILDREN 属性!!!

evolay
老爸我爱你 回复phenix2009: 好的,我加你QQ了。
大约 2 年之前 回复
phenix2009
白色一大坨 回复evolay: 之前回答的被删了,我改了你的CPngButton::PaintParent函数,目前的确不闪了,如果需要可以加个联系方式,我好帮你解决一下
大约 2 年之前 回复
dabocaiqq
穷在人世中少你左右我想我连什么价值也没有 回复evolay: 除了我会回答你的问题,还有谁会呢。我是真心要帮你。你不采纳就算了。
大约 2 年之前 回复
evolay
老爸我爱你 要是你有心帮我调,代码链接都在上面。CSDN上的大家都看不中这点C币,没解决就算了不采纳任何。
大约 2 年之前 回复
dabocaiqq
穷在人世中少你左右我想我连什么价值也没有 回复evolay: 不然你采纳了我的回答,我帮你调试下你的程序。
大约 2 年之前 回复
evolay
老爸我爱你 我试了一下,如果按钮不用雪碧图,你这种方法没问题,用了雪碧图后,就不行了,按钮不能在多种状态下切换(颜色深浅变化)
大约 2 年之前 回复

oid CXPButton::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
if (!m_bTracking)
{
TRACKMOUSEEVENT tme;
tme.cbSize = sizeof(tme);
tme.hwndTrack = m_hWnd;
tme.dwFlags = TME_LEAVE | TME_HOVER;
tme.dwHoverTime = 1;
m_bTracking = _TrackMouseEvent(&tme);
}
CButton::OnMouseMove(nFlags, point);
}
接着添加WM_MOUSELEAVE和WM_MOUSEHOVER消息消息函数。在CXPButton类的声明中(即在XPButton.h文件中)找到afx_msg void OnMouseMove(UINT nFlags, CPoint point);的函数声明,紧接其下输入
afx_msg LRESULT OnMouseLeave(WPARAM wParam, LPARAM lParam);
afx_msg LRESULT OnMouseHover(WPARAM wParam, LPARAM lParam);
然后在XPButton.cpp文件中找到ON_WM_MOUSEMOVE(),紧接其后输入
ON_MESSAGE(WM_MOUSELEAVE, OnMouseLeave)
ON_MESSAGE(WM_MOUSEHOVER, OnMouseHover)
当然最后还有函数的实现了,详细代码可见本篇提供的源程序,这里就不再重复了。

DRAWITEMSTRUCT结构的定义如下:
typedef struct tagDRAWITEMSTRUCT {
UINT CtlType; //控件类型
UINT CtlID; //控件ID
UINT itemID; //菜单项、列表框或组合框中某一项的索引值
UINT itemAction; //控件行为
UINT itemState; //控件状态
HWND hwndItem; //父窗口句柄或菜单句柄
HDC hDC; //控件对应的绘图设备句柄
RECT rcItem; //控件所占据的矩形区域
DWORD itemData; //列表框或组合框中某一项的值
} DRAWITEMSTRUCT, *PDRAWITEMSTRUCT, *LPDRAWITEMSTRUCT;

phenix2009
白色一大坨 回复evolay: 有需要可以加我qq465617727
大约 2 年之前 回复
evolay
老爸我爱你 兄台,感谢回复。你这回复两个不一样的答案是咋回事呀,而且你这复制一大段代码也不加文字说明,没看懂呀
大约 2 年之前 回复

1 CDC buttonDC;
2 CBitmap bitmapTrans;
3 BITMAP bmp;
4 CDC mem;
5 CRect rc;
6 buttonDC.Attach(lpDrawItemStruct->hDC);//得到用于绘制按钮的DC
7 mem.CreateCompatibleDC(&buttonDC);//准备向按钮区域传输图形

8

9 if (lpDrawItemStruct->CtlID == IDC_BUTTON1)
10 {
11 rc = lpDrawItemStruct->rcItem;//获取按钮所占的矩形大小
12 UINT state = lpDrawItemStruct->itemState;//获取按钮当前的状态,不同状态绘制不同的按钮

13 if (state & ODS_FOCUS)//如果按钮已经取得焦点,绘制选中状态下的按钮
14 {
15 bitmapTrans.LoadBitmap(IDB_BITMAP2);
16 bitmapTrans.GetBitmap(&bmp);
17 CBitmap * old = mem.SelectObject(&bitmapTrans);
18 //向按钮传输位图,使用stretcnblt可以使图片随按钮大小而改变
19 buttonDC.StretchBlt(rc.left,rc.top,rc.right,rc.bottom,&mem,0,0,bmp.bmWidth,bmp.bmHeight,SRCCOPY);
20 mem.SelectObject(old);
21 bitmapTrans.DeleteObject();
22 }
23 else //如果按钮已经取得焦点,绘制选中状态下的按钮
24 {
25 bitmapTrans.LoadBitmap(IDB_BITMAP3);
26 CBitmap *old2 = mem.SelectObject(&bitmapTrans);
27 bitmapTrans.GetBitmap(&bmp);
28 CBitmap *old=mem.SelectObject(&bitmapTrans);
29 buttonDC.StretchBlt(rc.left,rc.top,rc.right,rc.bottom,&mem,0,0,bmp.bmWidth,bmp.bmHeight,SRCCOPY);
30 mem.SelectObject(old2);
31 bitmapTrans.DeleteObject();
32 }
33 }

     void CMyDlg::OnNcLButtonDown(UINT nHitTest, CPoint point)  
{     

   // 1,查询当前系统“拖动显示窗口内容”设置  
    SystemParametersInfo(SPI_GETDRAGFULLWINDOWS, 0, &m_bDragFullWindow, NULL);  

   // 2,如果需要修改设置,则在每次进入CDialog::OnNcLButtonDown默认处理之前修改  
    if(m_bDragFullWindow)  
         SystemParametersInfo(SPI_SETDRAGFULLWINDOWS, FALSE, NULL, NULL);  
   // 3,默认处理,系统会自动绘制虚框  
    CDialog::OnNcLButtonDown(nHitTest, point);  

   // 4,默认处理完毕后,还原系统设置  
    if(m_bDragFullWindow)  
         SystemParametersInfo(SPI_SETDRAGFULLWINDOWS, TRUE, NULL, NULL);  
}  

void CMyDlg::OnNcLButtonDown(UINT nHitTest, CPoint point)

{

// 1,查询当前系统“拖动显示窗口内容”设置

SystemParametersInfo(SPI_GETDRAGFULLWINDOWS, 0, &m_bDragFullWindow, NULL);

// 2,如果需要修改设置,则在每次进入CDialog::OnNcLButtonDown默认处理之前修改

if(m_bDragFullWindow)

SystemParametersInfo(SPI_SETDRAGFULLWINDOWS, FALSE, NULL, NULL);

// 3,默认处理,系统会自动绘制虚框

CDialog::OnNcLButtonDown(nHitTest, point);

// 4,默认处理完毕后,还原系统设置

if(m_bDragFullWindow)

SystemParametersInfo(SPI_SETDRAGFULLWINDOWS, TRUE, NULL, NULL);

}

添加 以下代码在 .h中:
//member variable
CPoint m_oldPt;
CPoint m_offsetPt;
CRect m_wndRc;
CRect m_newRc;
CRect m_newSizeRect;
CRect m_oldSizeRect;
BOOL m_bDrawFrame;
BOOL m_needFinish;
BOOL m_bDrawSize;
BOOL m_needSize;

LRESULT OnMoving(WPARAM,LPARAM);
LRESULT OnExitSizeMove(WPARAM,LPARAM);
LRESULT OnEnterSizeMove(WPARAM, LPARAM);
LRESULT OnSizing(WPARAM,LPARAM);

DECLARE_MESSAGE_MAP()

添加 以下代码在 .CPP中:
ON_MESSAGE(WM_MOVING,OnMoving)
ON_MESSAGE(WM_EXITSIZEMOVE,OnExitSizeMove)
ON_MESSAGE(WM_ENTERSIZEMOVE,OnEnterSizeMove)
ON_MESSAGE(WM_SIZING,OnSizing)
END_MESSAGE_MAP()

下面是简单的处理函数,你可以扩展

LRESULT CPrimeNumDlg::OnEnterSizeMove(WPARAM, LPARAM){
GetWindowRect(&m_wndRc);
m_oldSizeRect = m_wndRc;
m_oldPt = CPoint(m_wndRc.left,m_wndRc.top);
m_offsetPt = CPoint(0,0);
m_bDrawFrame = FALSE;
return TRUE; //we don't process this message

}

LRESULT CPrimeNumDlg::OnExitSizeMove(WPARAM,LPARAM){
if(m_needFinish){
CRect rect;
rect.left = m_oldPt.x - m_offsetPt.x;
rect.top = m_oldPt.y - m_offsetPt.y;
rect.right = rect.left + m_wndRc.Width();
rect.bottom = rect.top + m_wndRc.Height();
DrawDashFrame(rect);
m_needFinish = FALSE;

SetWindowPos(NULL,m_oldPt.x,m_oldPt.y,m_wndRc.Width(),m_wndRc.Height(),\
            SWP_NOSENDCHANGING ); //prevent receiving more moving message
GetWindowRect(&m_wndRc);
m_oldPt = CPoint(m_wndRc.left,m_wndRc.top);
m_offsetPt = CPoint(0,0);
m_bDrawFrame = FALSE;

}
if(m_needSize){
m_bDrawSize = FALSE;
m_needSize = FALSE;
DrawDashFrame(m_oldSizeRect);
SetWindowPos(NULL,m_newSizeRect.left,m_newSizeRect.top,m_newSizeRect.Width(),m_newSizeRect.Height(),\
SWP_NOSENDCHANGING ); //prevent receiving more moving message
}
return TRUE; //we don't process this message
}

void CPrimeNumDlg::DrawDashFrame(CRect& rect){

CPen pen;
pen.CreatePen(PS_DASH,1,RGB(0,0,0));
CWnd* p_desktop = GetDesktopWindow();
CDC* p_dc_desktop = p_desktop->GetWindowDC();

p_dc_desktop->SetROP2(R2_XORPEN );
CPen* p_old_pen = p_dc_desktop->SelectObject(&pen);
p_dc_desktop->MoveTo(rect.left,rect.top);
p_dc_desktop->LineTo(rect.left,rect.bottom);

p_dc_desktop->MoveTo(rect.left,rect.bottom);
p_dc_desktop->LineTo(rect.right,rect.bottom);

p_dc_desktop->MoveTo(rect.right,rect.bottom);
p_dc_desktop->LineTo(rect.right,rect.top);

p_dc_desktop->MoveTo(rect.right,rect.top);
p_dc_desktop->LineTo(rect.left,rect.top);
p_dc_desktop->SelectObject(p_old_pen);
p_desktop->ReleaseDC(p_dc_desktop);
}

LRESULT CPrimeNumDlg::OnMoving(WPARAM wp,LPARAM lp){

RECT* p_rc = (RECT*)lp;
m_newRc = CRect(p_rc);
CPoint pt;
GetCursorPos(&pt);

if(!m_bDrawFrame){

m_bDrawFrame = TRUE;
m_offsetPt = CPoint(pt.x - m_wndRc.left,pt.y - m_wndRc.top);
m_oldPt = pt;

CRect rect;
rect.left = m_oldPt.x - m_offsetPt.x;
rect.top = m_oldPt.y - m_offsetPt.y;
rect.right = rect.left + m_wndRc.Width();
rect.bottom = rect.top + m_wndRc.Height();
DrawDashFrame(rect);
m_needFinish = TRUE;

}
else{
CRect rect;
rect.left = m_oldPt.x - m_offsetPt.x;
rect.top = m_oldPt.y - m_offsetPt.y;
rect.right = rect.left + m_wndRc.Width();
rect.bottom = rect.top + m_wndRc.Height();
DrawDashFrame(rect);

rect.left = pt.x - m_offsetPt.x;
rect.top = pt.y - m_offsetPt.y;
rect.right = rect.left + m_wndRc.Width();
rect.bottom = rect.top + m_wndRc.Height();
DrawDashFrame(rect);
m_oldPt = pt;

}

int width = p_rc->right - p_rc->left;
int height = p_rc->bottom - p_rc->top;

p_rc->left = m_wndRc.left;
p_rc->right = p_rc->left + width;
p_rc->top = m_wndRc.top;
p_rc->bottom = p_rc->top + height;
return FALSE; //we process this message

}

LRESULT CPrimeNumDlg::OnSizing(WPARAM wp,LPARAM lp){

m_needSize = TRUE;

if(!m_bDrawSize){
DrawDashFrame(m_wndRc);
m_oldSizeRect = m_wndRc;
m_bDrawSize = TRUE;

}
else{
RECT* p_rc = (RECT*)lp;
m_newSizeRect = CRect(p_rc);
DrawDashFrame(m_oldSizeRect);
DrawDashFrame(m_newSizeRect);
p_rc->left = m_wndRc.left;
p_rc->right = m_wndRc.right;
p_rc->top = m_wndRc.top;
p_rc->bottom = m_wndRc.bottom;
m_oldSizeRect = m_newSizeRect;
}
return FALSE; //we don't process this message
}

LRESULT CPrimeNumDlg::OnEnterSizeMove(WPARAM, LPARAM){
GetWindowRect(&m_wndRc);
m_oldSizeRect = m_wndRc;
m_oldPt = CPoint(m_wndRc.left,m_wndRc.top);
m_offsetPt = CPoint(0,0);
m_bDrawFrame = FALSE;
return TRUE; //we don't process this message

}

 LRESULT CPrimeNumDlg::OnEnterSizeMove(WPARAM, LPARAM){
GetWindowRect(&m_wndRc);
m_oldSizeRect = m_wndRc;
m_oldPt = CPoint(m_wndRc.left,m_wndRc.top);
m_offsetPt = CPoint(0,0);
m_bDrawFrame = FALSE;
return TRUE; //we don't process this message

}
共17条数据 1 尾页
Csdn user default icon
上传中...
上传图片
插入图片
抄袭、复制答案,以达到刷声望分或其他目的的行为,在CSDN问答是严格禁止的,一经发现立刻封号。是时候展现真正的技术了!
立即提问
相关内容推荐