Erase GDI+ Drawn Image?

I've been experimenting with GDI+ and i managed to draw 2 PNG in a button button.

1 for the normal state
and one for the Clicked (down state)

but when i click the button,the down state image gets draw on top of the normal state, and when i release the button,the normal state image gets drawn on top of the down state.

I don't want this,I want to when the button is in the down state,erase the normal state png and draw the new Down state png and so forth with the normal state.

i made an example so you have a basic idea of what i mean.

this is the dialog with the button un clicked.

http://i14.photobucket.com/albums/a325/AntiBNI/beforeclick.jpg

and this is what happends when i click the button (Down state)
Note that the check mark png draws on top of the logo,you can still see part of the logo.

http://i14.photobucket.com/albums/a325/AntiBNI/Clicked.jpg

And this is what happends when i release the button (normal state again)
note that the logo gets drawn on top of the check mark png.

http://i14.photobucket.com/albums/a325/AntiBNI/AfterClick.jpg


But if i drag the dialog off screen,the dialog redraws and the back png gets erased.


How can i erase the back png without having to move the dialog off screen?

Here's the part of the code where the button gets drawn with the png.

Note: "MyButton" is a derived class from CButton.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
void MyButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
    Gdiplus::GdiplusStartupInput gdiplusstartupinput;
	Gdiplus::GdiplusStartup(&m_gdiplusToken,&gdiplusstartupinput,NULL);

    // TODO:  Add your code to draw the specified item
    CDC dc;
    dc.Attach(lpDrawItemStruct->hDC);
    CRect rt;
    rt = lpDrawItemStruct->rcItem;

	Graphics grpx(dc);

	Rect rect(0,0,90,90);//size of image

	Image * Img = Image::FromFile(L"C:\\11.png",FALSE);//Downstate Image

	Image * Img2 = Image::FromFile(L"C:\\10.png",FALSE);//Upstate Image



	UINT state = lpDrawItemStruct->itemState; //Get state of the button
  if ( (state & ODS_SELECTED) )            // If it is pressed
  {
        grpx.DrawImage(Img,rect);//Downstate Image
	}
  else
  {

        grpx.DrawImage(Img2,rect);//Upstate Image
	}


    dc.Detach();
} 

BOOL MyButton::OnEraseBkgnd(CDC* pDC) //erase the BG so it's transparent and not grey
{
	// Erase the BG of the CButton
	return TRUE;
}

Last edited on
Could you edit the image to have an opaque background?
What u mean by opaque background?
closed account (z05DSL3A)
Your OnEraseBkgnd doesn't do anything. If you want to erase your background, should you not fill the client area with a transparent brush?

I also noted that the start of DrawItem you call GdiplusStartup but I do not see a corresponding GdiplusShutdown. Each time you call GdiplusStartup you should also call GdiplusShutdown. In this case I would probably put GdiplusStartup in MyButton constructor and GdiplusShutdown in it's destructor. I would also look at making the Image variables into data members and load then on construction, saves doing it each time the button is redrawn.

Hop that helps.
The OnEraseBkgnd erases the gray background of the button and makes it transparent.

So you don't see a big gray square.
closed account (z05DSL3A)
The code that you have posted for OnEraseBkgnd just returns true, it does not change the background in any way. This is why the previouse image is still there.

You could try adding a message handler for =WM_CTLCOLOR with the following
1
2
3
4
5
6
7
HBRUSH MyButton::CtlColor(CDC* pDC, UINT /*nCtlColor*/)
{
    // TODO:  Change any attributes of the DC here
    pDC->SetBkMode(TRANSPARENT);
    // TODO:  Return a non-NULL brush if the parent's handler should not be called
    return (HBRUSH)::GetStockObject(NULL_BRUSH);
}


and change OnEraseBkgnd back to

1
2
3
4
BOOL MyButton::OnEraseBkgnd(CDC* pDC)
{
    return CButton::OnEraseBkgnd(pDC);
}


Last edited on
I tried it and i still get the same result and the gray square.
closed account (z05DSL3A)
Hmm...I tried it as well and it partly worked (on XP SP3).

It does not draw a gray box, as it is not given a grey brush when asking for the controls colour.

OnEraseBkgnd would seem to only be called after a button up message, so I get a clear redraw then but not on button down...maybe it is called on the clicked message...but any how it does a clean redraw at the end, just needs to be encouraged to erase background as a response to other mouse events...

...but anyway if it is not working for you...


Ok this is my whole MyButton.cpp file

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
// MyButton.cpp : implementation file
//

#include "stdafx.h"
#include "CustomDraw.h"
#include "MyButton.h"
#include <gdiplus.h>

#pragma comment (lib,"gdiplus.lib")

using namespace Gdiplus;

// MyButton
bool once = TRUE;
IMPLEMENT_DYNAMIC(MyButton, CButton)


MyButton::MyButton()
{

}

MyButton::~MyButton()
{
}


BEGIN_MESSAGE_MAP(MyButton, CButton)
	ON_WM_ERASEBKGND()
	ON_WM_CTLCOLOR_REFLECT()
END_MESSAGE_MAP()



// MyButton message handlers

void MyButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
    Gdiplus::GdiplusStartupInput gdiplusstartupinput;
	Gdiplus::GdiplusStartup(&m_gdiplusToken,&gdiplusstartupinput,NULL);

    // TODO:  Add your code to draw the specified item
    CDC dc;
    dc.Attach(lpDrawItemStruct->hDC);
    CRect rt;
    rt = lpDrawItemStruct->rcItem;

	Graphics grpx(dc);

	Rect rect(0,0,90,90);

	Image * Img = Image::FromFile(L"C:\\11.png",FALSE);

	Image * Img2 = Image::FromFile(L"C:\\10.png",FALSE);



	UINT state = lpDrawItemStruct->itemState; //Get state of the button
  if ( (state & ODS_SELECTED) )            // If it is pressed
  {
        grpx.DrawImage(Img,rect);//Draw The PNG on the Button
		Invalidate(TRUE);
	}
  else
  {

        grpx.DrawImage(Img2,rect);//Draw The PNG on the Button
		Invalidate(TRUE);
	}

    dc.Detach();
} 

BOOL MyButton::OnEraseBkgnd(CDC* pDC)
{
	// Erase the BG of the CButton
	return CButton::OnEraseBkgnd(pDC);
}

HBRUSH MyButton::CtlColor(CDC* pDC, UINT /*nCtlColor*/)
{
	// TODO:  Change any attributes of the DC here
    pDC->SetBkMode(TRANSPARENT);
    return (HBRUSH)::GetStockObject(NULL_BRUSH);
}


If it works for you it must work for me,i have SP3 aswell.


Can you post your .cpp content?

I can't find Init and ExitInstance to start and shutdown GDIplus,that maybe is the problem.

oh and...Thanks for all your help bro,i really appreciate it.
Last edited on
closed account (z05DSL3A)
My code looks (pretty much) the same as yours:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#pragma once

class MyButton : public CButton
{
	DECLARE_DYNAMIC(MyButton)

public:
	MyButton();
	virtual ~MyButton();

protected:
	DECLARE_MESSAGE_MAP()
public:
    afx_msg BOOL OnEraseBkgnd(CDC* pDC);
    void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
    afx_msg HBRUSH CtlColor(CDC* /*pDC*/, UINT /*nCtlColor*/);
private:
    ULONG_PTR gdiplusToken;
};
MyButton.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
// MyButton.cpp : implementation file
//

#include "stdafx.h"
#include "TestMfcDlg.h"
#include "MyButton.h"

#include <gdiplus.h>

IMPLEMENT_DYNAMIC(MyButton, CButton)

MyButton::MyButton()
: gdiplusToken(0)
{
    Gdiplus::GdiplusStartupInput gdiplusstartupinput; 
    Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusstartupinput, NULL);
}

MyButton::~MyButton()
{
    Gdiplus::GdiplusShutdown(gdiplusToken);
}


BEGIN_MESSAGE_MAP(MyButton, CButton)
    ON_WM_ERASEBKGND()
    ON_WM_CTLCOLOR_REFLECT()
END_MESSAGE_MAP()



// MyButton message handlers

void MyButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
    // TODO:  Add your code to draw the specified item
    CDC dc;
    dc.Attach(lpDrawItemStruct->hDC);

    CRect rt;
    rt = lpDrawItemStruct->rcItem;

    Gdiplus::Graphics grpx(dc);

    Gdiplus::Rect rect(0,0,24,24);//size of image

    Gdiplus::Image * Img = Gdiplus::Image::FromFile(L"C:\\11.png",FALSE);//Downstate Image

    Gdiplus::Image * Img2 = Gdiplus::Image::FromFile(L"C:\\10.png",FALSE);//Upstate Image

    UINT state = lpDrawItemStruct->itemState; //Get state of the button
    if ( (state & ODS_SELECTED) )            // If it is pressed
    {
        grpx.DrawImage(Img,rect);//Downstate Image
    }
    else
    {
        grpx.DrawImage(Img2,rect);//Upstate Image
    }
    dc.Detach();
}

BOOL MyButton::OnEraseBkgnd(CDC* pDC)
{
    return CButton::OnEraseBkgnd(pDC);
}

HBRUSH MyButton::CtlColor(CDC* pDC, UINT /*nCtlColor*/)
{
    pDC->SetBkMode(TRANSPARENT);
    return (HBRUSH)::GetStockObject(NULL_BRUSH);
}
MyButton.cpp
I tried with your code and still same results as first time.


WTF is going on >_<!
closed account (z05DSL3A)
I have just noticed that the dialog box that I am using to test Invalidates the client area in response a click event. This would be why I am getting a clean redraw after clicking on the button. You may be able to Invalidate the buttons client area in response to the relevant events to ensure a clean redraw.

The reverse of this may be why you are getting a gray box. If your dialog painting routine is not filling the ‘hole’.

I’m will play with a few ideas I have when I get some time. The annoying thing is I have done this before but can’t remember how (and have not got access to the code anymore).

Edit:
I have made some progress, just tiding up the code.
Last edited on
closed account (z05DSL3A)
OK try this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#pragma once

class Gdiplus::Image;
class MyButton : public CButton
{
	DECLARE_DYNAMIC(MyButton)

public:
	MyButton();
	virtual ~MyButton();

protected:
	DECLARE_MESSAGE_MAP()
public:
    afx_msg BOOL OnEraseBkgnd(CDC* pDC);
    void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
    afx_msg HBRUSH CtlColor(CDC* /*pDC*/, UINT /*nCtlColor*/);
private:
    ULONG_PTR gdiplusToken;

    Gdiplus::Image * Img;
    Gdiplus::Image * Img2;
public:
    afx_msg void OnBnClicked();
    afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
    void InvalidateParent(void);
    afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
};
MyButton.h


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
// MyButton.cpp : implementation file
//

#include "stdafx.h"
#include "TestMfcDlg.h"
#include "MyButton.h"

#include <gdiplus.h>

IMPLEMENT_DYNAMIC(MyButton, CButton)

MyButton::MyButton()
: gdiplusToken(0)
{
    Gdiplus::GdiplusStartupInput gdiplusstartupinput; 
	Gdiplus::GdiplusStartup(&gdiplusToken,&gdiplusstartupinput,NULL);

    Img = Gdiplus::Image::FromFile(L"C:\\11.png",FALSE);//Downstate Image
	Img2 = Gdiplus::Image::FromFile(L"C:\\10.png",FALSE);//Upstate Image
}

MyButton::~MyButton()
{
    Gdiplus::GdiplusShutdown(gdiplusToken);
}


BEGIN_MESSAGE_MAP(MyButton, CButton)
    ON_WM_ERASEBKGND()
    ON_WM_CTLCOLOR_REFLECT()
    ON_WM_LBUTTONDOWN()
    ON_WM_LBUTTONUP()
END_MESSAGE_MAP()



// MyButton message handlers

void MyButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
    // TODO:  Add your code to draw the specified item
    CDC dc;
    dc.Attach(lpDrawItemStruct->hDC);

    CRect rt;
    rt = lpDrawItemStruct->rcItem;

	Gdiplus::Graphics grpx(dc);

	Gdiplus::Rect rect(0,0,90,90);//size of image

	UINT state = lpDrawItemStruct->itemState; //Get state of the button
    if ( (state & ODS_SELECTED) )            // If it is pressed
    {
        grpx.DrawImage(Img,rect);//Downstate Image
	}
    else
    {
        grpx.DrawImage(Img2,rect);//Upstate Image
	}

    dc.Detach();
}

BOOL MyButton::OnEraseBkgnd(CDC* pDC)
{
    return CButton::OnEraseBkgnd(pDC);
}

HBRUSH MyButton::CtlColor(CDC* pDC, UINT /*nCtlColor*/)
{
    pDC->SetBkMode(TRANSPARENT);
    return (HBRUSH)::GetStockObject(NULL_BRUSH);
}

void MyButton::InvalidateParent(void)
{
    CRect rect;
    GetWindowRect(rect);
    CWnd* pParent = GetParent();
    if( ::IsWindow(pParent->GetSafeHwnd() ) )
    {
        pParent->ScreenToClient(rect);
        pParent->InvalidateRect(rect);
    }
}

void MyButton::OnLButtonDown(UINT nFlags, CPoint point)
{
    CButton::OnLButtonDown(nFlags, point);
    InvalidateParent();
}

void MyButton::OnLButtonUp(UINT nFlags, CPoint point)
{
    CButton::OnLButtonUp(nFlags, point);
    InvalidateParent();
}
MyButton.cpp


I have only put in code to handle the left buton down and up, I will leave it to you to do any others.
Wow,Now it works ^_^.


Now i can make Eye candy GUI =P


Thanks for all your help bro,i appreciate it allot.
Topic archived. No new replies allowed.