From: Luigino on
Hello Joe and Stephen,

maybe I am a bit lost about those OnDraw and OnPaint things... so I
modified the code to put everything only in the OnPaint so the
behaviour about DC should be correct BUT when I set MM_ANISOTROPIC and
new origin to bottom right in the OnPaint as you said it doesn't make
the graphic sliding from right to left...

Here I paste the full code of this test app (the CMemDC mDC(&dc) is an
external class):

In the .H file I have:

#ifdef MYDLL_BUILD
#define MYDLL_IO _declspec(dllexport) //conditional on MYLIB_BUILD
#else
#define MYDLL_IO _declspec(dllimport)
#endif

class MYDLL_IO CMyDLL : public CWnd
{
DECLARE_DYNCREATE(CMyDLL)

// matrix of POINT for each element to represent
typedef vector<POINT> m_fPoints; // array
representing points values for each single source
typedef vector<m_fPoints> m_Points; // array
representing list of sources having their POINT values

public:
CMyDLL(CWnd *parent, CWnd *parentcontrol, CRect parentrect,
int nparentID);
virtual ~CMyDLL();

public:
void set_MinMax(int minvalue, int maxvalue, int maxsizearray);
void AddElement(UINT nX, UINT nY, const char * description,
double value);

protected:
BOOL scrollState;
UINT iTimerVal;
UINT nElapse;
BOOL bSetDraw;
m_Points
mPoints; // matrix of
sources representing POINT values
int mSourceMaxSizeArray;

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CMyDLL)
protected:
virtual void PreSubclassWindow();
//}}AFX_VIRTUAL

protected:
BOOL EnableTimer(BOOL tmrstate);
int OnCreate(LPCREATESTRUCT lpCreateStruct);

public:
static BOOL RegisterWindowClass();

protected:
//{{AFX_MSG(CMyDLL)
afx_msg void OnPaint();
afx_msg BOOL OnEraseBkgnd(CDC* pDC);
afx_msg void OnTimer(UINT TimerVal);
afx_msg void OnWindowPosChanged(WINDOWPOS* lpwndpos);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};

In the .CPP file I have:

#ifdef _DEBUG
#define new DEBUG_NEW
#define GDI_FLUSH() GdiFlush()
#else
#define GDI_FLUSH()
#endif

#define CMYDLL_CLASSNAME _T("MFCMyDLLCtrlTest") // Window class
name
#define IDT_TIMER_0 WM_USER + 1000

IMPLEMENT_DYNAMIC(CMyDLL, CWnd)

CMyDLL::CMyDLL(CWnd *parent, CWnd *parentcontrol, CRect parentrect,
int nparentID)
{
RegisterWindowClass();

scrollState = TRUE;
gridOffset = 0;
bSetDraw = TRUE;
nElapse = 1000;
iTimerVal = 0;

// Initializing graph's DC...
if( !Create(_T("MFCMyDLLCtrlTest "), NULL, WS_CHILD|
WS_VISIBLE, parentrect, parent, nparentID) )
{
MessageBox(_T("Unable to create object!"),
_T("Alert!"), MB_OK|MB_ICONSTOP|MB_SYSTEMMODAL);
}
SetWindowPos(parentcontrol, 0, 0, 0, 0, SWP_NOSIZE|
SWP_NOMOVE);
}

CMyDLL::~ CMyDLL ()
{
DestroyWindow();
}

// Register the window class if it has not already been registered.
BOOL C CMyDLL::RegisterWindowClass()
{
WNDCLASS wndcls;
HINSTANCE hInst = AfxGetInstanceHandle();

if (!(::GetClassInfo(hInst, CMYDLL_CLASSNAME, &wndcls)))
{
// otherwise we need to register a new class
wndcls.style = CS_DBLCLKS | CS_HREDRAW |
CS_VREDRAW;
wndcls.lpfnWndProc = ::DefWindowProc;
wndcls.cbClsExtra = wndcls.cbWndExtra = 0;
wndcls.hInstance = hInst;
wndcls.hIcon = NULL;
wndcls.hCursor = AfxGetApp()-
>LoadStandardCursor(IDC_ARROW);
wndcls.hbrBackground = (HBRUSH) (COLOR_3DFACE + 1);
wndcls.lpszMenuName = NULL;
wndcls.lpszClassName = CACHART_CLASSNAME;

if (!AfxRegisterClass(&wndcls))
{
AfxThrowResourceException();
return FALSE;
}
}

return TRUE;
}

BEGIN_MESSAGE_MAP(CMyDLL, CWnd)
//{{AFX_MSG_MAP(CMyDLL)
ON_WM_PAINT()
ON_WM_CREATE()
ON_WM_ERASEBKGND()
ON_WM_SIZE()
ON_WM_WINDOWPOSCHANGED()
ON_WM_TIMER()
ON_MESSAGE(WM_ENTERSIZEMOVE, OnEnterSizeMove)
ON_MESSAGE(WM_EXITSIZEMOVE, OnExitSizeMove)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

int CMyDLL::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
int ret = CWnd::OnCreate(lpCreateStruct);

EnableTimer(scrollState);

return ret;
}

//{{AFX_MSG(CMyDLL) - message handlers
void CMyDLL::OnPaint()
{
CPaintDC dc(this); // device context for painting

CRect rect;
GetClientRect(&rect);

int save = dc.SaveDC();

//dc.FillRect(rect,
CBrush::FromHandle((HBRUSH)GetStockObject(BLACK_BRUSH)));

dc.SetBkColor(RGB(0,0,0));
dc.SetMapMode(MM_ANISOTROPIC);
dc.SetWindowOrg(rect.BottomRight());
dc.SetViewportOrg(0, 0);
dc.SetViewportExt(-1, -1);
dc.SetWindowExt(1, 1);

if(bSetDraw)
{
CMemDC mDC(&dc);

// ********** Background ***********
// Grid
if (bActivateGrid)
{
CPen qLinePen(PS_SOLID, 1, RGB(0,139,0));
mDC->SelectObject(&qLinePen);

// Grid - Horizontal lines
mDC->MoveTo(1, 0);
mDC->LineTo(rect.Width(), 0);
int height = rect.Height();
int maxlines = height / (int)12.5;
for (int i=1;i<=maxlines;i++){
int y_axis = (int)((double)i * 12.5);
if (y_axis <= rect.Height()) {
mDC->MoveTo(1, y_axis);
mDC->LineTo(rect.Width(),
y_axis);
}
}

// Grid - Vertical lines
mDC->MoveTo(0, 0);
mDC->LineTo(0, rect.Height());
int width = rect.Width();
maxlines = width / (int)12.5;
for (int i=1;i<=maxlines;i++){
int x_axis = (int)(((double)i * 12.5)
- gridOffset);
if (x_axis <= rect.Width()) {
mDC->MoveTo(x_axis, 1);
mDC->LineTo(x_axis,
rect.Height());
}
}

qLinePen.DeleteObject();
}
// *********** graphic component ***********
// based on choice of graph type
CPen qPolylinePen(PS_SOLID, 1, RGB(0, 255, 0));
switch (iGraphType)
{
case GRAPH_BARS:
break;

case GRAPH_LINES:
{
if (mPoints.capacity() == 1)
{
mDC-
>SelectObject(qPolylinePen);
m_fPoints* pointsline =
&mPoints[0];
mDC->Polyline(&(*pointsline)
[0], (int)pointsline->size());
//mDC->PolyPolyline()
qPolylinePen.DeleteObject();
}
}
break;

default:
break;
}
GDI_FLUSH();
}

dc.RestoreDC(save);
}

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

void CMyDLL::OnTimer(UINT TimerVal)
{
// ****** processing event ******
if (TimerVal == IDT_TIMER_0)
{
gridOffset++;
if (gridOffset > 12.5)
gridOffset = 0.0;

Invalidate(); // to call OnDraw()/OnPaint()
UpdateWindow();
}
// call base class handler
CWnd::OnTimer(TimerVal);
}

void CMyDLL::OnWindowPosChanged(WINDOWPOS* lpwndpos)
{
CWnd::OnWindowPosChanged(lpwndpos);
}

void CMyDLL::PreSubclassWindow()
{
// TODO: Add your specialized code here and/or call the base class
// In our case this is not needed - yet - so just drop through to
// the base class

// Get Size of Display area
CWnd::PreSubclassWindow();
}

void CMyDLL::set_MinMax(int minvalue, int maxvalue, int maxsizearray)
{
iMinRange = minvalue;
iMaxRange = maxvalue;
mSourceMaxSizeArray = maxsizearray; //
getting max size array of each source which has to be fixed
mPoints.resize(1);
mPoints[0].resize(mSourceMaxSizeArray);

CRect rc;
GetClientRect(&rc);
int iPointOfOrigin = rc.Width();
for (int i=mPoints[0].size()-1;i>0;i--) //
initializing this first source to initial values

{ //
which are consecutive integer values for X-axis
mPoints[0].at(i).x = iPointOfOrigin--; //
and bottom screen value for Y-axis
mPoints[0].at(i).y = 0;
}
}

BOOL CMyDLL::EnableTimer(BOOL tmrstate)
{
//
***************************************************************************
// if enabled it's a realtime task manager, otherwise it'd be
a graphical
// representation of values passed from external source
//
***************************************************************************

if (tmrstate)
{
iTimerVal = SetTimer(IDT_TIMER_0, nElapse, 0);
if (iTimerVal == 0)
{
MessageBox(_T("Unable to obtain timer!"),
_T("IDT_TIMER_0"), MB_OK|MB_ICONSTOP|MB_SYSTEMMODAL);
return FALSE;
}
}
else
{
if (iTimerVal > 0)
{
if (!KillTimer(IDT_TIMER_0))
{
// message
MessageBox(_T("Unable to stop
timer!"), _T("IDT_TIMER_0"), MB_OK|MB_ICONSTOP|MB_SYSTEMMODAL);
return FALSE;
}
}
}

return TRUE;
}

void CMyDLL::AddElement(UINT nX, UINT nY, const char * description,
double value)
{
// add in value format because CPoint depends from resize...
int i;
CRect rc;
::GetClientRect(this->m_hWnd, &rc);

if (nY+1 > mElements.size()) // check if I am adding a new
source
{
mPoints.resize(nY
+1); // setting the array to max
size defined
mPoints[nY].resize(mSourceMaxSizeArray);
int iPointOfOrigin = 0;
for (i=mPoints[nY].size()-1;i>0;i--) //
initializing this first source to initial values

{ //
which are consecutive integer values for X-axis
mPoints[nY].at(i).x = iPointOfOrigin++; // and
bottom screen value for Y-axis
mPoints[nY].at(i).y = 0;
}
}

// Now I add element value to the source

if (scrollState) {
// SHIFT-LEFT the array of POINTs to add new value
rotate(mPoints[nY].begin(), mPoints[nY].begin()+1,
mPoints[nY].end());
mPoints[nY].at(mPoints[nY].size() - 1).y = (LONG)
( (double)rand() / (RAND_MAX + 1) * (rc.bottom - 1) + 1 );
int iPointOfOrigin = rc.Width();
for
(i=mPoints[nY].size()-1;i>0;i--)
mPoints[nY].at(i).x = iPointOfOrigin--;
}

if (nY > mGraphElements.capacity())
{
GraphElement single_element;
single_element.description = description;
single_element.color = mGraphColors.at(nY);
mGraphElements.push_back(single_element);
}

Invalidate();
UpdateWindow();
}

Thanks....
Ciao
Luigi
From: Stephen Myers on
Luigino wrote:
> Hello Joe and Stephen,
>
> maybe I am a bit lost about those OnDraw and OnPaint things... so I
> modified the code to put everything only in the OnPaint so the
> behaviour about DC should be correct BUT when I set MM_ANISOTROPIC and
> new origin to bottom right in the OnPaint as you said it doesn't make
> the graphic sliding from right to left...
>
> Here I paste the full code of this test app (the CMemDC mDC(&dc) is an
> external class):
>
> In the .H file I have:
>
> #ifdef MYDLL_BUILD
> #define MYDLL_IO _declspec(dllexport) //conditional on MYLIB_BUILD
> #else
> #define MYDLL_IO _declspec(dllimport)
> #endif
>
> class MYDLL_IO CMyDLL : public CWnd
> {
> DECLARE_DYNCREATE(CMyDLL)
>
> // matrix of POINT for each element to represent
> typedef vector<POINT> m_fPoints; // array
> representing points values for each single source
> typedef vector<m_fPoints> m_Points; // array
> representing list of sources having their POINT values
>
> public:
> CMyDLL(CWnd *parent, CWnd *parentcontrol, CRect parentrect,
> int nparentID);
> virtual ~CMyDLL();
>
> public:
> void set_MinMax(int minvalue, int maxvalue, int maxsizearray);
> void AddElement(UINT nX, UINT nY, const char * description,
> double value);
>
> protected:
> BOOL scrollState;
> UINT iTimerVal;
> UINT nElapse;
> BOOL bSetDraw;
> m_Points
> mPoints; // matrix of
> sources representing POINT values
> int mSourceMaxSizeArray;
>
> // Overrides
> // ClassWizard generated virtual function overrides
> //{{AFX_VIRTUAL(CMyDLL)
> protected:
> virtual void PreSubclassWindow();
> //}}AFX_VIRTUAL
>
> protected:
> BOOL EnableTimer(BOOL tmrstate);
> int OnCreate(LPCREATESTRUCT lpCreateStruct);
>
> public:
> static BOOL RegisterWindowClass();
>
> protected:
> //{{AFX_MSG(CMyDLL)
> afx_msg void OnPaint();
> afx_msg BOOL OnEraseBkgnd(CDC* pDC);
> afx_msg void OnTimer(UINT TimerVal);
> afx_msg void OnWindowPosChanged(WINDOWPOS* lpwndpos);
> //}}AFX_MSG
> DECLARE_MESSAGE_MAP()
> };
>
> In the .CPP file I have:
>
> #ifdef _DEBUG
> #define new DEBUG_NEW
> #define GDI_FLUSH() GdiFlush()
> #else
> #define GDI_FLUSH()
> #endif
>
> #define CMYDLL_CLASSNAME _T("MFCMyDLLCtrlTest") // Window class
> name
> #define IDT_TIMER_0 WM_USER + 1000
>
> IMPLEMENT_DYNAMIC(CMyDLL, CWnd)
>
> CMyDLL::CMyDLL(CWnd *parent, CWnd *parentcontrol, CRect parentrect,
> int nparentID)
> {
> RegisterWindowClass();
>
> scrollState = TRUE;
> gridOffset = 0;
> bSetDraw = TRUE;
> nElapse = 1000;
> iTimerVal = 0;
>
> // Initializing graph's DC...
> if( !Create(_T("MFCMyDLLCtrlTest "), NULL, WS_CHILD|
> WS_VISIBLE, parentrect, parent, nparentID) )
> {
> MessageBox(_T("Unable to create object!"),
> _T("Alert!"), MB_OK|MB_ICONSTOP|MB_SYSTEMMODAL);
> }
> SetWindowPos(parentcontrol, 0, 0, 0, 0, SWP_NOSIZE|
> SWP_NOMOVE);
> }
>
> CMyDLL::~ CMyDLL ()
> {
> DestroyWindow();
> }
>
> // Register the window class if it has not already been registered.
> BOOL C CMyDLL::RegisterWindowClass()
> {
> WNDCLASS wndcls;
> HINSTANCE hInst = AfxGetInstanceHandle();
>
> if (!(::GetClassInfo(hInst, CMYDLL_CLASSNAME, &wndcls)))
> {
> // otherwise we need to register a new class
> wndcls.style = CS_DBLCLKS | CS_HREDRAW |
> CS_VREDRAW;
> wndcls.lpfnWndProc = ::DefWindowProc;
> wndcls.cbClsExtra = wndcls.cbWndExtra = 0;
> wndcls.hInstance = hInst;
> wndcls.hIcon = NULL;
> wndcls.hCursor = AfxGetApp()-
>> LoadStandardCursor(IDC_ARROW);
> wndcls.hbrBackground = (HBRUSH) (COLOR_3DFACE + 1);
> wndcls.lpszMenuName = NULL;
> wndcls.lpszClassName = CACHART_CLASSNAME;
>
> if (!AfxRegisterClass(&wndcls))
> {
> AfxThrowResourceException();
> return FALSE;
> }
> }
>
> return TRUE;
> }
>
> BEGIN_MESSAGE_MAP(CMyDLL, CWnd)
> //{{AFX_MSG_MAP(CMyDLL)
> ON_WM_PAINT()
> ON_WM_CREATE()
> ON_WM_ERASEBKGND()
> ON_WM_SIZE()
> ON_WM_WINDOWPOSCHANGED()
> ON_WM_TIMER()
> ON_MESSAGE(WM_ENTERSIZEMOVE, OnEnterSizeMove)
> ON_MESSAGE(WM_EXITSIZEMOVE, OnExitSizeMove)
> //}}AFX_MSG_MAP
> END_MESSAGE_MAP()
>
> int CMyDLL::OnCreate(LPCREATESTRUCT lpCreateStruct)
> {
> int ret = CWnd::OnCreate(lpCreateStruct);
>
> EnableTimer(scrollState);
>
> return ret;
> }
>
> //{{AFX_MSG(CMyDLL) - message handlers
> void CMyDLL::OnPaint()
> {
> CPaintDC dc(this); // device context for painting
>
> CRect rect;
> GetClientRect(&rect);
>
=======================================================================
No reason to do anything to dc. All Drawing will be in mDC. Do all the
setup stuff on mDC.
=======================================================================
> int save = dc.SaveDC();
>
> //dc.FillRect(rect,
> CBrush::FromHandle((HBRUSH)GetStockObject(BLACK_BRUSH)));
>
> dc.SetBkColor(RGB(0,0,0));
> dc.SetMapMode(MM_ANISOTROPIC);
> dc.SetWindowOrg(rect.BottomRight());
> dc.SetViewportOrg(0, 0);
> dc.SetViewportExt(-1, -1);
> dc.SetWindowExt(1, 1);
>
> if(bSetDraw)
> {
> CMemDC mDC(&dc);
>
> // ********** Background ***********
> // Grid
> if (bActivateGrid)
> {
> CPen qLinePen(PS_SOLID, 1, RGB(0,139,0));
> mDC->SelectObject(&qLinePen);
==================================================================
mDC is an object, you should not be using 'mDC->'. This should be
'mDC.' instead.
==================================================================
>
> // Grid - Horizontal lines
> mDC->MoveTo(1, 0);
> mDC->LineTo(rect.Width(), 0);
> int height = rect.Height();
> int maxlines = height / (int)12.5;
> for (int i=1;i<=maxlines;i++){
> int y_axis = (int)((double)i * 12.5);
> if (y_axis <= rect.Height()) {
> mDC->MoveTo(1, y_axis);
> mDC->LineTo(rect.Width(),
> y_axis);
> }
> }
>
> // Grid - Vertical lines
> mDC->MoveTo(0, 0);
> mDC->LineTo(0, rect.Height());
> int width = rect.Width();
> maxlines = width / (int)12.5;
> for (int i=1;i<=maxlines;i++){
> int x_axis = (int)(((double)i * 12.5)
> - gridOffset);
> if (x_axis <= rect.Width()) {
> mDC->MoveTo(x_axis, 1);
> mDC->LineTo(x_axis,
> rect.Height());
> }
> }
>
> qLinePen.DeleteObject();
> }
> // *********** graphic component ***********
> // based on choice of graph type
> CPen qPolylinePen(PS_SOLID, 1, RGB(0, 255, 0));
> switch (iGraphType)
> {
> case GRAPH_BARS:
> break;
>
> case GRAPH_LINES:
> {
> if (mPoints.capacity() == 1)
> {
> mDC-
>> SelectObject(qPolylinePen);
> m_fPoints* pointsline =
> &mPoints[0];
> mDC->Polyline(&(*pointsline)
> [0], (int)pointsline->size());
> //mDC->PolyPolyline()
> qPolylinePen.DeleteObject();
> }
> }
> break;
>
> default:
> break;
> }
> GDI_FLUSH();
> }
>
> dc.RestoreDC(save);
> }
>
> BOOL CMyDLL::OnEraseBkgnd(CDC* pDC)
> {
> return FALSE;
> }
>
> void CMyDLL::OnTimer(UINT TimerVal)
> {
> // ****** processing event ******
> if (TimerVal == IDT_TIMER_0)
> {
> gridOffset++;
> if (gridOffset > 12.5)
> gridOffset = 0.0;
>
> Invalidate(); // to call OnDraw()/OnPaint()
> UpdateWindow();
> }
> // call base class handler
> CWnd::OnTimer(TimerVal);
> }
>
> void CMyDLL::OnWindowPosChanged(WINDOWPOS* lpwndpos)
> {
> CWnd::OnWindowPosChanged(lpwndpos);
> }
>
> void CMyDLL::PreSubclassWindow()
> {
> // TODO: Add your specialized code here and/or call the base class
> // In our case this is not needed - yet - so just drop through to
> // the base class
>
> // Get Size of Display area
> CWnd::PreSubclassWindow();
> }
>
> void CMyDLL::set_MinMax(int minvalue, int maxvalue, int maxsizearray)
> {
> iMinRange = minvalue;
> iMaxRange = maxvalue;
> mSourceMaxSizeArray = maxsizearray; //
> getting max size array of each source which has to be fixed
> mPoints.resize(1);
> mPoints[0].resize(mSourceMaxSizeArray);
>
> CRect rc;
> GetClientRect(&rc);
> int iPointOfOrigin = rc.Width();
> for (int i=mPoints[0].size()-1;i>0;i--) //
> initializing this first source to initial values
>
> { //
> which are consecutive integer values for X-axis
> mPoints[0].at(i).x = iPointOfOrigin--; //
> and bottom screen value for Y-axis
> mPoints[0].at(i).y = 0;
> }
> }
>
> BOOL CMyDLL::EnableTimer(BOOL tmrstate)
> {
> //
> ***************************************************************************
> // if enabled it's a realtime task manager, otherwise it'd be
> a graphical
> // representation of values passed from external source
> //
> ***************************************************************************
>
> if (tmrstate)
> {
> iTimerVal = SetTimer(IDT_TIMER_0, nElapse, 0);
> if (iTimerVal == 0)
> {
> MessageBox(_T("Unable to obtain timer!"),
> _T("IDT_TIMER_0"), MB_OK|MB_ICONSTOP|MB_SYSTEMMODAL);
> return FALSE;
> }
> }
> else
> {
> if (iTimerVal > 0)
> {
> if (!KillTimer(IDT_TIMER_0))
> {
> // message
> MessageBox(_T("Unable to stop
> timer!"), _T("IDT_TIMER_0"), MB_OK|MB_ICONSTOP|MB_SYSTEMMODAL);
> return FALSE;
> }
> }
> }
>
> return TRUE;
> }
>
> void CMyDLL::AddElement(UINT nX, UINT nY, const char * description,
> double value)
> {
> // add in value format because CPoint depends from resize...
> int i;
> CRect rc;
> ::GetClientRect(this->m_hWnd, &rc);
>
> if (nY+1 > mElements.size()) // check if I am adding a new
> source
> {
> mPoints.resize(nY
> +1); // setting the array to max
> size defined
> mPoints[nY].resize(mSourceMaxSizeArray);
> int iPointOfOrigin = 0;
> for (i=mPoints[nY].size()-1;i>0;i--) //
> initializing this first source to initial values
>
> { //
> which are consecutive integer values for X-axis
> mPoints[nY].at(i).x = iPointOfOrigin++; // and
> bottom screen value for Y-axis
> mPoints[nY].at(i).y = 0;
> }
> }
>
> // Now I add element value to the source
>
> if (scrollState) {
> // SHIFT-LEFT the array of POINTs to add new value
> rotate(mPoints[nY].begin(), mPoints[nY].begin()+1,
> mPoints[nY].end());
> mPoints[nY].at(mPoints[nY].size() - 1).y = (LONG)
> ( (double)rand() / (RAND_MAX + 1) * (rc.bottom - 1) + 1 );
> int iPointOfOrigin = rc.Width();
> for
> (i=mPoints[nY].size()-1;i>0;i--)
> mPoints[nY].at(i).x = iPointOfOrigin--;
> }
>
> if (nY > mGraphElements.capacity())
> {
> GraphElement single_element;
> single_element.description = description;
> single_element.color = mGraphColors.at(nY);
> mGraphElements.push_back(single_element);
> }
>
> Invalidate();
> UpdateWindow();
> }
>
> Thanks....
> Ciao
> Luigi

I also don't see the CMemDC class. I assume it does the bitblt etc. In
order for it to work well, I would expect it to use MM_TEXT mode to do
the bitblt and calculate the proper bitmap size etc. Changing the
scaling for dc, makes this harder and doesn't accomplish anything as
it's mDC that you need to be anisotropic.

Steve
From: Joseph M. Newcomer on
See below...
On Thu, 4 Feb 2010 03:08:07 -0800 (PST), Luigino <npuleio(a)rocketmail.com> wrote:

>Hello Joe and Stephen,
>
>maybe I am a bit lost about those OnDraw and OnPaint things... so I
>modified the code to put everything only in the OnPaint so the
>behaviour about DC should be correct BUT when I set MM_ANISOTROPIC and
>new origin to bottom right in the OnPaint as you said it doesn't make
>the graphic sliding from right to left...
>
>Here I paste the full code of this test app (the CMemDC mDC(&dc) is an
>external class):
>
>In the .H file I have:
>
>#ifdef MYDLL_BUILD
>#define MYDLL_IO _declspec(dllexport) //conditional on MYLIB_BUILD
>#else
>#define MYDLL_IO _declspec(dllimport)
>#endif
>
>class MYDLL_IO CMyDLL : public CWnd
****
Seems odd to call a window class a DLL...
****
>{
> DECLARE_DYNCREATE(CMyDLL)
>
> // matrix of POINT for each element to represent
> typedef vector<POINT> m_fPoints; // array
>representing points values for each single source
> typedef vector<m_fPoints> m_Points; // array
>representing list of sources having their POINT values
>
>public:
> CMyDLL(CWnd *parent, CWnd *parentcontrol, CRect parentrect,
>int nparentID);
****
This should not be done in a constructor. Constructors can be executed before window
objects are created, and in addition, you have no particular guarantees of the order in
which a constructor is executed relative to other constructors. Therefore, it is risky to
do this in a constructor. You should define a 'Create' method to create the window
instance.
****
> virtual ~CMyDLL();
>
>public:
> void set_MinMax(int minvalue, int maxvalue, int maxsizearray);
> void AddElement(UINT nX, UINT nY, const char * description,
****
char is an obsolete data type, and should probably not be used here. Use TCHAR, or as you
have specified it, it would be LPCTSTR.
****
>double value);
>
>protected:
> BOOL scrollState;
> UINT iTimerVal;
> UINT nElapse;
> BOOL bSetDraw;
> m_Points
>mPoints; // matrix of
****
You should not name data types using m_. This naming convention is reserved for naming
variables.
****
>sources representing POINT values
> int mSourceMaxSizeArray;
****
There is no Hungarian Notation that suggests an integer variable should start with an "m"
****
>
>// Overrides
> // ClassWizard generated virtual function overrides
> //{{AFX_VIRTUAL(CMyDLL)
>protected:
> virtual void PreSubclassWindow();
> //}}AFX_VIRTUAL
>
>protected:
> BOOL EnableTimer(BOOL tmrstate);
> int OnCreate(LPCREATESTRUCT lpCreateStruct);
****
It is dangerous to use OnCreate in a window class; if the window is created as part of a
dialog, the OnCreate method is never called. This is very bad style, because you cannot
control how the client of the DLL uses the class, and therefore must not depend upon
behavior which cannot be guaranteed
****
>
>public:
> static BOOL RegisterWindowClass();
>
>protected:
> //{{AFX_MSG(CMyDLL)
> afx_msg void OnPaint();
> afx_msg BOOL OnEraseBkgnd(CDC* pDC);
> afx_msg void OnTimer(UINT TimerVal);
> afx_msg void OnWindowPosChanged(WINDOWPOS* lpwndpos);
> //}}AFX_MSG
> DECLARE_MESSAGE_MAP()
>};
>
>In the .CPP file I have:
>
>#ifdef _DEBUG
>#define new DEBUG_NEW
>#define GDI_FLUSH() GdiFlush()
>#else
>#define GDI_FLUSH()
>#endif
>
>#define CMYDLL_CLASSNAME _T("MFCMyDLLCtrlTest") // Window class
>name
>#define IDT_TIMER_0 WM_USER + 1000
****
This is a timer ID. It would not be defined in terms of WM_USER. And you should not be
using WM_USER, and if you do use expressions for #define, they must be parenthesized for
maximum robustness:
#define whatever (a + b)
is the correct notation, not
#define whatever a + b
****
>
>IMPLEMENT_DYNAMIC(CMyDLL, CWnd)
>
>CMyDLL::CMyDLL(CWnd *parent, CWnd *parentcontrol, CRect parentrect,
>int nparentID)
>{
> RegisterWindowClass();
****
This is incorrect. You want to register the class only once, and this will try to
register it every time you instantiate the class variable. Do not do this.
****
>
> scrollState = TRUE;
> gridOffset = 0;
> bSetDraw = TRUE;
> nElapse = 1000;
> iTimerVal = 0;
>
> // Initializing graph's DC...
> if( !Create(_T("MFCMyDLLCtrlTest "), NULL, WS_CHILD|
>WS_VISIBLE, parentrect, parent, nparentID) )
> {
> MessageBox(_T("Unable to create object!"),
>_T("Alert!"), MB_OK|MB_ICONSTOP|MB_SYSTEMMODAL);
****
Do not use MessageBox. Use AfxMessageBox. But more seriously, you CANNOT assume that at
constructor time that a ::MessageBox will work correctly. So get rid of it.

Also, to improve readability, ALWAYS put whitespace around binary operators!
****
> }
****
This will usually not be guaranteed to work. Because you cannot rely on constructor
behavior, you cannot know that the parent is defined! Do not do this.

Furthermore, you have erroneously used a literal string which at this instant just happens
to have the same text as CMYDLL_CLASSNAME. Why didn't you use the macro here?
****
> SetWindowPos(parentcontrol, 0, 0, 0, 0, SWP_NOSIZE|
>SWP_NOMOVE);
****
This is incorrect. You have specified that the window *follow* its parent in the tab
sequence, but that doesn't make any sense! A child window cannot *follow* its parent.

It can only have a Z-order relative to its sibling windows, which are also child windows
of the parent. You probably didn't notice this failed because it returned an error code
you didn't test for. Remove this line.

I note that this suggests that you are probably calling 'new' to create a new instance of
this class. That is probably a mistake. You should not call 'new' unless you need to
actually do dynamic allocation, but I would simply declare it as
CMyDLL wnd;
and therefore a parameterized constructor is inappropriate, and you should never assume
that 'new' is going to be called when defining an instance of a child window class.
(Generally, the only place we make this assumption is for modeless dialogs)
****
>}
>
>CMyDLL::~ CMyDLL ()
>{
> DestroyWindow();
****
Note that this is already the definition of CWnd::~CWnd, but in general this is bad
practice. For one thing, you have no guarantee that the actual window exists at the time
the destructor is called. So you should not do this.
****
>}
>
>// Register the window class if it has not already been registered.
>BOOL C CMyDLL::RegisterWindowClass()
>{
> WNDCLASS wndcls;
> HINSTANCE hInst = AfxGetInstanceHandle();
****
There is no reason to declare a separate variable for this purpose; move the call to the
assignment of wndcls.hInstance
****
>
> if (!(::GetClassInfo(hInst, CMYDLL_CLASSNAME, &wndcls)))
> {
> // otherwise we need to register a new class
> wndcls.style = CS_DBLCLKS | CS_HREDRAW |
>CS_VREDRAW;
> wndcls.lpfnWndProc = ::DefWindowProc;
> wndcls.cbClsExtra = wndcls.cbWndExtra = 0;
> wndcls.hInstance = hInst;
> wndcls.hIcon = NULL;
> wndcls.hCursor = AfxGetApp()-
>>LoadStandardCursor(IDC_ARROW);
> wndcls.hbrBackground = (HBRUSH) (COLOR_3DFACE + 1);
> wndcls.lpszMenuName = NULL;
> wndcls.lpszClassName = CACHART_CLASSNAME;
>
> if (!AfxRegisterClass(&wndcls))
> {
> AfxThrowResourceException();
****
If you ever declare a second instance of the class, this will throw an exception.
Therefore, this code is erroneous in the context you use it. You must arrange that this
can be called exactly once, no matter how many instances of the object you create.
Overall, except for the AfxThrowResourceException, there's nothing wrong with this code,
except you don't handle its call correctly.
****
> return FALSE;
****
This doesn't actually make sense, because the AfxThrowResourceException doesn't return.
You should probably not throw the exception in this function, but throw it only in a
context that calls it and needs to do a throw.
****
> }
> }
>
> return TRUE;
>}
>
>BEGIN_MESSAGE_MAP(CMyDLL, CWnd)
> //{{AFX_MSG_MAP(CMyDLL)
> ON_WM_PAINT()
> ON_WM_CREATE()
****
This should be removed. No matter what you do, you should NEVER assume that OnCreate can
be called for a child window.
****
> ON_WM_ERASEBKGND()
> ON_WM_SIZE()
> ON_WM_WINDOWPOSCHANGED()
> ON_WM_TIMER()
> ON_MESSAGE(WM_ENTERSIZEMOVE, OnEnterSizeMove)
> ON_MESSAGE(WM_EXITSIZEMOVE, OnExitSizeMove)
> //}}AFX_MSG_MAP
>END_MESSAGE_MAP()
>
>int CMyDLL::OnCreate(LPCREATESTRUCT lpCreateStruct)
>{
> int ret = CWnd::OnCreate(lpCreateStruct);
>
> EnableTimer(scrollState);
****
This should not be done here. You need to define a Create method and do it there.
Otherwise, this function should be completely eliminated.
****
>
> return ret;
>}
>
>//{{AFX_MSG(CMyDLL) - message handlers
>void CMyDLL::OnPaint()
>{
> CPaintDC dc(this); // device context for painting
>
> CRect rect;
> GetClientRect(&rect);
>
> int save = dc.SaveDC();
>
> //dc.FillRect(rect,
>CBrush::FromHandle((HBRUSH)GetStockObject(BLACK_BRUSH)));
>
> dc.SetBkColor(RGB(0,0,0));
> dc.SetMapMode(MM_ANISOTROPIC);
> dc.SetWindowOrg(rect.BottomRight());
> dc.SetViewportOrg(0, 0);
> dc.SetViewportExt(-1, -1);
> dc.SetWindowExt(1, 1);
****
As pointed out already, you want to draw in the memory DC, but you change the parameters
of the DC for the window. So all your drawing is done with the default settings for the
memory DC, and these calls do absolutely nothing whatsoever.
****
>
> if(bSetDraw)
> {
> CMemDC mDC(&dc);
>
> // ********** Background ***********
> // Grid
> if (bActivateGrid)
> {
> CPen qLinePen(PS_SOLID, 1, RGB(0,139,0));
> mDC->SelectObject(&qLinePen);
>
> // Grid - Horizontal lines
> mDC->MoveTo(1, 0);
> mDC->LineTo(rect.Width(), 0);
> int height = rect.Height();
> int maxlines = height / (int)12.5;
****
12.5 sounds like a magic number. Why is it not a #define or a const double?
****
> for (int i=1;i<=maxlines;i++){
> int y_axis = (int)((double)i * 12.5);
> if (y_axis <= rect.Height()) {
> mDC->MoveTo(1, y_axis);
> mDC->LineTo(rect.Width(),
>y_axis);
> }
> }
>
> // Grid - Vertical lines
> mDC->MoveTo(0, 0);
> mDC->LineTo(0, rect.Height());
> int width = rect.Width();
> maxlines = width / (int)12.5;
> for (int i=1;i<=maxlines;i++){
> int x_axis = (int)(((double)i * 12.5)
>- gridOffset);
> if (x_axis <= rect.Width()) {
> mDC->MoveTo(x_axis, 1);
> mDC->LineTo(x_axis,
>rect.Height());
> }
> }
>
> qLinePen.DeleteObject();
> }
> // *********** graphic component ***********
> // based on choice of graph type
> CPen qPolylinePen(PS_SOLID, 1, RGB(0, 255, 0));
> switch (iGraphType)
> {
> case GRAPH_BARS:
> break;
>
> case GRAPH_LINES:
> {
> if (mPoints.capacity() == 1)
> {
> mDC-
>>SelectObject(qPolylinePen);
> m_fPoints* pointsline =
>&mPoints[0];
> mDC->Polyline(&(*pointsline)
>[0], (int)pointsline->size());
****
This is a bizarre way to accomplish what is a simple task. It is extremely confusing
because you have used a variable name (m_fPoints) as if it were a type. Oh, sorry, you
named a type to look like a variable, then used that type name. See how confusing it is
when you don't use the naming conventions correctly? Either use them correctly, or don't
use them at all. But using them incorrectly is fatal.
mDC->PolyLine(&mPoints[0], (int)pointsline->size());
No need to introduce a bizarre intermediate variable which you then dereference again!
****
> //mDC->PolyPolyline()
> qPolylinePen.DeleteObject();
****
This is erroneous, and does nothing. THe reason is that DeleteObject will do absolutely
nothing to an object which is selected into a DC, and you have not deselected it.
Furthermore, you don't *need* to delete it, because it will be deleted when the variable
goes out of scope. It is your responsibility to make sure that the destructor CPen::~CPen
cannot be called before the pen is deselected. I would suggest moving the declaration of
the pen to before the SaveDC so it cannot be deleted accidentally. Otherwise, you have a
serious resource leak here; the DeleteObject call calls the kernel API ::DeleteObject,
then sets the m_hPen to NULL even though the object was not deleted
****
> }
> }
> break;
>
> default:
> break;
> }
> GDI_FLUSH();
> }
>
> dc.RestoreDC(save);
>}
>
>BOOL CMyDLL::OnEraseBkgnd(CDC* pDC)
>{
> return FALSE;
>}
>
>void CMyDLL::OnTimer(UINT TimerVal)
>{
> // ****** processing event ******
> if (TimerVal == IDT_TIMER_0)
> {
> gridOffset++;
> if (gridOffset > 12.5)
****
Here's that mystical 12.5 appearing again. Use a symbolic name
****
> gridOffset = 0.0;
>
> Invalidate(); // to call OnDraw()/OnPaint()
> UpdateWindow();
> }
> // call base class handler
> CWnd::OnTimer(TimerVal);
>}
>
>void CMyDLL::OnWindowPosChanged(WINDOWPOS* lpwndpos)
>{
> CWnd::OnWindowPosChanged(lpwndpos);
>}
****
Since this function does nothing, eliminate it.
****
>
>void CMyDLL::PreSubclassWindow()
>{
> // TODO: Add your specialized code here and/or call the base class
> // In our case this is not needed - yet - so just drop through to
> // the base class
>
> // Get Size of Display area
> CWnd::PreSubclassWindow();
>}
>
>void CMyDLL::set_MinMax(int minvalue, int maxvalue, int maxsizearray)
>{
> iMinRange = minvalue;
> iMaxRange = maxvalue;
> mSourceMaxSizeArray = maxsizearray; //
>getting max size array of each source which has to be fixed
> mPoints.resize(1);
> mPoints[0].resize(mSourceMaxSizeArray);
****
Note again the naming failure; the variable should be called m_Points, and its type should
have a name that does not include m_ in the prefix.
****
>
> CRect rc;
> GetClientRect(&rc);
> int iPointOfOrigin = rc.Width();
> for (int i=mPoints[0].size()-1;i>0;i--) //
>initializing this first source to initial values
>
>{ //
>which are consecutive integer values for X-axis
> mPoints[0].at(i).x = iPointOfOrigin--; //
>and bottom screen value for Y-axis
> mPoints[0].at(i).y = 0;
> }
>}
>
>BOOL CMyDLL::EnableTimer(BOOL tmrstate)
>{
> //
>***************************************************************************
> // if enabled it's a realtime task manager, otherwise it'd be
>a graphical
> // representation of values passed from external source
> //
>***************************************************************************
>
> if (tmrstate)
> {
> iTimerVal = SetTimer(IDT_TIMER_0, nElapse, 0);
> if (iTimerVal == 0)
> {
> MessageBox(_T("Unable to obtain timer!"),
>_T("IDT_TIMER_0"), MB_OK|MB_ICONSTOP|MB_SYSTEMMODAL);
> return FALSE;
> }
> }
> else
> {
> if (iTimerVal > 0)
> {
> if (!KillTimer(IDT_TIMER_0))
> {
> // message
> MessageBox(_T("Unable to stop
>timer!"), _T("IDT_TIMER_0"), MB_OK|MB_ICONSTOP|MB_SYSTEMMODAL);
> return FALSE;
> }
> }
> }
>
> return TRUE;
>}
>
>void CMyDLL::AddElement(UINT nX, UINT nY, const char * description,
****
Not clear why you are using the obsolete 'char' data type. Use LPCTSTR.
****
>double value)
>{
> // add in value format because CPoint depends from resize...
> int i;
****
Since this variable is only used in the context of a for-loop iterator, remove this
declaration.
****
> CRect rc;
> ::GetClientRect(this->m_hWnd, &rc);
****
This is completely silly. Just write
GetClientRect(&rc);
everything else is distracting noise.
****
>
> if (nY+1 > mElements.size()) // check if I am adding a new
>source
> {
> mPoints.resize(nY
>+1); // setting the array to max
>size defined
> mPoints[nY].resize(mSourceMaxSizeArray);
> int iPointOfOrigin = 0;
> for (i=mPoints[nY].size()-1;i>0;i--) //
>initializing this first source to initial values
>
>{ //
>which are consecutive integer values for X-axis
> mPoints[nY].at(i).x = iPointOfOrigin++; // and
>bottom screen value for Y-axis
> mPoints[nY].at(i).y = 0;
> }
> }
>
> // Now I add element value to the source
>
> if (scrollState) {
> // SHIFT-LEFT the array of POINTs to add new value
> rotate(mPoints[nY].begin(), mPoints[nY].begin()+1,
>mPoints[nY].end());
> mPoints[nY].at(mPoints[nY].size() - 1).y = (LONG)
>( (double)rand() / (RAND_MAX + 1) * (rc.bottom - 1) + 1 );
> int iPointOfOrigin = rc.Width();
> for
>(i=mPoints[nY].size()-1;i>0;i--)
> mPoints[nY].at(i).x = iPointOfOrigin--;
> }
****
for(int i = mPoints[nY].size() - 1; i > 0; i--)

Since i is not needed outside this loop, there is no reason to declare it at the block
entry. Note that adding whitespace makes the code actually readable.
****
>
> if (nY > mGraphElements.capacity())
> {
> GraphElement single_element;
> single_element.description = description;
> single_element.color = mGraphColors.at(nY);
> mGraphElements.push_back(single_element);
> }
>
> Invalidate();
> UpdateWindow();
>}
>
>Thanks....
>Ciao
>Luigi
Joseph M. Newcomer [MVP]
email: newcomer(a)flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
From: Luigino on
Hello again Joe and Stephen,

I've corrected whatever you suggested to, Joe, made a Create() method
to initialize, removed the OnCreate and the rest of stuffs...

Stephen, I modified also the OnPaint() thing as you said as you can
see below:

void CCaChart::OnPaint()
{
CPaintDC dc(this); // device context for painting
dc.SetBkColor(RGB(0,0,0));

CRect rect;
GetClientRect(&rect);

int save = dc.SaveDC();

if(bSetDraw)
{
CMemDC mDC(&dc);

//mDC.SetBkColor(RGB(0,0,0));
mDC.SetMapMode(MM_ANISOTROPIC);
mDC.SetWindowOrg(rect.BottomRight());
mDC.SetViewportOrg(0, 0);
mDC.SetViewportExt(-1, -1);
//mDC.SetViewportExt(-rect.right, -rect.bottom);
mDC.SetWindowExt(1, 1);
//mDC.SetWindowExt(rect.Width(), rect.Height());
// ********** Background ***********
// Grid
if (bActivateGrid)
{
CPen qLinePen(PS_SOLID, 1, RGB(0,139,0));
mDC.SelectObject(&qLinePen);

// Grid - Horizontal lines
mDC.MoveTo(1, 0);
mDC.LineTo(rect.Width(), 0);
int height = rect.Height();
int maxlines = height / (int)12.5;
for (int i=1;i<=maxlines;i++){
int y_axis = (int)((double)i * 12.5);
if (y_axis <= rect.Height()) {
mDC.MoveTo(1, y_axis);
mDC.LineTo(rect.Width(),
y_axis);
}
}

// Grid - Vertical lines
mDC.MoveTo(0, 0);
mDC.LineTo(0, rect.Height());
int width = rect.Width();
maxlines = width / (int)12.5;
for (int i=1;i<=maxlines;i++){
int x_axis = (int)(((double)i * 12.5)
- gridOffset);
if (x_axis <= rect.Width()) {
mDC.MoveTo(x_axis, 1);
mDC.LineTo(x_axis,
rect.Height());
}
}

qLinePen.DeleteObject();
}
// *********** graphic component ***********
// based on choice of graph type
CPen qPolylinePen(PS_SOLID, 1, RGB(0, 255, 0));
switch (iGraphType)
{
case GRAPH_BARS:
break;

case GRAPH_LINES:
{
if (vtPoints.capacity() == 1)
{

mDC.SelectObject(qPolylinePen);
vectfPoints* pointsline =
&vtPoints[0];
mDC.Polyline(&(*pointsline)
[0], (int)pointsline->size());
//mDC->PolyPolyline()
qPolylinePen.DeleteObject();
}
}
break;

default:
break;
}
GDI_FLUSH();
}

dc.RestoreDC(save);
}

but when I resize window the vertical and horizontal lines are
repainted to the whole canvas but the Polyline stuff isn't rescaled; I
mean I have values in the vtPoints array which MM_ANISOTROPIC should
rescale automatically due having set SetViewportExt and SetWindowExt
respectively to -1, -1 and 1, 1 so I guess it should scale 1:1 and the
polyline graphic should be adjusted when resizing window....but it
doesn't... what I could have missed or made wrong in those
SetViewportExt and SetWindowExt?...

Thanks
Ciao
Luigi
From: Joseph M. Newcomer on
See below...
On Fri, 5 Feb 2010 03:57:31 -0800 (PST), Luigino <npuleio(a)rocketmail.com> wrote:

>Hello again Joe and Stephen,
>
>I've corrected whatever you suggested to, Joe, made a Create() method
>to initialize, removed the OnCreate and the rest of stuffs...
>
>Stephen, I modified also the OnPaint() thing as you said as you can
>see below:
>
>void CCaChart::OnPaint()
>{
> CPaintDC dc(this); // device context for painting
> dc.SetBkColor(RGB(0,0,0));
****
Setting the background color in a DC you are not using except for a BitBlt is not useful
****
>
> CRect rect;
> GetClientRect(&rect);
>
> int save = dc.SaveDC();
>
> if(bSetDraw)
> {
> CMemDC mDC(&dc);
>
> //mDC.SetBkColor(RGB(0,0,0));
> mDC.SetMapMode(MM_ANISOTROPIC);
> mDC.SetWindowOrg(rect.BottomRight());
> mDC.SetViewportOrg(0, 0);
> mDC.SetViewportExt(-1, -1);
> //mDC.SetViewportExt(-rect.right, -rect.bottom);
> mDC.SetWindowExt(1, 1);
> //mDC.SetWindowExt(rect.Width(), rect.Height());
> // ********** Background ***********
> // Grid
> if (bActivateGrid)
> {
****
int msave = mDC.SaveDC();

Required because you cannot use DeleteObject as you are doing!
****
> CPen qLinePen(PS_SOLID, 1, RGB(0,139,0));
> mDC.SelectObject(&qLinePen);
>
> // Grid - Horizontal lines
> mDC.MoveTo(1, 0);
> mDC.LineTo(rect.Width(), 0);
> int height = rect.Height();
> int maxlines = height / (int)12.5;
> for (int i=1;i<=maxlines;i++){
> int y_axis = (int)((double)i * 12.5);
> if (y_axis <= rect.Height()) {
> mDC.MoveTo(1, y_axis);
> mDC.LineTo(rect.Width(),
>y_axis);
> }
> }
>
> // Grid - Vertical lines
> mDC.MoveTo(0, 0);
> mDC.LineTo(0, rect.Height());
> int width = rect.Width();
> maxlines = width / (int)12.5;
> for (int i=1;i<=maxlines;i++){
> int x_axis = (int)(((double)i * 12.5)
>- gridOffset);
> if (x_axis <= rect.Width()) {
> mDC.MoveTo(x_axis, 1);
> mDC.LineTo(x_axis,
>rect.Height());
> }
> }
>
> qLinePen.DeleteObject();
****
This still generates a GDI resource leak. Remove it. Permanently.

Note that if you remove it without moving the declaration, then when
CGdiObject::~CGdiObject (the superclass of CPen) deletes the object when it goes out of
scope, it again loses the object, because you cannot successfully delete an object when it
is selected into the DC.

Instead, you should, at this point, write
mDC.RestoreDC(msave);
then you can expect something that will work.
****
> }
****
You have an odd construct here. It is the equivalent of
{
int q;
}
int q;
The fact that it is a CPen qPolylinePen doesn't change the oddity.
You should nest this in another scope, and you should save the DC state again, so add
{
int msave = dc.SaveDC();
and see below
****
> // *********** graphic component ***********
> // based on choice of graph type
> CPen qPolylinePen(PS_SOLID, 1, RGB(0, 255, 0));
> switch (iGraphType)
> {
> case GRAPH_BARS:
> break;
>
> case GRAPH_LINES:
> {
> if (vtPoints.capacity() == 1)
> {
>
>mDC.SelectObject(qPolylinePen);
> vectfPoints* pointsline =
>&vtPoints[0];
> mDC.Polyline(&(*pointsline)
>[0], (int)pointsline->size());
****
Still unbelievably complex way to say something trivial. Why do you apply the & operator
to a dereference? If you just said
mDC.Polyline(pointsline, (int)pointsline->size());
it would have the same effect.
****
> //mDC->PolyPolyline()
> qPolylinePen.DeleteObject();
****
Likewise. Do not call DeleteObject unless you understand the implications. Remove this
line. If the program doesn't work correctly without this, then recode so it is correct.
But you need to add the RestoreDC and close brace, below
****

> }
> }
> break;
>
> default:
> break;
> }
> GDI_FLUSH();
****
mDC.RestoreDC(msave);
}

It is hard to match braces that don't have comments, so I think this is the place to do
it.
****
> }
>
> dc.RestoreDC(save);
>}
>
>but when I resize window the vertical and horizontal lines are
>repainted to the whole canvas but the Polyline stuff isn't rescaled; I
>mean I have values in the vtPoints array which MM_ANISOTROPIC should
>rescale automatically due having set SetViewportExt and SetWindowExt
>respectively to -1, -1 and 1, 1 so I guess it should scale 1:1 and the
>polyline graphic should be adjusted when resizing window....but it
>doesn't... what I could have missed or made wrong in those
>SetViewportExt and SetWindowExt?...
****
How do you mean "not rescaled"? How do you know that? Perhaps because the lines don't
appear? If so, that is not surprising, since I don't see any place at which the bitmap in
the memDC is transferred to the dc. Also, I don't see where you are actually setting the
scale, since the points you are recording cannot be prescaled; they have to be "raw" data,
since by the time they are displayed, the size of the window in which they are displayed
can change.
joe

****
>
>Thanks
>Ciao
>Luigi
Joseph M. Newcomer [MVP]
email: newcomer(a)flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm