We help IT Professionals succeed at work.

Animated objects in dialog application do not stay on a background

khlauster
khlauster asked
on
Hi, experts!

I am trying to build an animated object in my dialog application:

1. User clicks on button1 and object1(bitmap1) moves then draws into background a few times  at some point in dialog
2. User clicks on button2 and the object moves then draws into background  a few times at some other point in dialog.
In the code snippet bellow the drawings do not remain on dialog’s background.
All the object drawings must remain on dialog’s background and do not get erased after dialog is dragged off screen or other drawings took a place in the same application.

Working code sample is greatly appreciated!

Thank you for your help in advance

BOOL CFlickerFreeDlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	BITMAP bm;
	CClientDC dc(this);

	m_picture.topleft.x = 0;m_picture.topleft.y = 0;
	m_offs.cx = 0;m_offs.cy = 0;

	m_CanvasDC->CreateCompatibleDC(&dc);
	m_PicDC->CreateCompatibleDC(&dc);
	m_FinalCanvasDC->CreateCompatibleDC(&dc);

	m_BkgBitmap->LoadBitmap(IDB_BG);
	m_BkgBitmap->GetBitmap(&bm);
	m_hBkg = bm.bmHeight;
	m_wBkg = bm.bmWidth;
	m_Bitmap->LoadBitmap(IDB_PIC);
	m_Bitmap->GetBitmap(&bm);
	m_picture.height = bm.bmHeight;
	m_picture.width = bm.bmWidth;

	return TRUE;  
}


void CFlickerFreeDlg::OnPaint()
{

	CPaintDC dc(this);
	OnDraw(&dc);
}

void CFlickerFreeDlg::OnDraw(CDC* pDC)
{
	
		CBitmap* pOldBmp1; 
		CBitmap* pOldBmp2; 

		CRect cr;

		//selects canvas 
		pOldBmp1 = m_CanvasDC->SelectObject(m_CanvasBitmap);

		//selects background, blits into canvas
		pOldBmp2 = m_PicDC->SelectObject(m_BkgBitmap);
		m_CanvasDC->BitBlt(0, 0, m_wBkg, m_hBkg, m_PicDC, 0, 0, SRCCOPY);

		//selects small pic, blits into canvas
		m_PicDC->SelectObject(m_Bitmap);
		m_CanvasDC->BitBlt(m_picture.topleft.x, 
			m_picture.topleft.y, 
			m_picture.width, 
			m_picture.height, 
			m_PicDC, 
			0, 0, SRCCOPY);

		//blits canvas on screen.The "dirty rectangle" part only.
		pDC->GetClipBox(cr);
		pDC->BitBlt(cr.left, cr.top, cr.right, cr.bottom, m_CanvasDC, cr.left, cr.top, SRCCOPY);

		//deselects all the bitmaps 
		m_CanvasDC->SelectObject(pOldBmp1);
		m_PicDC->SelectObject(pOldBmp2);


}

void CFlickerFreeDlg::OnSize(UINT nType, int cx, int cy) 
{
	ASSERT(!(m_CanvasBitmap == NULL));
	delete m_CanvasBitmap;
	m_CanvasBitmap = new CBitmap;
	CClientDC dc(this);
	m_CanvasBitmap->CreateCompatibleBitmap(&dc, cx, cy);

	CDialog::OnSize(nType, cx, cy);
}


BOOL CFlickerFreeDlg::OnEraseBkgnd(CDC* pDC) 
{
	return false;
}


CRect CFlickerFreeDlg::CalcDirtyRect(int pic_w, int pic_h, CPoint p1, CPoint p2)
{
	CSize PicSize(pic_w,pic_h);
	CRect r1(p1,PicSize);
	CRect r2(p2,PicSize);

	return r1 | r2;
}

void CFlickerFreeDlg::Animate(int count) 
{

	for (int i = 0; i < 40; i ++)
	{
	bAnimate = TRUE;

	CRect DirtyRc;
		DirtyRc.left = 0 + i*5;
		DirtyRc.right = DirtyRc.left + m_picture.width;
		DirtyRc.top = 0 + i*5;
		DirtyRc.bottom = DirtyRc.top + m_picture.height;

	m_picture.topleft.x = i*5;
	m_picture.topleft.y = i*5;

	if(i > (40 - count))
	{
	InvalidateRect(DirtyRc,1);
	}else if( i <= (40 - count) )
		{
		InvalidateRect(NULL,1);
		UpdateWindow();
		}

	bAnimate = FALSE;

	UpdateWindow();

	Sleep(100);
		
	}


}

void CFlickerFreeDlg::OnButton1() 
{
	Animate(9);
}


void CFlickerFreeDlg::OnButton2() 
{
	Animate(19);
}

Open in new window

Comment
Watch Question

Commented:
Firstly, all drawing code has to be in OnPaint method of the CFlickerFreeDlg. So the image will be always on the dialog. OnInitDialog works only once - when the dialog is loaded. In this function you can load the images, detect their sizes. But you can draw them in OnPaint.
Two buttons you mentioned, should set a state for a variable. Then, in OnPaint, you check this variable and draw accordingly.


Commented:
Here you will see how to put the drawing code in OnPaint of CDialog:
The Bdlg32.exe sample demonstrates four methods for how to place a bitmap on a dialog box in Visual C++
http://support.microsoft.com/kb/141863

The image is loaded into the 'bmp' variable (CBitmap bmp). It better to make this variable a member of the dialog class and load the image in OnInitDialog:

// Load the bitmap resource
bmp.LoadBitmap( IDB_CORPLOGO );

Here is one more example:
http://www.functionx.com/visualc/controls/picture.htm

Author

Commented:
Thank you for helping me out again, pgnatyuk!

Bellow I’m trying to build a simple animation from OnPaint(), where the resulted drawings are getting erased along with entire background after dragging dialogue off screen
void CMyDlg::OnPaint() 
{

    CPaintDC dc( this ); // Device context for painting

    CBitmap *poldbmp, *poldbmpPic;
    CDC memdc, memdcPic;

	if(bBackground)
	{
    memdc.CreateCompatibleDC( &dc );
    poldbmp = memdc.SelectObject( &bmp );
    dc.BitBlt( 0, 0, 700, 560, &memdc, 0, 0, SRCCOPY );
    memdc.SelectObject( poldbmp );
	bBackground = FALSE;
	}

	if(bAnimate)
	{
    memdcPic.CreateCompatibleDC( &dc );
    poldbmpPic = memdcPic.SelectObject( &bmpPic );
    dc.BitBlt( left, top, 14, 15, &memdcPic, 0, 0, SRCCOPY );
    memdcPic.SelectObject( poldbmpPic );
	}

}

void CMyDlg::OnButton3() 
{
	CRect DirtyRc;

	for ( int i = 0; i < 5; i ++)
	{

	bAnimate = TRUE;

	left = i*10;
	top =  i*10;

	DirtyRc.left = left;
	DirtyRc.right = left + 14;
	DirtyRc.top = top;
	DirtyRc.bottom = top + 15;

	InvalidateRect(DirtyRc,TRUE);
	UpdateWindow();

	bAnimate = FALSE;

	Sleep(200);

	}
	
}

Open in new window

Commented:
Are you sure you see something drawn by you on the dialog? You don't copy the images from this memdc onto dc.
AndyAinscowFreelance programmer / Consultant

Commented:
           Firstly - Is the background being displayed when you drag off and back on screen ?


Secondly - if it is only the other pictures then
pDC->GetClipBox(cr);
Is this actually working when you drag the dialog off and back on screen ?

Author

Commented:
pgnatyuk!

I did exactly what suggested from the link you provided:
http://support.microsoft.com/kb/141863
It does draw a bitmap background and animation places the pics in order I need except
when I drag my dialog off screen it erases everything including background

AndyAinscow!

Animation works in  both code snippets above but background is getting erased after dragging dialog off screen ..

Author

Commented:
Posting code once again:
BOOL CMyDlg::OnInitDialog()
{
	CDialog::OnInitDialog();

    bmp.LoadBitmap( IDB_BG );
    bmpPic.LoadBitmap( IDB_PIC );

	
	return TRUE;  // return TRUE  unless you set the focus to a control
}



BOOL CMyDlg::OnEraseBkgnd(CDC* pDC) 
{
	return FALSE;

}

void CMyDlg::OnPaint() 
{

    CPaintDC dc( this ); // Device context for painting

    CBitmap *poldbmp, *poldbmpPic;
    CDC memdc, memdcPic;

	if(bBackground)
	{

    memdc.CreateCompatibleDC( &dc );
    poldbmp = memdc.SelectObject( &bmp );
    dc.BitBlt( 0, 0, 700, 560, &memdc, 0, 0, SRCCOPY );
    memdc.SelectObject( poldbmp );
	bBackground = FALSE;
	}

	if(bAnimate)
	{
			dc.SetBkMode (TRANSPARENT);
    memdcPic.CreateCompatibleDC( &dc );
    poldbmpPic = memdcPic.SelectObject( &bmpPic );
    dc.BitBlt( left, top, 14, 15, &memdcPic, 0, 0, SRCCOPY );
    memdcPic.SelectObject( poldbmpPic );
	}

///////////////////////////////////////////////////////////

	if(bButton1)
	{
	// text drawing
	dc.SetBkMode (TRANSPARENT);
	dc.SetTextColor (RGB(255,255,255));
	dc.TextOut ( 155, 155, m_strText, strlen ( m_strText) ) ;
	bButton1 = FALSE;
	}


	if(bButton2)
	{
	// text drawing
	dc.SetBkMode (TRANSPARENT);
	dc.SetTextColor (RGB(255,255,255));
	dc.TextOut ( 155, 255, m_strText, strlen ( m_strText) ) ;
	bButton2 = FALSE;
	}



}

void CMyDlg::OnDestroy()
{
    CDialog::OnDestroy();
    
}


HCURSOR CMyDlg::OnQueryDragIcon()
{
	return (HCURSOR) m_hIcon;
}
 
void CMyDlg::OnButton1() 
{
	bButton1 = TRUE;
	m_strText = "Text from OnButton1";
	Invalidate(FALSE);	
}


void CMyDlg::OnButton2() 
{
	bButton2 = TRUE;
	m_strText = "Text from OnButton2";
	Invalidate(FALSE);	
}

void CMyDlg::OnButton3() 
{
	
	CRect DirtyRc;

	for ( int i = 0; i < 5; i ++)
	{

	bAnimate = TRUE;

	left = i*10;
	top =  i*10;

	DirtyRc.left = left;
	DirtyRc.right = left + 14;
	DirtyRc.top = top;
	DirtyRc.bottom = top + 15;

	InvalidateRect(DirtyRc,TRUE);
	UpdateWindow();

	bAnimate = FALSE;

	Sleep(200);

	}

	
}

Open in new window

Commented:
I wanted to say that the code look good.
But these small things like:

       InvalidateRect(DirtyRc,TRUE);
        UpdateWindow();

        bAnimate = FALSE;

        Sleep(200);

Why you need this trick?
Because of the animation? Start a timer with SetTime OnButton3 and KillTimer if the button pressed again. in the timer function you may Invalidate(FALSE). That will work fine.

Please post a screenshot about an erased background - I really do not understand what you mean.


Author

Commented:
From the code attached bellow background comes out properly.
I still do not see accumulation of moving objects (explained in Image attached)..
also, I just realize that is simply because of my background is redrawn every time to its original state held by bmp (CBitmap bmp) ..
so i need to find a way to copy a newly repainted background(with pic in it) into bmp in order to pass it to OnPaint each time in a loop
Once again I would like to have the object(red square shown in attached image) moving from the top left corner of my dialog and at some point starting to paint itself into the background each time after the move
void CMyDlg::OnPaint() 
{

    CPaintDC dc( this ); // Device context for painting

    CBitmap *poldbmp, *poldbmpPic;
    CDC memdc, memdcPic;

    memdc.CreateCompatibleDC( &dc );
    poldbmp = memdc.SelectObject( &bmp );
    dc.BitBlt( 0, 0, 700, 560, &memdc, 0, 0, SRCCOPY );
    memdc.SelectObject( poldbmp );

	dc.SetBkMode (TRANSPARENT);
    memdcPic.CreateCompatibleDC( &dc );
    poldbmpPic = memdcPic.SelectObject( &bmpPic );
    dc.BitBlt( left, top, 14, 15, &memdcPic, 0, 0, SRCCOPY );
    memdcPic.SelectObject( poldbmpPic );
	bAnimate = FALSE;


}

void CMyDlg::OnButton3() 
{

	for ( int i = 0; i < 5; i ++)
	{

	left = i*10;
	top =  i*10;

	SetTimer(1,200,NULL);

	}
}


void CMyDlg::OnTimer(UINT nIDEvent) 
{
	InvalidateRect(FALSE);
	KillTimer(1);
	CDialog::OnTimer(nIDEvent);
}

Open in new window

Image.gif

Commented:
I cannot say that I fully understand what kind and how many bitmap you have right now.
The background is a bitmap too?
Make two functions:
1) DrawBackground(CDC* pDC, LPRECT lpRect);
2) DrawObject(CDC* pDC, LPRECT lpRect);
Call them from OnPaint. If the m_bAnimation is TRUE move the coordinates of the object before DrawObject call.

Author

Commented:
Dialog's background is a bitmap (all light red)
After OnButton3() the dark red square(presenting another bitmap) appeals in a top left corner and starts moving diagonally(trace shown in dots) and then at some point the dark red square gets painted into background with each next move, animation stops and, if dialogs is dragged off screen or other drawings occur in dialog afterwards the results of animation must stay in background.
Different animated drawings  can take a place afterwards as a response to user events like buttons clicks

Commented:
Because you need to erase the image.

You can implement everything as I have said:

From OnPaint you call DrawBackground and DrawObject.
In DrawBackground you draw the background image. This function receives the dc from the OnPaint and the window rectangle.

In DrawObject you draw the object in a certain position. This position is a member of your dialog class. This position can be updated in a timer handler. This timer handler you need to start when the user presses on the button.

In OnPaint method, if you wish, you can create a memory DC and use this DC for the darwing. In the end copy the image from this memory DC on the main dc (CPaintDC). I'm not sure, this is a very simple case.

I do not know why you cannot implement it.
To get the window rectangle for the background drawing - GetClientRect function.
CPaintDC exists in the OnPaint method.

Author

Commented:
Only draws the last remains on background from the code bellow
I need somehow to copy a just  painted background into bitmap and then draw one onto DC out of every step from OnTimer  so, all the drawn object remain permanently in dialog's background

BOOL CMyDlg::DrawPicture(HDC hDC, HBITMAP hBitmap, LPRECT lpRect) 
{ 
        if (hBitmap == NULL) 
                return FALSE; 
        BITMAP bitmap = { 0 }; 
        GetObject(hBitmap, sizeof(BITMAP), &bitmap); 
        HDC hMemDC = CreateCompatibleDC(hDC); 
        HBITMAP hBitmapOld = (HBITMAP)SelectObject(hMemDC, hBitmap); 
        StretchBlt(hDC, lpRect->left, lpRect->top,  
                lpRect->right - lpRect->left, 
                lpRect->bottom - lpRect->top,  
                hMemDC, 0, 0, bitmap.bmWidth, bitmap. bmHeight,  
                SRCCOPY); 
        SelectObject(hMemDC, hBitmapOld); 
        DeleteDC(hMemDC); 
        return TRUE; 
} 

 
void CMyDlg::DrawBkgnd(CDC* pDC)
{

	if(!m_bitmap.m_hObject)
	return ; 

	CRect rect;
	GetClientRect(&rect);
	CDC dc;
	dc.CreateCompatibleDC(pDC);
	CBitmap* pOldBitmap = dc.SelectObject(&m_bitmap);

	pDC->BitBlt(0, 0, 700,
	560, &dc,0, 0, SRCCOPY); 
	dc.SelectObject(pOldBitmap);
	DeleteObject(pOldBitmap);
	pOldBitmap = NULL;


}

void CMyDlg::OnButton3() 
{

	for ( int i = 0; i < 5; i ++)
	{

	left = i*10;
	top =  i*10;

	SetTimer(1,200,NULL);

	}
}


void CMyDlg::OnTimer(UINT nIDEvent) 
{
	CRect rect;
	rect.left = left; rect.right = left + 14; rect.top = top; rect.bottom = top + 15;

	InvalidateRect(rect,1);
	KillTimer(1);
	CDialog::OnTimer(nIDEvent);
}

Open in new window

Commented:
You need to post also OnPaint method.

If in OnPaint you always the draw background and then the picture, the screen will be ok.
I think, it is already so. Because I see the error in another place - the rect in OnTimer.

This rect that is used in InvalidateRect should have the size of two pictures - the old one that was drawn in the previous iteration and the new one - the one you will draw now. Also the second parameter for InvalidateRect can be FALSE in this case.

Commented:
For a test, you can use the background size for this InvalidateRect call in OnTimer:
        RECT rect = { 0, 0, 700, 560 };
        InvalidateRect(rect, FALSE);

Author

Commented:
RECT rect = { left, top, 700, 560 };
gives an error C2664: 'InvalidateRect' : cannot convert parameter 1 from 'struct tagRECT' to 'const struct tagRECT

I tried this:

      CRect rect;
      rect.left = 0; rect.right = 700; rect.top = 0; rect.bottom = 560;
      InvalidateRect(rect,FALSE);
still the same:
I do not see the pic walking as desired and
The object only apeals in the last step on dialog's background
 

void CMyDlg::OnPaint() 
{

    CPaintDC dc( this ); // Device context for painting
	DrawBkgnd(&dc);
	CRect rect;
	rect.left = left; rect.right = left + 14; rect.top = top; rect.bottom = top + 15;

	DrawPicture(dc, bmpPic, CRect(left, top, left+14, top+15));
}

Open in new window

Commented:
The RECT begins at 0,0 and with & before rect it in InvalidateRect it will compile and work fine.
Check that the moving object is in rectangle 0,0,700,700 (the coordinates of your background image on the screen). Check that you dra the background each time before the object. Draw a single line or a dot instead of the drawing object. draw a solid rectangle instead of the background image. If you will follow this plan you will find the mistake.
Commented:
I attached an example - it's h- and the cpp-files.
I didn't do something special. It is a simple MFC dialog-based application. There are two special buttons on the dialog - start and stop, to start and to stop the "animation".
Two images are loaded from the bmp-files.
You will see the functions I told about: DrawBk and DrawObject. You will see how I call them frm OnPaint.
Pay attention on the OnTimer function - the "moving" happens there - I move the object rectange and invalidate the dialog. The invalidation rectangle can be just the background rectangle:
void CMoveObjectDlg::OnTimer(UINT_PTR nIDEvent)
{
    m_rcObject.MoveToXY(m_rcObject.left + 10, m_rcObject.top + 10);
    if (m_rcBk.right < m_rcObject.right)
          m_rcObject.MoveToXY(m_rcBk.left, m_rcBk.top);
    if (m_rcBk.bottom < m_rcObject.bottom)
         m_rcObject.MoveToXY(m_rcBk.left, m_rcBk.top);
     InvalidateRect(m_rcBk, FALSE);
}
 
 or something more complicated but correct.



// Header file
// MoveObjectDlg.h : header file
//

#pragma once


// CMoveObjectDlg dialog
class CMoveObjectDlg : public CDialog
{
	enum
	{
		IDD_TIME	= 200,
		IDD_TIMER	= 12345
	};

	UINT_PTR	m_nTimer;
	CBitmap		m_Bk;
	CBitmap		m_Object;
	CRect		m_rcBk;
	CRect		m_rcObject;

	void LoadImages();
	void DrawBk(CDC* pDC);
	void DrawObject(CDC* pDC);
	void StartTimer();
	void StopTimer();

// Construction
public:
	CMoveObjectDlg(CWnd* pParent = NULL);	// standard constructor

// Dialog Data
	enum { IDD = IDD_MOVEOBJECT_DIALOG };

	protected:
	virtual void DoDataExchange(CDataExchange* pDX);	// DDX/DDV support


// Implementation
protected:
	HICON m_hIcon;

	// Generated message map functions
	virtual BOOL OnInitDialog();
	afx_msg void OnPaint();
	afx_msg HCURSOR OnQueryDragIcon();
	DECLARE_MESSAGE_MAP()

public:
	afx_msg void OnBnClickedButtonStart();
	afx_msg void OnBnClickedButtonStop();
	afx_msg void OnTimer(UINT_PTR nIDEvent);
};

// cpp-file
// MoveObjectDlg.cpp : implementation file
//

#include "stdafx.h"
#include "MoveObject.h"
#include "MoveObjectDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

// CMoveObjectDlg dialog
CMoveObjectDlg::CMoveObjectDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CMoveObjectDlg::IDD, pParent)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
	m_nTimer = 0;
}

void CMoveObjectDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CMoveObjectDlg, CDialog)
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	//}}AFX_MSG_MAP
	ON_WM_TIMER()
	ON_BN_CLICKED(IDC_BUTTON_START, &CMoveObjectDlg::OnBnClickedButtonStart)
	ON_BN_CLICKED(IDC_BUTTON_STOP, &CMoveObjectDlg::OnBnClickedButtonStop)
END_MESSAGE_MAP()


// CMoveObjectDlg message handlers

BOOL CMoveObjectDlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	// Set the icon for this dialog.  The framework does this automatically
	//  when the application's main window is not a dialog
	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon

	LoadImages();
	return TRUE;  // return TRUE  unless you set the focus to a control
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CMoveObjectDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// Center icon in client rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// Draw the icon
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CPaintDC dc(this);
		DrawBk(&dc);
		DrawObject(&dc);
		CDialog::OnPaint();
	}
}

// The system calls this function to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CMoveObjectDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}

void CMoveObjectDlg::LoadImages()
{
	HBITMAP hBitmap = (HBITMAP)LoadImage(NULL, L"Bk.bmp", IMAGE_BITMAP, 0, 0,
		LR_CREATEDIBSECTION | LR_DEFAULTSIZE | LR_LOADFROMFILE);
	if (hBitmap != NULL) 
	{
		BITMAP bm = { 0 };
		GetObject(hBitmap, sizeof(BITMAP), &bm);

		m_rcBk.SetRect(10, 10, 10 + bm.bmWidth, 10 + bm.bmHeight);
		m_Bk.Attach(hBitmap);
	}

	hBitmap = (HBITMAP)LoadImage(NULL, L"Object.bmp", IMAGE_BITMAP, 0, 0,
		LR_CREATEDIBSECTION | LR_DEFAULTSIZE | LR_LOADFROMFILE);
	if (hBitmap != NULL) 
	{
		BITMAP bm = { 0 };
		GetObject(hBitmap, sizeof(BITMAP), &bm);
		m_rcObject.SetRect(10, 10, 10 + bm.bmWidth, 10 + bm.bmHeight);
		m_Object.Attach(hBitmap);
	}
}

void CMoveObjectDlg::StartTimer()
{
	m_nTimer = SetTimer(IDD_TIMER, IDD_TIME, NULL);
}

void CMoveObjectDlg::StopTimer()
{
	if (m_nTimer != 0)
	{
		KillTimer(IDD_TIMER);
		m_nTimer = NULL;
	}
}

void CMoveObjectDlg::OnBnClickedButtonStart()
{
	StartTimer();
}

void CMoveObjectDlg::OnBnClickedButtonStop()
{
	StopTimer();
}

void CMoveObjectDlg::DrawBk(CDC* pDC)
{
	if ((HBITMAP)m_Bk != NULL) 
	{
		CDC dc;
		dc.CreateCompatibleDC(pDC);
		HGDIOBJ hOld = dc.SelectObject(m_Bk);
		pDC->BitBlt(m_rcBk.left, m_rcBk.top, 
			m_rcBk.right - m_rcBk.left, m_rcBk.bottom - m_rcBk.top,
			&dc, 0, 0, SRCCOPY);
		dc.SelectObject(hOld);
		dc.DeleteDC();
	}
}

void CMoveObjectDlg::DrawObject(CDC *pDC)
{
	if ((HBITMAP)m_Object != NULL) 
	{
		CDC dc;
		dc.CreateCompatibleDC(pDC);
		HGDIOBJ hOld = dc.SelectObject(m_Object);
		pDC->BitBlt(m_rcObject.left, m_rcObject.top, 
			m_rcObject.right - m_rcObject.left, m_rcObject.bottom - m_rcObject.top,
			&dc, 0, 0, SRCCOPY);
		dc.SelectObject(hOld);
		dc.DeleteDC();
	}
}

void CMoveObjectDlg::OnTimer(UINT_PTR nIDEvent)
{
	m_rcObject.MoveToXY(m_rcObject.left + 10, m_rcObject.top + 10);
	if (m_rcBk.right < m_rcObject.right)
		m_rcObject.MoveToXY(m_rcBk.left, m_rcBk.top);
	if (m_rcBk.bottom < m_rcObject.bottom)
		m_rcObject.MoveToXY(m_rcBk.left, m_rcBk.top);
	Invalidate(FALSE);
}

Open in new window

first.PNG
second.PNG

Author

Commented:
pgnatyuk!

I really appreciate your input
I will be working on your sample,

Thanks again

Commented:
you are welcome