Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

MFC Custom Control in a MFC DLL

1,025 views
Skip to first unread message

Luigino

unread,
Oct 14, 2009, 5:00:07 AM10/14/09
to
Hello everyone!!

I'm going to make a custom control which can be loaded on Toolbox to
add in a MFC Application as a component. Before I tried creating a MFC
ActiveX Control but it's an OCX and plus it uses COM.
So I'm trying to create a regular DLL (which could have more than one
control...) and I started with creating a new MFC DLL choosing Use MFC
in a Shared DLL.
Then I added a MFC Class where at the start I want to show a rectangle
that redraws every time I resize the control on a resource window in a
MFC application.

I'd like to know how I can initialize the MFC DLL in the main .CPP
file of the DLL in the way I can load the DLL in the Toolbox and have
a custom control to apply in a MFC application? Or maybe I shall
change the CWnd base in my class with, for example, CDialog?

Thanks to everyone in advance for the suggests..... :-)

Ciao
Luigi

Here's the code of custom control's class:

//
// CaChart.cpp : implementation file
//

#include "stdafx.h"
#include "CACharts.h"
#include "CaChart.h"

// CCaChart

IMPLEMENT_DYNAMIC(CCaChart, CWnd)

CCaChart::CCaChart()
: iGraphType(GRAPH_BARS)
{
RegisterWindowClass();
}

CCaChart::~CCaChart()
{
}

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

if (!(::GetClassInfo(hInst, CACHART_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(CCaChart, CWnd)
//{{AFX_MSG_MAP(CCaChart)
ON_WM_PAINT()
ON_WM_ERASEBKGND()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

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

// Create memory DC
CDC MemDC;
if (!MemDC.CreateCompatibleDC(&dc))
return;

CRect rect;
GetClientRect(rect);

MemDC.FillRect(rect, CBrush::FromHandle((HBRUSH)GetStockObject
(BLACK_BRUSH)));
MemDC.Ellipse(rect);
}

BOOL CCaChart::OnEraseBkgnd(CDC* pDC)
{
return CWnd::OnEraseBkgnd(pDC);
}

void CCaChart::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();
}

BOOL CCaChart::Create(CWnd* pParentWnd, const RECT& rect, UINT nID,
DWORD dwStyle /*=WS_VISIBLE*/)
{
return CWnd::Create(CACHART_CLASSNAME, _T(""), dwStyle, rect,
pParentWnd, nID);
}

void CCaChart::set_GraphType(GRAPH_TYPE graphtype)
{
iGraphType = graphtype;
}

GRAPH_TYPE CCaChart::get_GraphType()
{
return (GRAPH_TYPE)iGraphType;
}


Joseph M. Newcomer

unread,
Oct 14, 2009, 2:39:18 PM10/14/09
to
If you create a custom control in a DLL, you cannot make it something that goes into the
ToolBox in VS. You can use it by creating a user-defined resource (a blank rectangle on
the dialog template) and assigning it the class name of your control and setting the style
bits manually. But if you want a control you can place in the toolbox, it has to be an
OCX and use COM.

Question: what's the Big Deal with an OCX control that uses COM? Given you've already
done this work, stripping it back to be a pure DLL doesn't seem to be a productive thing
to do.

I usually create a CStatic and give it an ID, then at OnInitialUpdate/OnInitDialog I will
create the control with the same ID in the same place and destroy the old control.

For example, if I have a CStatic called IDC_MYCONTROL, then I will create a control
variable for it, c_MyControlPlace, and add to my class definition

CMyControl c_MyControl;

In the OnInitialUpdate/OnInitDialog handler, I will do

CRect r;
c_MyControlPlace.GetWindowRect(&r);
ScreenToClient(&r);

c_MyControl.Create(r, c_MyControlPlace);

c_MyControlPlace.DestroyWindow();

where I have defined

BOOL CMyControl::Create(const CRect & r, CWnd wnd)
{
if(!CWnd::Create(..., &r, ... wnd.GetDlgCtrlId()))
return FALSE;
SetWindowPos(&wnd, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
return TRUE;
}

(I didn't take the time to look up all the parameters, so I've just used ... here and the
rest is left as an Exercise For The Reader)

Note that this is clumsier than using an ActiveX control.
joe

Joseph M. Newcomer [MVP]
email: newc...@flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm

Luigino

unread,
Oct 16, 2009, 2:29:53 AM10/16/09
to
On 14 Ott, 20:39, Joseph M. Newcomer <newco...@flounder.com> wrote:

OK now I finalized to create a Toolbox component, it has to be an OCX
Activex COM...

> I usually create a CStatic and give it an ID, then at OnInitialUpdate/OnInitDialog I will
> create the control with the same ID in the same place and destroy the old control.
>
> For example, if I have a CStatic called IDC_MYCONTROL, then I will create a control
> variable for it, c_MyControlPlace, and add to my class definition
>
> CMyControl  c_MyControl;
>
> In the OnInitialUpdate/OnInitDialog handler, I will do
>
> CRect r;
> c_MyControlPlace.GetWindowRect(&r);
> ScreenToClient(&r);
>
> c_MyControl.Create(r, c_MyControlPlace);
>
> c_MyControlPlace.DestroyWindow();
>
> where I have defined
>
> BOOL CMyControl::Create(const CRect & r, CWnd wnd)
>     {
>      if(!CWnd::Create(..., &r, ... wnd.GetDlgCtrlId()))
>         return FALSE;
>      SetWindowPos(&wnd, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
>      return TRUE;
>     }
>

I tried your approach to test its behavior and I compiled the DLL then
added the path + .lib in a test project's properties but when I call
Create(...) function, which is declared as public static, I get an
unresolved reference external error.... why?....
I say before this error I got same error also on constructor and
destructor which I had to declare

CMyControl c_MyControl;

as

CMyControl* c_MyControl;

any suggest?

Thanks
Ciao
Luigi

Luigino

unread,
Oct 16, 2009, 3:59:16 AM10/16/09
to
I tried this too:

CStatic* c_CStaticPlaced = (CStatic *)GetDlgItem(IDC_MYSTATIC);
CRect r;
c_CStaticPlaced->GetWindowRect(&r);
ScreenToClient(&r);
CCaChart* c_MyControl = new CCaChart();
CWnd* pWnd = c_CStaticPlaced->GetDlgItem(c_CStaticPlaced->GetDlgCtrlID
());
c_MyControl->Create(r, pWnd);
c_CStaticPlaced->DestroyWindow();

but I get this error:

1>test_chartDlg.obj : error LNK2019: unresolved external symbol
"public: __thiscall CCaChart::CCaChart(void)" (??0CCaChart@@QAE@XZ)
referenced in function "protected: virtual int __thiscall
Ctest_chartDlg::OnInitDialog(void)" (?
OnInitDialog@Ctest_chartDlg@@MAEHXZ)

but the constructor CaChart isn't declared as virtual in the header's
class, plus it's public...

Any suggest?...
Thanks
Ciao
Luigi

Ismo Salonen

unread,
Oct 16, 2009, 6:33:52 AM10/16/09
to

This is linker error message. The real reason for the error message is
that the linker does not see the implementation. Either you do not have
the implementation for the CCaChart:.CCaChart(void) constructor of you
are not including relevant object file on command line.

ismo


ismo

Luigino

unread,
Oct 16, 2009, 8:19:05 AM10/16/09
to
> This is linker error message. The real reason for the error message is
> that the linker does not see the implementation. Either you do not have
> the implementation for the CCaChart:.CCaChart(void) constructor of you
> are not including relevant object file on command line.

HI ismo,

as I said the VS DLL ClassWizard added the constructor and destructor
which are:

.CPP

CCaChart::CCaChart()
{
iGraphType = GRAPH_BARS;
RegisterWindowClass();
}

CCaChart::~CCaChart()
{
}

.H

class CCaChart : public CWnd
{
DECLARE_DYNAMIC(CCaChart)

public:
CCaChart();

public:
virtual ~CCaChart();
.
.
.
(plus other functions...)
}

and as I said before, I added in test application's properties the
path and the name of the lib file...
even I also added in the test application file the include to the
header...
So in fact it's weird to get this error... that's why I'm here asking
you...

Thanks
Luigi

Joseph M. Newcomer

unread,
Oct 16, 2009, 9:47:48 AM10/16/09
to
See below...

On Fri, 16 Oct 2009 00:59:16 -0700 (PDT), Luigino <npu...@rocketmail.com> wrote:

>I tried this too:
>
>CStatic* c_CStaticPlaced = (CStatic *)GetDlgItem(IDC_MYSTATIC);

****
It is completely silly to use GetDlgItem in this case; you should create a control
variable!
****


>CRect r;
>c_CStaticPlaced->GetWindowRect(&r);
>ScreenToClient(&r);
>CCaChart* c_MyControl = new CCaChart();

****
Why do you new a 'new' here? This is silly. You can simply declare a CCaChart variable
in your class definition! There is ABSOLUTELY no reason to do a 'new'.
****


>CWnd* pWnd = c_CStaticPlaced->GetDlgItem(c_CStaticPlaced->GetDlgCtrlID
>());
>c_MyControl->Create(r, pWnd);
>c_CStaticPlaced->DestroyWindow();
>
>but I get this error:
>
>1>test_chartDlg.obj : error LNK2019: unresolved external symbol
>"public: __thiscall CCaChart::CCaChart(void)" (??0CCaChart@@QAE@XZ)
>referenced in function "protected: virtual int __thiscall
>Ctest_chartDlg::OnInitDialog(void)" (?
>OnInitDialog@Ctest_chartDlg@@MAEHXZ)

****
OnInitDialog is always virtual, so I see no issue here; I see no reference to CaChart as
virtual; what you probably failed to do was include the .lib file for your DLL, which is a
common mistake.
****


>
>but the constructor CaChart isn't declared as virtual in the header's
>class, plus it's public...
>
>Any suggest?...
>Thanks
>Ciao
>Luigi

Joseph M. Newcomer

unread,
Oct 16, 2009, 9:49:35 AM10/16/09
to
So what? VS adding a declaration does not make that declaration visible to the client of
the DLL if you fail to include the .lib file in the linker command line. You are
confusing the C++ compiler with the linker here. They are different, and what you do in
C++ has no implicit effect on the linker. Go to the Linker, Inputs, and add your .lib
file to the additional libraries line.
joe

Luigino

unread,
Oct 16, 2009, 11:42:21 AM10/16/09
to

> confusing the C++ compiler with the linker here. They are different, and what you do in
> C++ has no implicit effect on the linker. Go to the Linker, Inputs, and add your .lib
> file to the additional libraries line.

Ehm I did... I put under Linker-General in Additional Library
Directories the path of that Debug directory where the .lib file is
created once compiled the DLL and I added under Linker-Input, in
Additional Dependencies that CACharts.lib.....
So I also created now a control variable (from the class add variable
wizard) for this and implemented the code:

CRect rect;
c_MyStaticControl.GetWindowRect(&rect);
ScreenToClient(&rect);
CCaChart c_MyControl;
c_MyControl.Create(rect, GetDlgItem
(c_MyStaticControl.GetDlgCtrlID()));
c_MyStaticControl.DestroyWindow();

but I get these linker errors:

1>test_chartDlg.obj : error LNK2019: unresolved external symbol

"public: virtual __thiscall CCaChart::~CCaChart(void)" (??
1CCaChart@@UAE@XZ) referenced in function "protected: virtual int


__thiscall Ctest_chartDlg::OnInitDialog(void)" (?
OnInitDialog@Ctest_chartDlg@@MAEHXZ)

1>test_chartDlg.obj : error LNK2019: unresolved external symbol

"public: virtual int __thiscall CCaChart::Create(class CRect const
&,class CWnd *)" (?Create@CCaChart@@UAEHABVCRect@@PAVCWnd@@@Z)


referenced in function "protected: virtual int __thiscall
Ctest_chartDlg::OnInitDialog(void)" (?
OnInitDialog@Ctest_chartDlg@@MAEHXZ)
1>test_chartDlg.obj : error LNK2019: unresolved external symbol
"public: __thiscall CCaChart::CCaChart(void)" (??0CCaChart@@QAE@XZ)
referenced in function "protected: virtual int __thiscall
Ctest_chartDlg::OnInitDialog(void)" (?
OnInitDialog@Ctest_chartDlg@@MAEHXZ)

So, suspecting a possible issue of my VS2008, I tried to create from
scratch with class wizards a simple stupid DLL without adding or
modifying anything and trying to create a Test file from Visual Studio
IDE itself and when it asks me to choose the DLL project to test, it
returns "Unknown address 0"... then I try to create a test project for
the example, adding the .lib and I get same linker error I pasted
before...

Stephen Myers

unread,
Oct 16, 2009, 1:15:52 PM10/16/09
to Luigino
I thought you needed to export the symbols in the library.

In CaChart.h

Something like

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

class MYDLL_IO CCaChart: public ...

Then add MYLIB_BUILD to the library project
Configuration Properties
C/C++
Preprocessor
Preprocessor Defines

Steve

Joseph M. Newcomer

unread,
Oct 16, 2009, 4:27:16 PM10/16/09
to
I assumed the class had the appropriate AFX_EXT_CLASS or other suitable macro definition;
this should be added automatically when a project for an extension DLL is created. My
error. Of course, if the all-important source code had been included in the post, I might
have noticied this omission (but hey, why make it easy for the guys you are asking the
question of...?)
joe

Luigino

unread,
Oct 19, 2009, 8:56:27 AM10/19/09
to
> email: newco...@flounder.com

In fact now it works.... just a thing that I'm trying to draw a filled
ellipse (where in the place I'll code the real component) and I
implemented an OnPaint, since I guess right after calling Create
function it should paint the DC on the parent but at debug it doesn't
fire...
I coded the OnPaint like this:

in the .h

afx_msg void OnPaint();

in the .cpp

added ON_WM_PAINT() in the MESSAGE_MAP routine

void MyDLL::OnPaint()
{
CPaintDC dc(this);

CDC MemDC;
if (!MemDC.CreateCompatibleDC(&dc))
return;

CRect rect;
GetClientRect(rect); // because it is a client


MemDC.FillRect(rect, CBrush::FromHandle((HBRUSH)
GetStockObject(BLACK_BRUSH)));
MemDC.Ellipse(rect);
}

I tried also to move the creation thing to the test app in the way to
have an OnCreate(LPCREATESTRUCT lpCreateStruct) with same code of
OnPaint in the way to apply the drawing of the ellipse right after
creation of the DC and during Create(...) call it doesn't even fire
OnCreate(...) event to draw the ellipse, plus at SetWindowPos
execution I get an assert error on this line and at debug reports
passing the WndStatic variable (got by: CWnd* WndStatic = GetDlgItem
(c_MyStaticControl.GetDlgCtrlID()); since c_MyStaticControl control
variable is CStatic type), the m_hWnd in the function has value 0....
Shall I have missed something?....

Thanks
Ciao
Luigi

Stephen Myers

unread,
Oct 19, 2009, 10:39:29 AM10/19/09
to

CWnd* WndStatic = GetDlgItem(c_MyStaticControl.GetDlgCtrlID());

is not at all the way things should be done. There is no reason to
create WndStatic. GetDlgItem should not be used in MFC. Check the
source of your code snippets. Chances are very good they are either not
written for MFC or written by someone who had not figured out what you
get with MFC.

Use c_MyStaticControl. instead of WndStatic->

This will not cirrect your problem.

I suspect that the real problem is due to accessing the control before
it is created (in OnInitDialog).

Steve

Luigino

unread,
Oct 19, 2009, 11:02:51 AM10/19/09
to
> CWnd* WndStatic = GetDlgItem(c_MyStaticControl.GetDlgCtrlID());
>
> is not at all the way things should be done. There is no reason to
> create WndStatic. GetDlgItem should not be used in MFC. Check the
> source of your code snippets. Chances are very good they are either not
> written for MFC or written by someone who had not figured out what you
> get with MFC.
>
> Use c_MyStaticControl. instead of WndStatic->
>
> This will not cirrect your problem.
>
> I suspect that the real problem is due to accessing the control before
> it is created (in OnInitDialog).
>
> Steve

HI Steve,

the fact is I created a CStatic in the resource window, and I created
a Control Variable for this CStatic so the variable created is a
CStatic type and the first parameter of SetWindowPos() which is a
const CWnd *pWndInsertAfter wouldn't cast from CStatic to const CWnd,
that's why I declared that variable.
Actually I can "create" my cwnd derived class which now fires an
OnCreate event and it executes those instructions:

CPaintDC dc(this);
CDC MemDC;

if (!MemDC.CreateCompatibleDC(&dc))
return;

CRect rect;
GetClientRect(rect); // because it is a client
MemDC.FillRect(rect, CBrush::FromHandle((HBRUSH) GetStockObject
(BLACK_BRUSH)));
MemDC.Ellipse(rect);

but when I SetWindowPos with WndCStatic it appears the window without
showing the control...

in the OnInitDialog I coded like here:

CRect rect;
c_MyStaticControl.GetWindowRect(&rect);
ScreenToClient(&rect);
CCaChart c_MyControl;

CWnd* WndStatic = GetDlgItem(c_MyStaticControl.GetDlgCtrlID());
c_MyControl.Create(_T("MFCCaChartCtrl"), _T(""), WS_CHILD|WS_VISIBLE|
WS_BORDER, rect, this, IDC_MYCONTROL);
c_MyControl.SetWindowPos(c_WndStatic, 0, 0, 0, 0, SWP_NOSIZE|
SWP_NOMOVE);
c_MyStaticControl.DestroyWindow();

from an example at begin of this post I tried to put c_MyStaticControl
in the SetWindowPost but no cast available...

David Wilkinson

unread,
Oct 19, 2009, 11:44:06 AM10/19/09
to
Luigino wrote:
> the fact is I created a CStatic in the resource window, and I created
> a Control Variable for this CStatic so the variable created is a
> CStatic type and the first parameter of SetWindowPos() which is a
> const CWnd *pWndInsertAfter wouldn't cast from CStatic to const CWnd,
> that's why I declared that variable.

There is no reason you cannot pass &c_MyStaticControl to a function expecting
const CWnd*. Perhaps you were forgetting the '&'?

When something doesn't work, try to figure out why. Don't turn around and do
something that makes no sense at all (even if it works...). The main purpose of
control variables is so you do not have to use GetDlgItem().

--
David Wilkinson
Visual C++ MVP

Stephen Myers

unread,
Oct 19, 2009, 12:04:31 PM10/19/09
to

Try

... SetWindowPos(&wndTop,...);

or

... SetWindowPos(&c_MyStaticControl,...);


Please remove CWnd* WndStatic ... ; It is similar to the following

double a;
double *b = (double *)(&a);

c = *b;

Steve

Luigino

unread,
Oct 19, 2009, 6:22:05 PM10/19/09
to
On 19 Ott, 18:04, Stephen Myers <""StephenMyers

I tried to change putting &c_MyStaticControl and I put those three
lines of code in the OnInitDialog so in the class I can have OnCreate
event that's fired and where I have those drawing lines code FillRect
and Ellipse but it didn't draw anything...
I thought it would be fired the OnPaint to draw whatever it has to be
drawn on the CWnd but that event doesn't fire so it should be in the
OnCreate but it doesn't show anything....so I don't know if I could
have missed something in the OnCreate or I should fire different event
for drawing...
Suggests?....

Joseph M. Newcomer

unread,
Oct 19, 2009, 9:22:19 PM10/19/09
to

***
This is nonsense. Why? You *already have* c_MyStaticControl so doing a GetDlgItem is a
nonsensical waste of time and effort! In any conceivable context in which you would use
WndStatic-> you can use c_MyStaticControl. and get the effect you want! So there is no
reason to have ever written this line of nonsense code!
****


>c_MyControl.Create(_T("MFCCaChartCtrl"), _T(""), WS_CHILD|WS_VISIBLE|
>WS_BORDER, rect, this, IDC_MYCONTROL);
>c_MyControl.SetWindowPos(c_WndStatic, 0, 0, 0, 0, SWP_NOSIZE|
>SWP_NOMOVE);

***
The correct first argument is &c_yStaticControl; the error is obviously a complete
misunderstanding of the C language. The first argument has to be a CWnd *. So why would
you create a variable for that purpose *when you already have a variable that serves that
purpose*???? You are not only using GetDlgItem in a context which should never occur, but
your are *replicating* information *that already exists*!
****


>c_MyStaticControl.DestroyWindow();
>
>from an example at begin of this post I tried to put c_MyStaticControl
>in the SetWindowPost but no cast available..

****
Of course not, because c_MyStaticControl is a CWnd, and SetWindowPos wants a CWnd*. It
should be screamingly obvious to anyone who understands C or C++ that the & operator does
this! I see this error all the time, and I have absolutely *no idea* why such an
elementary concept such as the address-of (&) operator is uniformly missed!
joe
****
Joseph M. Newcomer [MVP]
email: newc...@flounder.com

Joseph M. Newcomer

unread,
Oct 19, 2009, 9:28:54 PM10/19/09
to
It is (a) not clear why you want or should even consider an OnCreate handler and (b) why
you think an OnCreate handler should duplicate the code that is in OnPaint. Both of these
are probably serious design errors. I do lots of custom controls using DLLs and not once
have I ever had to either use OnCreate or put one in for the purpose of duplicating code
that correctly functions elsewhere.

Another comment below...
joe

****
Also, why are you hardwiring IDC_MYCONTROL? I create the static control with the ID I
want to use, then use c_MyStaticControl.GetDlgCtrlID() to retrieve that ID so I can use
it. Since I destroy the window, the brief period in which two controls of the same ID
exist is irrelevant, because during that time nobody accesses the control by ID.

The second parameter to Create can be NULL. Unfortunately, this is not correctly
documented.

I note that the CCaChart variable appears to be a local variable; of course, what this
means is that as soon as OnInitDialog completes, the window is destroyed. This variable
*MUST* be a variable declared in your class!

The reason you do not see the window paint is *there is no window there*. It has been
destroyed.
joe
****


>> > c_MyControl.SetWindowPos(c_WndStatic, 0, 0, 0, 0, SWP_NOSIZE|
>> > SWP_NOMOVE);
>> > c_MyStaticControl.DestroyWindow();
>>
>> > from an example at begin of this post I tried to put c_MyStaticControl
>> > in the SetWindowPost but no cast available...
>>
>> Try
>>
>> ... SetWindowPos(&wndTop,...);
>>
>> or
>>
>> ... SetWindowPos(&c_MyStaticControl,...);
>>
>> Please remove CWnd* WndStatic ... ; �It is similar to the following
>>
>> double a;
>> double *b = (double *)(&a);
>>
>> c = *b;
>>
>> Steve
>
>I tried to change putting &c_MyStaticControl and I put those three
>lines of code in the OnInitDialog so in the class I can have OnCreate
>event that's fired and where I have those drawing lines code FillRect
>and Ellipse but it didn't draw anything...
>I thought it would be fired the OnPaint to draw whatever it has to be
>drawn on the CWnd but that event doesn't fire so it should be in the
>OnCreate but it doesn't show anything....so I don't know if I could
>have missed something in the OnCreate or I should fire different event
>for drawing...
>Suggests?....

Joseph M. Newcomer [MVP]
email: newc...@flounder.com

Luigino

unread,
Oct 22, 2009, 9:01:27 AM10/22/09
to
OK it looks like now I figured out about to draw a black background
with a green grid inside an OnDraw() event.
Since I want to initialize the control inside creation method, I
supposed to start getting the DC since, as I did before in OnDraw(), I
know calling "CPaintDC dc(this);" means performing a BeginPaint and
EndPaint...
So I did:

CMemDC MemDC(this->GetDC());
CRect rect;
GetClientRect(rect);

then the rest of the code to fillrect a black rectangle as background,
selecting a pen to draw all those lines with MoveTo and LineTo....and
I commented the OnDraw() because this background has to be drawn in my
own CCreate event. Obviously I called the Create method properly of
CWnd and SetWindowPos() and the result is that it doesn't draw
anything... (no errors and in debug it executes this code
correctly)...

Maybe I shall call some instruction to force drawing on the context as
initialization?....

Thanks to everyone!!!
Ciao Luigi

Joseph M. Newcomer

unread,
Oct 22, 2009, 11:06:25 AM10/22/09
to
See below...

On Thu, 22 Oct 2009 06:01:27 -0700 (PDT), Luigino <npu...@rocketmail.com> wrote:

>OK it looks like now I figured out about to draw a black background
>with a green grid inside an OnDraw() event.
>Since I want to initialize the control inside creation method,

****
Probably a Bad Idea. You may not ever *get* a creation notification, for example, if you
were to simply give the control class and style flags at dialog editing time. If you need
to do initialization, you would do it in PreSubclassWindow
****


>I
>supposed to start getting the DC since, as I did before in OnDraw(), I
>know calling "CPaintDC dc(this);" means performing a BeginPaint and
>EndPaint...
>So I did:
>
>CMemDC MemDC(this->GetDC());
>CRect rect;
>GetClientRect(rect);

****
You are confusing drawing with "initialization". There is no reason to ever, ever do this
during creation time; the OnDraw will handle initial drawing. THere is no reason to do
GetDC(); the correct way to get a DC for a window is
CClientDC(this);
but why do you need it? Doing drawing outside the OnDraw handler makes no sense!
****


>
>then the rest of the code to fillrect a black rectangle as background,
>selecting a pen to draw all those lines with MoveTo and LineTo....and
>I commented the OnDraw() because this background has to be drawn in my
>own CCreate event. Obviously I called the Create method properly of
>CWnd and SetWindowPos() and the result is that it doesn't draw
>anything... (no errors and in debug it executes this code
>correctly)...

****
It never, ever, under any circumstances makes sense to do any drawing in your OnCreate
handler. You completely miss the point of how drawing is done. The ONLY place you would
do drawing is in your OnDraw handler. Period. If you are doing drawing in your OnCreate
(or PreSubclassWindow) handler, your design is wrong, and you have to fix it. You fix it
by removing all the drawing code from those places. Assume that if you have drawing code
in either place, your code is simply wrong.

(There are VERY RARE exceptions, such as doing rubber-banding in OnMouseMove, but they are
very rare exceptions. Any time you would think that doing drawing in OnCreate or
PreSubclassWindow is another such exception, you are guaranteed that such a choice is
wrong. And just about any other choice to draw outside OnDraw is wrong, again, with very
rare exceptions. But until you master the concept of the fact that OnDraw is where you do
the drawing, DO NOT assume you have a clue about any drawing outside this as being valid.
Instead, assume that it is not valid to draw outside OnDraw. This is the safest approach
for a beginner)
****


>
>Maybe I shall call some instruction to force drawing on the context as
>initialization?....

****
Your OnDraw handler draws the content of the window. If there is nothing to draw, it may
take some special action, but otherwise, you can assume that any drawing done outside
OnDraw (with the very rare exceptions, none of which you have here) is wrong.
joe
****


>
>Thanks to everyone!!!
>Ciao Luigi

Luigino

unread,
Oct 26, 2009, 7:06:10 AM10/26/09
to
> ****
> Probably a Bad Idea. You may not ever *get* a creation notification, for example, if you
> were to simply give the control class and style flags at dialog editing time. If you need
> to do initialization, you would do it in PreSubclassWindow
> ****>I
> You are confusing drawing with "initialization". There is no reason to ever, ever do this
> during creation time; the OnDraw will handle initial drawing. THere is no reason to do
> GetDC(); the correct way to get a DC for a window is
> CClientDC(this);
> but why do you need it? Doing drawing outside the OnDraw handler makes no sense!
> ****
> ****
> It never, ever, under any circumstances makes sense to do any drawing in your OnCreate
> handler. You completely miss the point of how drawing is done. The ONLY place you would
> do drawing is in your OnDraw handler. Period. If you are doing drawing in your OnCreate
> (or PreSubclassWindow) handler, your design is wrong, and you have to fix it. You fix it
> by removing all the drawing code from those places. Assume that if you have drawing code
> in either place, your code is simply wrong.
>
> (There are VERY RARE exceptions, such as doing rubber-banding in OnMouseMove, but they are
> very rare exceptions. Any time you would think that doing drawing in OnCreate or
> PreSubclassWindow is another such exception, you are guaranteed that such a choice is
> wrong. And just about any other choice to draw outside OnDraw is wrong, again, with very
> rare exceptions. But until you master the concept of the fact that OnDraw is where you do
> the drawing, DO NOT assume you have a clue about any drawing outside this as being valid.
> Instead, assume that it is not valid to draw outside OnDraw. This is the safest approach
> for a beginner)
> ****
> Your OnDraw handler draws the content of the window. If there is nothing to draw, it may
> take some special action, but otherwise, you can assume that any drawing done outside
> OnDraw (with the very rare exceptions, none of which you have here) is wrong.


HI Joe!!

You're right.... in fact right before I read this reply last friday I
figured, with some code tests, OnDraw is the only way to make drawing
stuffs, even at inizialization...just it will draw the initial state
and refresh it till I "send" changes to the layout...
Now I'm trying to study a class-style implementation of bars and
lines to draw on the DC, which isn't really easy since I want also to
"rotate"-left the background grid I draw... I guess I'll have to use
some incremental/decremental variables to pass to the for(...)
iterator so at every repaint it would do a sense of rotation, maybe it
is the only way...
But as you know, to draw lines and bars, the only way is to use MoveTo
and LineTo/Rectangle instructions to draw them, right?... Or are there
some functions that would draw lines and bars in function of
values?...

Thanks!!

Ciao,
Luigi

Joseph M. Newcomer

unread,
Oct 26, 2009, 9:25:55 AM10/26/09
to
See below...

****
Not sure what you mean by "rotate" left; do you mean actual rotation around an axis, so
the graph is drawn either horizontall or vertically? Please clarify
****


>But as you know, to draw lines and bars, the only way is to use MoveTo
>and LineTo/Rectangle instructions to draw them, right?... Or are there
>some functions that would draw lines and bars in function of
>values?...

****
Lines can be done with PolyLine and PolyPolyLine, among other techniques (and will go much
faster if you have a lot of line segments, such as on a continuous line graph!) Bars are
done by the Rectangle call.
joe
****
>
>Thanks!!
>
>Ciao,

Luigino

unread,
Oct 26, 2009, 11:37:22 AM10/26/09
to
HI again Joe,

> Not sure what you mean by "rotate" left; do you mean actual rotation around an axis, so
> the graph is drawn either horizontall or vertically? Please clarify

Yes I meant rotation around an axis like when looking at Process
Manager graph where it rotates, drawing new values at right side of
the graph...

> Lines can be done with PolyLine and PolyPolyLine, among other techniques (and will go much
> faster if you have a lot of line segments, such as on a continuous line graph!) Bars are
> done by the Rectangle call.

PolyLine/PolyPolyLine...ok... I'll get a study on these
instructions... thanks Joe!

Ciao
Luigi

Joseph M. Newcomer

unread,
Oct 26, 2009, 12:22:25 PM10/26/09
to
Actually, that is not "rotating", that is "sliding". So all you need to do is maintain an
origin value and redraw from the origin.

For optimization, you can use ScrollWindow to scroll the existing material to the left,
which will create a gap on the right, and the OnDraw will only actually write to the gap,
thus reducing apparent flicker.
joe

Luigino

unread,
Oct 26, 2009, 1:05:04 PM10/26/09
to

> Actually, that is not "rotating", that is "sliding". So all you need to do is maintain an
> origin value and redraw from the origin.

well yeah.... just I was thinking about "rotating" 'cause the effect
is similar...visually it is rotating to left though... ;-)

> For optimization, you can use ScrollWindow to scroll the existing material to the left,
> which will create a gap on the right, and the OnDraw will only actually write to the gap,
> thus reducing apparent flicker.

Hmmmmm.....you're right.... in fact if I draw everthing, grid and all
values of lines from an array of values, surely it would be more heavy
with a bad bad flicker I suppose...I think I'll have to remember
somewhere last point to draw next line from...
But, a question: does this ScrollWindow(...) also scroll some left
part in a hided area so if I resize window I can show that hided area
of values?... just like the task manager...

Luigi

Scott McPhillips [MVP]

unread,
Oct 26, 2009, 1:19:45 PM10/26/09
to
"Luigino" <npu...@rocketmail.com> wrote in message
news:3ee9e75e-9a89-47a6...@a37g2000prf.googlegroups.com...

> Hmmmmm.....you're right.... in fact if I draw everthing, grid and all
> values of lines from an array of values, surely it would be more heavy
> with a bad bad flicker I suppose...I think I'll have to remember
> somewhere last point to draw next line from...
> But, a question: does this ScrollWindow(...) also scroll some left
> part in a hided area so if I resize window I can show that hided area
> of values?... just like the task manager...
>
> Luigi

No, ScrollWindow just copies the visible pixels from old to new rectangle.
The pixels that are scrolled offscreen are gone. So to do what Task Manager
does you will need to save the old data, and redraw the entire picture when
the window size is changed.

--
Scott McPhillips [VC++ MVP]

Joseph M. Newcomer

unread,
Oct 26, 2009, 10:27:59 PM10/26/09
to
What I did was save an "optimized" set of drawing commands (in particular, the PolyLine
data). When I scrolled the window, it merely established what point at which I drew the
PolyLine data from. The ScrollWindow is just a graphical optimization. Note that the
PolyLine data was already pre-scaled to the window coordinates (I used MM_ANISOTROPIC) so
it was cheap to maintain, and I kept only a finite amount of scrollback. If the user went
back further than the "cache", I had to read data in from the file and recompute the range
(I always started some large delta-T before the left edge of the drawing).

So it is up to you to maintain and redraw the image at all times. For example, in my
OnDraw, I *always* did a PolyLine from T[left] for whatever the length was. But if I had
done a ScrollWindow, the first, say, three inches of the display (left-to-right) was
overdrawn, but also clipped, so GDI optimized it by not bothering to draw it at all, and
started the drawing in the invalidated region. But I never worried about the invalidated
region; I drew the whole thing. So ScrollWindow is just an optimization. If you
commented the ScrollWindow out, the whole thing should sitll work, but it just would
flicker more.
joe

On Mon, 26 Oct 2009 13:19:45 -0400, "Scott McPhillips [MVP]" <org-dot-mvps-at-scottmcp>
wrote:

Luigino

unread,
Oct 27, 2009, 11:25:23 AM10/27/09
to
HI Joe,

I studied a bit on this thing about ScrollWindow, PolyLine and the
flicker thing....and I got this point:
lemme guess using ScrollView means preventing flickering, even if I
use CMemDC which as documented it should reduce flickering effect,
when I am redesign the entire client area; indeed it should be enough
to redesign a single object in the client area without using
ScrollWindow since it's a little object with a very limited flicker
which would be like invisible to the eye... right?...

In fact with this code I implemented:

void MyDLL::OnTimer(UINT TimerVal)
{
BOOL timerstate;
// ****** stopping timer ******
timerstate = TRUE;
if (!KillTimer(iTimerVal))
{
// message
MessageBox(_T("Unable to stop timer!"), _T
("IDT_TIMER_0"), MB_OK|MB_ICONSTOP|MB_SYSTEMMODAL);
timerstate = FALSE;
}
// ****** processing event ******
gridOffset++;
if (gridOffset > 12.5)
gridOffset = 0;

Invalidate();

// ****** restart timer *******
if (timerstate)
{
iTimerVal = SetTimer(IDT_TIMER_0, 1000, 0);
}

// call base class handler
CWnd::OnTimer(TimerVal);
}

void MyDLL::OnDraw(CDC* pDC)
{
CMemDC mDC(pDC);

//EraseBkgnd(mDC);

CRect rect;
GetClientRect(rect);

// For now, background black fixed
mDC->FillRect(rect, CBrush::FromHandle((HBRUSH)GetStockObject
(BLACK_BRUSH)));

// ********** 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 / 12.5;
for (int i=1;i<=maxlines;i++){
int y_axis = 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 / 12.5;
for (int i=1;i<=maxlines;i++){
int x_axis = (i * 12.5) - gridOffset;
if (x_axis <= rect.Width()) {
mDC->MoveTo(x_axis, 1);
mDC->LineTo(x_axis, rect.Height());
}
}
}
}

here actually I'm redrawing the entire client area so it produces a
flicker even if I am using CMemDC because on this drawing I can
understand I'm doing lots of operations in a single draw...
Indeed, if I got it good as you are explaining, using ScrollWindow
would be like a single operation for almost entire part of client area
which would be very fast and then draw only next values in that little
remaining part at right edge.
The only thing I'm thinking at now is when I'm resizing which I think
it wouldn't really flicker redrawing the entire window client is about
to know what value from I have to draw left to right since in a
complete redraw due to resizing would draw from left to right....
Am I right?... What do you suggest in this case?....

Ciao!
Luigi

Joseph M. Newcomer

unread,
Oct 28, 2009, 11:50:41 PM10/28/09
to
See below...

On Tue, 27 Oct 2009 08:25:23 -0700 (PDT), Luigino <npu...@rocketmail.com> wrote:

>HI Joe,
>
>I studied a bit on this thing about ScrollWindow, PolyLine and the
>flicker thing....and I got this point:
>lemme guess using ScrollView means preventing flickering, even if I
>use CMemDC which as documented it should reduce flickering effect,
>when I am redesign the entire client area; indeed it should be enough
>to redesign a single object in the client area without using
>ScrollWindow since it's a little object with a very limited flicker
>which would be like invisible to the eye... right?...

****
If you are using the two-stage CMemDC approach, you don't need to worry about
ScrollWindow; it is an optimization that doesn't require maintiaining a separate bitmap
while giving the effect of having one.
****


>
>In fact with this code I implemented:
>
>void MyDLL::OnTimer(UINT TimerVal)
>{
> BOOL timerstate;
> // ****** stopping timer ******
> timerstate = TRUE;
> if (!KillTimer(iTimerVal))
> {
> // message
> MessageBox(_T("Unable to stop timer!"), _T
>("IDT_TIMER_0"), MB_OK|MB_ICONSTOP|MB_SYSTEMMODAL);
> timerstate = FALSE;
> }
> // ****** processing event ******
> gridOffset++;
> if (gridOffset > 12.5)
> gridOffset = 0;
>
> Invalidate();
>
> // ****** restart timer *******
> if (timerstate)
> {
> iTimerVal = SetTimer(IDT_TIMER_0, 1000, 0);
> }
>
> // call base class handler

****
I don't understand why you are stopping and starting the timer. YOu don't need to, and it
doesn't solve any known problem to do so. The code is also clumsy; you could have easily
have written
timerstate = KillTimer(TimerVal);
(I don't know what iTimerVal is) since that does everything you are doing, except that
nothing you are doing makes sense.

void MyDLL:OnTimer(UINT TimerVal)
{
if(TimerVal == IDT_TIMER)
{
gridOffset++;
if(gridOffset > 12.5)
gridOffset = 0.0;
Invalidate();
}
CWnd::OnTimer(TimerVal);
}

See how much simpler it is?

I'm presuming gridOffset is a double.
****

****
Fine, but I don't see where you do the BitBlt from mDC to PDC. So it doesn't actually do
anything.

Note that for raw performance, you would want to run through and convert the coordinates
to an array of CPoints and do a PolyLine or PolyPolyLine; it will be much faster.

Note that


int x_axis = (i * 12.5) - gridOffset;

should not compile without a warning. You should be compiling at /W4 for error checking.
int x_axis = (int) ( (double)i * 12.5) - gridOffset);
and you might consider adding 0.5 to get rounding.


****


> }
>}
>
>here actually I'm redrawing the entire client area so it produces a
>flicker even if I am using CMemDC because on this drawing I can
>understand I'm doing lots of operations in a single draw...

*****
TYpically, I consolidate the drawing into a separate handler, so I do

#ifdef _DEBUG_GRAPHICS
DoDrawing(*pDC);
#else
...create memDC
DoDrawing(mDC);
BitBlt(...);
#endif

and I do

#ifdef _DEBUG_GRAPHICS
#define GDI_FLUSH() GdiFlush()
#else
#define GDI_FLUSH()
#endif

Then you can single-step your graphics drawing if you define _DEBUG_GRAPHICS and see
things drawn one item at a time. Flicker is awful, but you can debug the graphics. Toss
GDI_FLUSH() calls after each drawing action.
****


>Indeed, if I got it good as you are explaining, using ScrollWindow
>would be like a single operation for almost entire part of client area
>which would be very fast and then draw only next values in that little
>remaining part at right edge.

****
The goal of ScrollWindow is to eliminate the double-buffer hack, particularly if you need
a massive bitmap for back (1920x1200x24bit = ~7MB)
****


>The only thing I'm thinking at now is when I'm resizing which I think
>it wouldn't really flicker redrawing the entire window client is about
>to know what value from I have to draw left to right since in a
>complete redraw due to resizing would draw from left to right....
>Am I right?... What do you suggest in this case?....

****
If you handle WM_ENTERSIZEMOVE and disable drawing (just set a flag and don't draw) and
WM_EXITSIZEMOVE clears the flag, does an Invalidate() and UpdateWindow(), then you don't
get the annoying "resize flicker" effect.

Due to a serious failure to understand reality, these messages don't have handlers you can
generate from VS; you have to hand-edit ON_MESSAGE handlers.
joe
****
>
>Ciao!
>Luigi

0 new messages