本帖最后由 awfymwvf 于 2014-12-11 14:26:34 编辑

解决方案 »

  1.   

    ONPAINT的代码是:
    void CPngImageButton::OnPaint()
    {
    CPaintDC dc(this); // device context for painting
    if(m_imgPngButton.IsNull()) return;

    CRect rcClient;
    GetClientRect(rcClient); HWND hwnd = GetSafeHwnd(); //获取窗口的HWND



    ::UpdateWindow(hwnd);
    CDC *pDC = GetDC();

    if (m_imgPngButton.IsNull())
    {
    MessageBox(_T("没加载成功"));
    return;
    }
    /*if (m_imgPngButton.GetBPP() == 32) //确认该图像包含Alpha通道  
    {  
    for (int i=0; i<m_imgPngButton.GetWidth(); ++i)  
    {  
    for (int j=0; j<m_imgPngButton.GetHeight(); ++j)  
    {  
    byte *pByte = (byte *)m_imgPngButton.GetPixelAddress(i, j);  
    pByte[0] = pByte[0] * pByte[3] / 255;  
    pByte[1] = pByte[1] * pByte[3] / 255;  
    pByte[2] = pByte[2] * pByte[3] / 255;  
    }  
    }  
    }  */ BOOL bIsEnabled = IsWindowEnabled();   //看按钮是不是禁用状态

    if (!bIsEnabled)   //如果是禁用状态
    {
    m_imgPngButton.Draw(dc.m_hDC,rcClient,CRect(m_imgPngButton.GetWidth()*3/4,0,m_imgPngButton.GetWidth()*4/4,m_imgPngButton.GetHeight()));

    return;
    } if (m_bIsHoverPng)
    {
    if (m_bIsDownPng)
    {   //按下状态 m_imgPngButton.Draw(dc.m_hDC,rcClient,CRect(m_imgPngButton.GetWidth()*2/4,0,m_imgPngButton.GetWidth()*3/4,m_imgPngButton.GetHeight()));

    }
    else
    { //经过状态 m_imgPngButton.Draw(dc.m_hDC,rcClient,CRect(m_imgPngButton.GetWidth()*1/4,0,m_imgPngButton.GetWidth()*2/4,m_imgPngButton.GetHeight()));

    }

    else
    { //正常状态
    //CBrush br(0xB22222);
    //pDC->FillRect(rcClient,&br); 
    m_imgPngButton.Draw(dc.m_hDC,rcClient,CRect(m_imgPngButton.GetWidth()*0/4,0,m_imgPngButton.GetWidth()*1/4,m_imgPngButton.GetHeight()));

    }

    }
      

  2.   

    不能 是 透明的 png
      

  3.   


    不能是透明的是什么意思啊,我看安全卫士类的软件都是PNG透明的按钮啊
      

  4.   

    那你先把背景图重绘一遍,然后画这个PNG. 如果闪的话就用内存DC.
      

  5.   

    如果是做产品的话,这事找你们美工去做,这事很EASY不需要你费劲。
      

  6.   

    1、BUTTON状态改变时重画PNG图片为什么会出现PNG图重叠的现象:因为你在OnEraseBkgnd函数中return TRUE;了,也就是不刷背景了,当PNG图画上去时由于没有背景画布刷,实际上在父窗口的界面上留下了那个PNG图片(就相当于PNG画到了父窗口上),于是当第二个PNG图画上去,就会重叠在父窗口的那个PNG图上了,因为没有去刷新父窗口。
    2、解决思路:每次重画PNG图时先把BUTTON在父窗口上对应的那一小块区域刷新一下即可解决,也就是刷新父窗口的一块区域。
    大致代码如下:
    void CPngImageButton::OnMouseMove(UINT nFlags, CPoint point)
    {
    // TODO: 在此添加消息处理程序代码和/或调用默认值
    if( !m_bIsMouseOver )
    {
    TRACKMOUSEEVENT tme;
    tme.cbSize = sizeof(tme);
    tme.hwndTrack = m_hWnd;
    tme.dwFlags = TME_LEAVE;
    m_bIsMouseOver = ( _TrackMouseEvent(&tme) == TRUE ); CWnd * pWndParent = this->GetParent();
    CRect rc;
    GetWindowRect( rc );
    pWndParent->ScreenToClient( rc );
    pWndParent->InvalidateRect( rc );
    this->Invalidate();
    } CButton::OnMouseMove(nFlags, point);
    }void CPngImageButton::OnMouseLeave()
    {
    // TODO: 在此添加消息处理程序代码和/或调用默认值
    m_bIsMouseOver = false; CWnd * pWndParent = this->GetParent();
    CRect rc;
    GetWindowRect( rc );
    pWndParent->ScreenToClient( rc );
    pWndParent->InvalidateRect( rc );
    this->Invalidate(); CButton::OnMouseLeave();
    }
      

  7.   

    void CPngImageButton::OnPaint()
    {
        CPaintDC dc(this); // device context for painting
        if(m_imgPngButton.IsNull()) return;
     CPngImageButton: 与 m_imgPngButton 什么关系 ?
      

  8.   


    CImage m_imgPngButton 是变量。CPngImageButton 是自己建的类啊,
      

  9.   

    CRect rcClient;
        GetClientRect(rcClient);
    pDC->FillSolidRect(&rcClient,RGB(255,255,255));
      

  10.   

    如果OnPaint是响应WM_PAINT消息的,你不要使用CDC* pDC = GetDC()来拿设备环境,这样会造成无限循环的绘制,需要使用
    CPaintDC dc;
      

  11.   

    "下面的背景"绘制后,不分前景背景。下面的背景 被 填充 为 白色, 就是 刷除 原 背景。因为 只在 控件 rect 内, 所以 不会 刷除 别的地方。
      

  12.   


    我写了个工程又测试了一遍,请看图:这是注释掉相关代码之后的图片切换效果:
    这是完整代码的图片切换效果:
    PNG图片按钮的完整代码如下:#pragma once//头文件//GDI+ 包含 这个我没记错的话安装的是 Microsoft Platform SDK  2003 这个 SDK包里包含的。
    #include "C:\\Program Files\\Microsoft SDKs\\Windows\\v6.0A\\Include\\GdiPlus.h"
    #pragma comment(lib,"C:\\Program Files\\Microsoft SDKs\\Windows\\v6.0A\\Lib\\GdiPlus.lib")// CPngImageButtonclass CPngImageButton : public CButton
    {
    DECLARE_DYNAMIC(CPngImageButton)public:
    CPngImageButton();
    virtual ~CPngImageButton(); Gdiplus::Image * m_pPngNormal; // 菜单按钮
    Gdiplus::Image * m_pPngOver ;
    Gdiplus::Image * m_pPngDown ; BOOL LoadImage(Gdiplus::Image* &pImage,LPCTSTR lpName,LPCTSTR lpType);
    bool m_bIsPressed, m_bIsFocus, m_bIsMouseOver;
    protected:
    DECLARE_MESSAGE_MAP()
    public:
    afx_msg void OnMouseMove(UINT nFlags, CPoint point);
    afx_msg void OnMouseLeave();
    virtual void DrawItem(LPDRAWITEMSTRUCT /*lpDrawItemStruct*/);
    protected:
    virtual void PreSubclassWindow();
    public:
    afx_msg BOOL OnEraseBkgnd(CDC* pDC);
    protected:
    virtual LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam);
    };// PngImageButton.cpp : 实现文件
    //#include "stdafx.h"
    #include "PngImageButton.h"
    #include "Resource.h"// CPngImageButtonIMPLEMENT_DYNAMIC(CPngImageButton, CButton)CPngImageButton::CPngImageButton()
    {
    m_bIsPressed = m_bIsFocus = m_bIsMouseOver = false;
    this->LoadImage( m_pPngNormal,MAKEINTRESOURCE(IDB_PNG1),_T("PNG") );
    this->LoadImage( m_pPngOver,MAKEINTRESOURCE(IDB_PNG2),_T("PNG") );
    this->LoadImage( m_pPngDown,MAKEINTRESOURCE(IDB_PNG3),_T("PNG") );
    }CPngImageButton::~CPngImageButton()
    {
    }BEGIN_MESSAGE_MAP(CPngImageButton, CButton)
    ON_WM_MOUSEMOVE()
    ON_WM_MOUSELEAVE()
    ON_WM_ERASEBKGND()
    END_MESSAGE_MAP()// CPngImageButton 消息处理程序void CPngImageButton::OnMouseMove(UINT nFlags, CPoint point)
    {
    // TODO: 在此添加消息处理程序代码和/或调用默认值
    if( !m_bIsMouseOver )
    {
    TRACKMOUSEEVENT tme;
    tme.cbSize = sizeof(tme);
    tme.hwndTrack = m_hWnd;
    tme.dwFlags = TME_LEAVE;
    m_bIsMouseOver = ( _TrackMouseEvent(&tme) == TRUE ); // 可测试:将下面5行代码注释起来:图片切换时会有上次图片残留。
    CWnd * pWndParent = this->GetParent();
    CRect rc;
    GetWindowRect( rc );
    pWndParent->ScreenToClient( rc );
    pWndParent->InvalidateRect( rc ); this->Invalidate();
    } CButton::OnMouseMove(nFlags, point);
    }void CPngImageButton::OnMouseLeave()
    {
    // TODO: 在此添加消息处理程序代码和/或调用默认值
    m_bIsMouseOver = false; // 可测试:将下面5行代码注释起来:图片切换时会有上次图片残留。
    CWnd * pWndParent = this->GetParent();
    CRect rc;
    GetWindowRect( rc );
    pWndParent->ScreenToClient( rc );
    pWndParent->InvalidateRect( rc );
    this->Invalidate(); CButton::OnMouseLeave();
    }void CPngImageButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
    {
    // TODO:  添加您的代码以绘制指定项
    CDC * pDC = CDC::FromHandle( lpDrawItemStruct->hDC );
    int nSavedDC = pDC->SaveDC(); CRect rc = lpDrawItemStruct->rcItem; Gdiplus::Graphics graphics( pDC->m_hDC );
    Gdiplus::Image * m_pImage = m_pPngNormal; if( m_bIsMouseOver )
    m_pImage = m_pPngOver;
    else if( m_bIsPressed )
    m_pImage = m_pPngDown; graphics.DrawImage( m_pImage,0,0,m_pImage->GetWidth(),m_pImage->GetHeight() );
    }void CPngImageButton::PreSubclassWindow()
    {
    // TODO: 在此添加专用代码和/或调用基类
    ModifyStyle(0,BS_OWNERDRAW);
    SetWindowPos( NULL,0,0,m_pPngNormal->GetWidth(),m_pPngNormal->GetHeight(),SWP_NOMOVE );
    CButton::PreSubclassWindow();
    }// 载入图片
    BOOL CPngImageButton::LoadImage(Gdiplus::Image* &pImage,LPCTSTR lpName,LPCTSTR lpType)
    {
    //------------------------------
    HINSTANCE hIns=::GetModuleHandle(NULL);
    HRSRC hRsrc = ::FindResource (hIns,lpName,lpType); // type 
    if (!hRsrc) 
    return FALSE; 
    // load resource into memory 
    DWORD len = SizeofResource(hIns, hRsrc); 
    BYTE* lpRsrc = (BYTE*)LoadResource(hIns, hRsrc); 
    if (!lpRsrc) 
    return FALSE; 
    // Allocate global memory on which to create stream 
    HGLOBAL hMem = GlobalAlloc(GMEM_FIXED, len); 
    BYTE* pmem = (BYTE*)GlobalLock(hMem); 
    memcpy(pmem,lpRsrc,len); 
    IStream* pstm; 
    CreateStreamOnHGlobal(hMem,FALSE,&pstm); 
    // load from stream 
    pImage=Gdiplus::Image::FromStream(pstm); 
    //------------------------------
    if(!pImage)
    return FALSE; GlobalUnlock(hMem); 
    pstm->Release(); 
    FreeResource(lpRsrc);
    return TRUE;
    }BOOL CPngImageButton::OnEraseBkgnd(CDC* pDC)
    {
    // TODO: 在此添加消息处理程序代码和/或调用默认值 return TRUE;//CButton::OnEraseBkgnd(pDC);
    }LRESULT CPngImageButton::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
    {
    // TODO: 在此添加专用代码和/或调用基类
    switch( message )
    {
    case BM_SETSTATE:
    if( (UINT)m_bIsPressed != wParam )
    {
    m_bIsPressed = ( wParam == TRUE );
    }
    break;
    default:
    break;
    } return CButton::WindowProc(message, wParam, lParam);
    }
      

  13.   

    试试 dc.setbkmode(transparent);
      

  14.   

    我说楼上的,各位不要有意见,其实楼上各位都没有搞清楚楼主问题出在哪儿,基本都是答非所问,我从接触MFC开始就研究界面研究了几年,一般问什么问题我基本马上就能知道原因出在哪儿。
    完整工程下载地址:http://download.csdn.net/detail/zhllxt/4843162
      

  15.   


    哥们,我想要TransPNG的工程文件,就是上面给我代码的那个例子,可以吗,万分感谢
      

  16.   


    哥们,我想要TransPNG的工程文件,就是上面给我代码的那个例子,可以吗,万分感谢我这不是已经提供了TransPNG的工程文件的下载地址了么。
      

  17.   

    不好意思,我贴错地址了,正确的是:http://download.csdn.net/detail/zhllxt/8301609
      

  18.   

    谢谢楼上的哥们,还有个问题,在您的例子中是鼠标移动到图片上后,又显示了另一个PNG图,如果我的图片是4种状态合在一张PNG中的,那我想显示图片的第一种状态的图,也就是图片的前四分之一,要在graphics.DrawImage中如何写呢?
    下面的代码不行
    graphics.DrawImage( m_pImage,Rectangle(m_pImage->GetWidth()*0/4,0,m_pImage->GetWidth()*1/4,m_pImage->GetHeight()) );
      

  19.   

    graphics.DrawImage( m_pImage,Gdiplus::Rect(0,0,m_pImage->GetWidth(),m_pImage->GetHeight()),0/*图片的起始X坐标*/,0,m_pImage->GetWidth()/4,m_pImage->GetHeight(),Gdiplus::Unit::UnitPixel/*最后一个参数我从来没用过,不知道该填什么,但这个参数是个enum枚举,所以把所有枚举全部试完就知道该用哪个了*/ );GDI+我也不太熟悉,也就是有网上搜索的资料,用了最基本的,我看了一下,这个函数应该就满足,我用的VS2008,自动提示一个一个函数看的。
      

  20.   

    搜索DrawImage的百度百科,应该就是这个函数:DrawImage(Image, Rectangle, Int32, Int32, Int32, Int32, GraphicsUnit, ImageAttributes, Graphics..::.DrawImageAbort, IntPtr) 在指定位置并且按指定大小绘制指定的 Image 的指定部分。
      

  21.   

    再次感谢兄弟我用的是上面这张PNG测试的,代码用的是下面的代码,void CPngImageButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
    {
    // TODO:  添加您的代码以绘制指定项
    CDC * pDC = CDC::FromHandle( lpDrawItemStruct->hDC );
    int nSavedDC = pDC->SaveDC(); CRect rc = lpDrawItemStruct->rcItem; Gdiplus::Graphics graphics( pDC->m_hDC );
    Gdiplus::Image * m_pImage = m_pPngNormal; if( m_bIsMouseOver )
    //m_pImage = m_pPngOver;
    graphics.DrawImage( m_pImage,Gdiplus::Rect(0,0,m_pImage->GetWidth()/4,m_pImage->GetHeight()),m_pImage->GetWidth()/4,0,m_pImage->GetWidth()/4,m_pImage->GetHeight(),Gdiplus::Unit::UnitPixel );
    else if( m_bIsPressed )
    //m_pImage = m_pPngDown;
    graphics.DrawImage( m_pImage,0,0,m_pImage->GetWidth(),m_pImage->GetHeight() );
    else 
    //graphics.DrawImage( m_pImage,m_pImage->GetWidth()*0/4,0,m_pImage->GetWidth()*1/4,m_pImage->GetHeight() );
    graphics.DrawImage( m_pImage,Gdiplus::Rect(0,0,m_pImage->GetWidth()/4,m_pImage->GetHeight()),0,0,m_pImage->GetWidth()/4,m_pImage->GetHeight(),Gdiplus::Unit::UnitPixel );

    }发现绘制的图片很宽,好像是连右边的一起都绘制出来了,导致鼠标向右只有移动到窗体外,图标才会发生改变。另外,为什么点一下图片程序就退出了呢。谢谢兄弟这些天对贴子的关注,愿兄弟新年发大财,万事如意。
      

  22.   


    鼠标移动到窗体右边,图标才会发生改变:这和绘制没有关系,我猜测你是完全复制我的代码,包括其中如下的代码:void CPngImageButton::PreSubclassWindow()
    {
    // TODO: 在此添加专用代码和/或调用基类
    ModifyStyle(0,BS_OWNERDRAW);
    SetWindowPos( NULL,0,0,m_pPngNormal->GetWidth(),m_pPngNormal->GetHeight(),SWP_NOMOVE );
    CButton::PreSubclassWindow();
    }
    将上面代码修改一下,也就是改变一下按钮的宽度,你试试,是不是这个原因。void CPngImageButton::PreSubclassWindow()
    {
    // TODO: 在此添加专用代码和/或调用基类
    ModifyStyle(0,BS_OWNERDRAW);
    SetWindowPos( NULL,0,0,m_pPngNormal->GetWidth()/4,m_pPngNormal->GetHeight(),SWP_NOMOVE );
    CButton::PreSubclassWindow();
    }
      

  23.   

    我是在ONPAINT中绘制的,是不是也要改成DRAWITEM来绘制呢
      

  24.   


    最好用DRAWITEM绘制,ONPAINT在有些情况下不可靠。
      

  25.   


    我工程中的pngimagebutton继承的是CWND类,而兄弟你的工程中的继承的是CBUTTON类,我的那个继承CWND类的pngimagebutton里面没有重写DRAWITEM,怎么办呢
      

  26.   


    我工程中的pngimagebutton继承的是CWND类,而兄弟你的工程中的继承的是CBUTTON类,我的那个继承CWND类的pngimagebutton里面没有重写DRAWITEM,怎么办呢如果是继承CWND类就用ONPAINT,没有任何问题,只是WINDWOS的控件如BUTTON等重载ONPAINT不可靠。
      

  27.   

    png 含4个 子图,
    子图的位置 计算 有 问题, 
    另外 那个 空的 是不是 可以 用来 涮背景 ?
      

  28.   


    如果在onpaint中自绘的,那六行代码应该加在哪呢。我试着加在ONPAINT里面,出现一直刷的效果
      

  29.   

    BOOL CPngImageButton::OnEraseBkgnd(CDC* pDC)
    {
        // TODO: 在此添加消息处理程序代码和/或调用默认值
     
    //    return TRUE;
          CButton::OnEraseBkgnd(pDC);
    }
      

  30.   

    BOOL CPngImageButton::OnEraseBkgnd(CDC* pDC)
    {
    // TODO: 在此添加消息处理程序代码和/或调用默认值
    return TRUE;
    return CWnd::OnEraseBkgnd(pDC);
    }我的CPngImageButton是CWND添加后,还是重叠。哥们怎么办,
      

  31.   

    @zhllxt
    void CWindowPNG::OnClickedPngBtn()
    {
    CWnd * pWndParent = this->GetParent();
    CRect rc;
    GetWindowRect( rc );
    pWndParent->ScreenToClient( rc );
    pWndParent->InvalidateRect( rc );
    this->Invalidate();
    }
    我把这几行代码加到图片区域的单击事件中,可以实现不重叠,但加到别的地方不好使,应该加到什么地方合适呢?谢谢兄弟解答,万分感谢。
      

  32.   

    dc.FillSolidRect(&rcClient,RGB(196,196,196)); 是擦除背景!!!void CBigPngDlg::OnTimer(UINT nIDEvent) 
    {
    // TODO: Add your message handler code here and/or call default
    static float j=0;
    CClientDC dc(this); // device context for painting
    if(! m_pImage) return; Gdiplus::Graphics graphics( dc.m_hDC );
    CRect rcClient;
    GetClientRect(rcClient);
    dc.FillSolidRect(&rcClient,RGB(196,196,196));
    graphics.DrawImage(m_pImage, RectF(0,0,300,300),j*300,0,300,300,UnitPixel);//
    j++;
    if(j>2) j=0;
    // CDialog::OnTimer(nIDEvent);
    }
      

  33.   

    你这帖子怎么还没结,我再说起来楼上的可能不高兴,还是那句话,他们的方法根本就解决不了问题。透明的PNG图片,你想把透明的效果显示在界面上,还用的是MFC的框架,就不要想着擦除背景什么的,就根本不能有背景,只要你有背景,透过那个透明的PNG就会看到背景,这就不是透明了,重叠的原因就是因为没有背景,导致画面刷到父窗口上去了,只要每次重画子窗口时先刷新一下父窗口对应的矩形区域即可。说实话,我第一次遇见这个问题时是07年,当时也是莫明其妙,不知道什么原因,我只在网上搜索了一下,看到一个人简单的说了一下原因,我当时就明白问题出在哪儿,当时自己就解决了。我这代码都贴上了,原因也说了,你还没解决。我QQ37792738你实在没办法我给你远程,C#不要找我,MFC的再找我。