Re: Creating A Window
"Joseph M. Newcomer" <newcomer@flounder.com> wrote in message
news:988hv4p26nla5j7d7r8vsr1ms0e2lo2423@4ax.com...
The CDialog constructor already gets passes a resource handle to a
dialogue
template, so in principle it knows exactly where they're going to be.
****
No, it doesn't. It can read the template, but then it has to transform
the DBU
coordinates of the template into the appropriate pixel coordinates, in the
same way the
dialog would when it was instantiated. Note, however, that the ID is not
seen until the
constructor for the CDialog is called, and by that time, if the
declaration
CWindowType name;
were to actually do the instantiation, the windows of the controls all had
to be created
in spite of the fact that there is no container in which to create them.
That is, there isn't a parent...
Sorry, I still don't understand the problem: the first thing a constructor
does is construct the base class, then it constructs members. The base
class constructor would first ensure there is a dialogue window, a parent
for the controls which are then created. If the CDialog base class
constructor is passed the template resource ID, it can surely do anything
DoModal currently does(). In fact it is obvious isn't it?
CMyDialog dlg( ....... );
dlg.DoModal();
could be achieved in a single call to a less thin constructor.
But note the code that positions them in the dialog is built into Windows,
and MFC would
have to simulate that, including dealing with evolution of the OS over
time
An MFC "thick constructor" for CDialog could still call the underlying
Windows API toposition the controls.
I think that the result is some very complicated engineering that does not
need to be done
in the current implementation. I think that overall the design will
rapidly get out of
control.
More complicated than the thin wrapper approach, but susrely not so far
beyond the wit of man.
and in fact if you look at what happens to
people who try to have the variable declaration create the window in the
constructor, they
end up always having to use 'new' and then worry about 'delete' because
of
the coupling.
...
I have never done that - it would involve fighting MFC too much. I do
pass my dialogues all the data they need in the constructor as I hate the
idea of assigning necessary stuff between the constructor and the call to
DoModal(), but I go with the flow and let OnInitDialog() do its stuff. I
do like Object Orientation but with MFC one has to be pragmatic about what
one has :-)
****
Indeed. I've also seen people add parameters to the constructor to do
this, which is
more in compliance with OO philosophy, and it turns out to be a nightmare
to maintain
after a while. By the time you've added 22 parameters to the constructor
(I once saw 14),
you begin to realize this doesn't scale very well.
Typically most of my constructors have about one extra parameter: a
reference to a structure (complete with copy constructor and assignment
operator) which holds the information which needs to be given to the
dialogue box. A copy is taken by the dilaogue constructor, and the
information is used in OnInitDialog to initialise the controls. OnOK()
ensures the dialogue's copy of the structure has the edited information, and
a member returns a const reference to it so tht it can be extracted after
DoModal() exits. Schematically:
MyData mydata;
CMyDialog dlg( nTemplateId, MyData, pParent );
if( dlg.DoModal() == IDOK ) myData = dlg.dlgdata();
That much is object-oriented enough; it's all the innards with
OnInitDialog() which isn't.
"If you have more than 9 parameters to a function, you've forgotten a few"
(Alan J.
Perlis)
Yes, exactly - that's what structures are for :-) You just have to
maintain the copy constructor and assignment operator of the structure.
The thicker the layer gets, the more it has to hide the underlying
mechanism.
I think that's a nice definition of Object Orientation :-)
At some
point, reaching down to manipulate the lower level becomes so complex that
you can only
work within the stringent limitations of what the layer-designers
anticipated.
That is the difficulty of designing such a system.
We're already seeing some of this with the "Feature Pack" which fails to
implement
mechanisms that already existed in the simpler controls.
I am trying hard not to look at the feature pack untill after my next
release. It would only distract me by tempting me to do all sorts of stuff
which would delay the next release :-( Discipline discipline! :-(
A classic example of this is the "point-and-grunt" driver frameworks.
They pretend to be
simple frameworks for building drivers, written in C++ (which also ignores
the fact that
C++ can't be used in the kernel, unless you are *very* careful, and only
use the
undocumented subset that currently works in the current version of the
compiler). They
create a very thick layer that idealizes the driver model, but if your
device requires the
slightest deviation from the formulaic approach then the whole thing
crumbles to dust.
I have never written a driver.
...
I think the cost interms of designing it would certainly have been far
higher. I am still not convinced that the cost in terms of using it
would
have been (had the design been good of course).
*****
But then, you are assuming that someone who thought that calling the
superclass of a "root
class" like CEdit should use the parameters from the last message instead
of the
parameters passed in the call understands OO programming. Could you have
*really*
expected someone who could make a mistake this serious could get a complex
design right?
Ah, the question of trusting Microsoft to get it right is a rather different
one - I'd rather not go there :-(
Sorry, I don't follow that. How can you initialise a member variable
of
an object before the object's constructor is called?
****
Because when the class is instantiated, all the constructors of all the
variables are
called first.
Depends what you mean by "variables" and "first". Stroustrup (my old 2nd
edn) r.12.1: "If a class has base classes or member objects with
constructors, their constructors are called before the constructor for the
derived class. The constructors for base classes are called first."
So a CDialog base class creates a CWnd base first, then it can create all
the controls as HWNDs.
Your CMyDialog can then associate these with CButton, CEdit etc members if
you need access to them.
Consider the following
> class Thing {
public:
Thing() { _tprintf(_T("Thing @ %p\n"), this);
}
};
class Whatever {
public:
Whatever() { _tprintf(_T("Whatever @ %p\n"), this);
}
protected:
Thing thing1;
Thing thing2;
Thing thing3;
};
int _tmain(int argc, _TCHAR* argv[])
{
Whatever what1;
Whatever what2;
return 0;
}
===========================
Output:
Thing @ 0012FF28
Thing @ 0012FF29
Thing @ 0012FF2A
Whatever @ 0012FF28
Thing @ 0012FF1C
Thing @ 0012FF1D
Thing @ 0012FF1E
Whatever @ 0012FF1C
Yes that is clear if you write the constructor as
Whatever::Whatever()
: thing1()
, thing2()
, thing3()
{
_tprintf(_T("Whatever @ %p\n"), this);
}
which is one reason I always write it like that these days (being careful
of member ordering).
But in this case Whatever hasn't got a base class, which would have been
constructed first. So you'd be ok with
CMyDialog :: CDialog :: CWnd
The constructor would create a window in CWnd first, then the CDialog could
create controls, eg as collection of child hWnds, and then CMyDialog's
constructor could associate them with members representing control windows.
You'd just the need CButton constructor to take its control ID and its
parent window handle and then it could find its own HWND.
BTW hope you don't mind me prolonging the discussion: I find it interesting
to think about what might have been. And you're quite right that it would
have been harder, but I still don't think it would have been *so*
complicated!
Dave
--
David Webber
Author of 'Mozart the Music Processor'
http://www.mozart.co.uk
For discussion/support see
http://www.mozart.co.uk/mozartists/mailinglist.htm