Re: PolyLine and autoscrolling canvas
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