From: Luigino on 4 Feb 2010 06:09 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 4 Feb 2010 11:54 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 4 Feb 2010 14:52 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 5 Feb 2010 06:57 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 5 Feb 2010 09:50
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 |