From: RB on
>That should cover it. Don't forget "if (UpdateData(TRUE)) { transfer
>from view to doc } ("if" is important, that's how UpdateData is
>supposed to work). Also, don't forget to call UpdateAllViews upon the
>change to the document (that's how doc/view is supposed to work).
>Goran.

Thanks Goran, that is a good point, and a weakness of mine, especially
when getting experimental code for posting (no excuse, just honesty)


From: RB on
> I consider using data variables to be a serious design error. I avoid them entirely. You
> should think about whether or not their existence makes sense. I consider the whole
> UpdateData mechanism to be a seriously deep design failure. I have yet to find a reason
> that it could ever make sense within the context of a dialog or CFormView-derived class.
> Their only value, which is marginal at best, is for a very limited subset of control types
> (edit controls, static controls, and check boxes; they are completely useless for any
> other kind of control)
> ****

Well, Joe first I must thank you for giving me what I asked for (as usual ) but the
context of the above pretty much flys right over my head. When you read this you
probably are either laughing or sighing. But without burdening your super helpful
energetic input methods, could you briefly elaborate on specifically you mean by
not using data variables ? Are you referrering to data variables for the controls ?
And I am correct in surmizing that you directing me further as to how to implement
this in the rest of your reply below ?

> ****
> Probably the wrong decision. What I do is keep the values in variables in the
> CDocument-derived class. In the serialize routine I simply call UpdateAllViews()
> using a > specific lHint and pHint value which are not (0,NULL), and when this particular
> combination is detected in my OnUpdate handler, I simply use operations like
> GetWindowText, GetCurSel, and similar operations to copy the values out of the controls
> and place them in variables in the CDocument-derived classs. In this way, onlly the
> document understands how to read and write the data, and there is never any use of streams
> in the view. Data transfer to and from the document are managed by the document class,
> which is where it should be handled.
> When the CFormView starts up, the OnInitialUpdate loads the controls from the variables in
> the CDocument-derived class.
>
> I do not believe in the DDX and DDV mechanisms at all, except for the DDX_Control calls to
> bind the controls to control variables.
> ****

Ok.... I will have to dwell on this awhile.

>> At first I thought I could just make the view class a friend of the document,
> ****
> NO!!!! This would require the CDocument-derived class know about the views, and this is
> COMPLETELY WRONG!. Use UpdateAllViews to handle the transfer, as described.
> NEVER include a view header file in the document class; if you think you have to, your
> design is wrong!
> *****

Ok, I had a feeling as such

> ***
> I consider writing UpdateData to be a fundamental design error. It is based on a set of
> myths that I simply do not believe; that it makes sense to EVER transfer *every* data
> value either to the controls, or from the controls. This has nasty implications when the
> values interact. Read my essay on Dialog Control Management on my MVP Tips site.
> ****

Ok I will check Tips site out

>****
>> DocPtr->InletA->dInvert_In_1 = m_Invert_In_1;
>> DocPtr->InletA->dInvert_Out_1 = m_Invert_Out_1;
>> DocPtr->InletA->uiInletNum = m_InletNum;
> ***
> THis is incosnsitent with your declaration; it should be
> DocPtr->InLetA.dInvert_In_1 = m_Invert_In_1;
>
> because InLetA is not a pointer
> ****

Yes that is correct, your eagle eye caught it. While we are here
would you be so kind as to tell me how I might write this if I changed
InletA to an array of structs as in
Inlet InletA[10];
In other words how would I first even declare properly the StructArrayPtr?

Inlet* InletA[10]; I sense this would only be an array of pts and not
what I want.
And then how would I properly write the update code ?
DocPtr->InletA[2] ?? dInvert_In_1 = m_Invert_In_1;
--------------
And finally to this last input (below) from you. I am saving this and will study
over it. But I have the following questions:
1. If I implement the below then I surmise I should call the OnUpDate from
my OnEnter handler ? (from a control button click)
2. Notwithstanding the previous input you have given me, is there an
additional key directional thought focus item that you could give me as
why this method is better than, or safer than, or logistically better than
using DDX

**********
> I would do it using approximately the same idea, but do it in the OnUpdate handler:
>
> void CMyView::OnUpdate(CView * view, LPARAM lHint, CObject * pHint)
> {
> if(lHint == 0 && pHint == NULL)
> {
> CView::OnUpdate(view, lHint, pHint);
> return;
> }
> switch(lHint)
> { /* lHint */
> case VALUES_TO_DOCUMENT:
> {
> CMyDoc * DocPtr = ...etc.
> DocPtr->SetInvertIn(...);
> DocPtr->SetInvertOut(...);
> DocPtr->SetInletNum(...);
> // I prefer to use set/get methods rather than directly access variables
> // I might also do this as
> // DocPtr->SetInLetValues(..., ..., ....);
> // and not worry about the specification of the structure at all!
> }
> break;
> case ...
> break;
> default:
> ...maybe ASSERT(FALSE) here....your choice
> break;
> } /* lHint */
> } // CMyView::OnUpdate
************


From: Joseph M. Newcomer on
See below,,,
On Wed, 5 May 2010 10:55:47 -0400, "RB" <NoMail(a)NoSpam> wrote:

>> I consider using data variables to be a serious design error. I avoid them entirely. You
>> should think about whether or not their existence makes sense. I consider the whole
>> UpdateData mechanism to be a seriously deep design failure. I have yet to find a reason
>> that it could ever make sense within the context of a dialog or CFormView-derived class.
>> Their only value, which is marginal at best, is for a very limited subset of control types
>> (edit controls, static controls, and check boxes; they are completely useless for any
>> other kind of control)
>> ****
>
>Well, Joe first I must thank you for giving me what I asked for (as usual ) but the
>context of the above pretty much flys right over my head. When you read this you
>probably are either laughing or sighing. But without burdening your super helpful
>energetic input methods, could you briefly elaborate on specifically you mean by
>not using data variables ? Are you referrering to data variables for the controls ?
***
Control variables would NEVer be public; it is a complete failure of the IDE design (one
of many too numerous to elaborate on here) to default to "public" for a control varfiable
or for an event handler. It goes beyond completely stupid into the realm of deeply
irresponsible I attrribute it to a lack of adult supervision of designs,

the basic contrast here is

class CWhatever : public CView {
public:
int SomeValue;
}

CWhatever * p;

p->SomeValue = 3;

and the setter/getter model:

class CWhatever : public CView {
protected:
int SomeValue;
public:
void SetSomeValue(int newvalue);
int GetSomeValue();
};

By having the functions, you can do many things, e.g.,

void CWhatever::SetSomeValue(int newvalue)
[
if(SomeValue == newvalue)
return;
SomeValue = newvalue;
if(GetSafeHwnd() != NULL)
Invalidate()l
}

CWhatever * p;
p->SetSomeValue(3);

A common childish belief is that the setter/gerr code is "less efficient" than straight
assignmen to a member variablet, which means that there is a belief that a couple
nanoseconds to do the call and return actually matter; also, modern compiler using Link
Time Code Generation (LTCG) will actually generate the code inline anyway, so the
rationale never made sense in the past and certainly doesn't make sense today. Such myths
are created by PDP-11 programmers who still teach programming but who never grew up.

Note that the setter does not need to know if the implementation needs to do anything in
response to the change; the setter just calls SetSomeValue and Magic Happens.
joe
*****
>And I am correct in surmizing that you directing me further as to how to implement
>this in the rest of your reply below ?
>
>> ****
>> Probably the wrong decision. What I do is keep the values in variables in the
>> CDocument-derived class. In the serialize routine I simply call UpdateAllViews()
>> using a > specific lHint and pHint value which are not (0,NULL), and when this particular
>> combination is detected in my OnUpdate handler, I simply use operations like
>> GetWindowText, GetCurSel, and similar operations to copy the values out of the controls
>> and place them in variables in the CDocument-derived classs. In this way, onlly the
>> document understands how to read and write the data, and there is never any use of streams
>> in the view. Data transfer to and from the document are managed by the document class,
>> which is where it should be handled.
>> When the CFormView starts up, the OnInitialUpdate loads the controls from the variables in
>> the CDocument-derived class.
>>
>> I do not believe in the DDX and DDV mechanisms at all, except for the DDX_Control calls to
>> bind the controls to control variables.
>> ****
>
>Ok.... I will have to dwell on this awhile.
****
See my essays on the use of control variables, dialog control management, and the use of
constraint-management patterns on my MVP tips site.
*****
>
>>> At first I thought I could just make the view class a friend of the document,
>> ****
>> NO!!!! This would require the CDocument-derived class know about the views, and this is
>> COMPLETELY WRONG!. Use UpdateAllViews to handle the transfer, as described.
>> NEVER include a view header file in the document class; if you think you have to, your
>> design is wrong!
>> *****
>
>Ok, I had a feeling as such
>
>> ***
>> I consider writing UpdateData to be a fundamental design error. It is based on a set of
>> myths that I simply do not believe; that it makes sense to EVER transfer *every* data
>> value either to the controls, or from the controls. This has nasty implications when the
>> values interact. Read my essay on Dialog Control Management on my MVP Tips site.
>> ****
>
>Ok I will check Tips site out
>
>>****
>>> DocPtr->InletA->dInvert_In_1 = m_Invert_In_1;
>>> DocPtr->InletA->dInvert_Out_1 = m_Invert_Out_1;
>>> DocPtr->InletA->uiInletNum = m_InletNum;
>> ***
>> THis is incosnsitent with your declaration; it should be
>> DocPtr->InLetA.dInvert_In_1 = m_Invert_In_1;
>>
>> because InLetA is not a pointer
>> ****
>
>Yes that is correct, your eagle eye caught it. While we are here
>would you be so kind as to tell me how I might write this if I changed
>InletA to an array of structs as in
>Inlet InletA[10];
> In other words how would I first even declare properly the StructArrayPtr?
****
In this case, I'd definitely use setters and getters!
*****
>
>Inlet* InletA[10]; I sense this would only be an array of pts and not
> what I want.
****
I have a strong belief that if you ever write a declaration in C++ of the form
type name[complie-time-constant]
you are not using C++ correctly., Instead, consider CArray or std::vector
implementations! Preferrably the latter.
*****
>And then how would I properly write the update code ?
>DocPtr->InletA[2] ?? dInvert_In_1 = m_Invert_In_1;
****
DocPtr->inLetA[2]->dInver_In_1 =

because InLetA[2] is a pointer type! But you shouldn't be writing code like this! Not in
C++. Using setters and getters, you might do

int n = DocPtr->Add(...something...?);
DocPtr->SetInvertIn(n, ...expression...);

Much cleaner, and avoids all kinds of nasty problems. For example,your setter can
implement array bounds checks and be a BOOL; return FALSE if there is a bounds error, and
you can write
VERIFY(DocPtr->SetInVertIn(,,.));
and it will catch bugs during debugging, and at least not do overruns on release,
****
>--------------
>And finally to this last input (below) from you. I am saving this and will study
>over it. But I have the following questions:
>1. If I implement the below then I surmise I should call the OnUpDate from
> my OnEnter handler ? (from a control button click)
****
OnUpdate is called by the framework (usually, with NULL,0,NULL) during the view setup, so
you don't need to worry about it. If called with other than (0, NULL) as the last two
arguments, you use this to determine if you must capture the values to the document, or
the document has new values to be placed in the controls, etc.
****
>2. Notwithstanding the previous input you have given me, is there an
> additional key directional thought focus item that you could give me as
> why this method is better than, or safer than, or logistically better than
> using DDX
****
DDX does not allo for you to detect or react to interacting changes; e.g., setting the
check box enables the dropdown list. If you start distributing this code throughout your
dialog/formview, eventually the code diverges, and you end up with six different
algorithms, none of which are quite right. My constraint-management pattern means that
there is exactly ONE place where interactions are managed and predicates are evaluated.

And DDX doesn't work right for dropdown lists, list controls, tree controls, etc. so using
it only in the very limited cases it works in fools you into thinking it makes sense.
joe
****
>
>**********
>> I would do it using approximately the same idea, but do it in the OnUpdate handler:
>>
>> void CMyView::OnUpdate(CView * view, LPARAM lHint, CObject * pHint)
>> {
>> if(lHint == 0 && pHint == NULL)
>> {
>> CView::OnUpdate(view, lHint, pHint);
>> return;
>> }
>> switch(lHint)
>> { /* lHint */
>> case VALUES_TO_DOCUMENT:
>> {
>> CMyDoc * DocPtr = ...etc.
>> DocPtr->SetInvertIn(...);
>> DocPtr->SetInvertOut(...);
>> DocPtr->SetInletNum(...);
>> // I prefer to use set/get methods rather than directly access variables
>> // I might also do this as
>> // DocPtr->SetInLetValues(..., ..., ....);
>> // and not worry about the specification of the structure at all!
>> }
>> break;
>> case ...
>> break;
>> default:
>> ...maybe ASSERT(FALSE) here....your choice
>> break;
>> } /* lHint */
>> } // CMyView::OnUpdate
>************
>
Joseph M. Newcomer [MVP]
email: newcomer(a)flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
From: RB on
Oh ok, if I get the jest of input you are saying to make all data vars
either protected or private and use getter setter funcs to access
and/or change them. Well that makes sense, I can't argue with that.
And I can see the advantages to such as opposed to just assignment
by any means, DDX or otherwise.

However I am still a bit out in left field on your latest instruction on
CArray or std::vector, and not just because I have never used either
one of them, but why is the structure array not ok if I access it using
setter getter paradigm. I am trying to write a calculator of pipe
inverts and the struct type seems to fit what I need. But I have many
manholes and need a structure array to reference all of them.
And by the way just for my knowledge. I see what you told me about
assigning the structure array member using a structure array ptr,
DocPtr->InletA[2]->dInver_In_1 =
But how would a person declare a ptr to a structure array.
Struct InletA[10];
Inlet* InletA[10]; I don't think this is it ??
Inlet (*InletA)[10]; ?? How would one do this ?
( I am at work now and do not have access to my compiler so my
suggestions may have given me errors to answer my questions,
but none the less would like your input on this )


From: Joseph M. Newcomer on
See below...
On Thu, 6 May 2010 10:08:28 -0400, "RB" <NoMail(a)NoSpam> wrote:

>Oh ok, if I get the jest of input you are saying to make all data vars
>either protected or private and use getter setter funcs to access
>and/or change them. Well that makes sense, I can't argue with that.
>And I can see the advantages to such as opposed to just assignment
>by any means, DDX or otherwise.
>
>However I am still a bit out in left field on your latest instruction on
>CArray or std::vector, and not just because I have never used either
>one of them, but why is the structure array not ok if I access it using
>setter getter paradigm.
****
Generally, because there is no bounds checking done in pure C. Also, it puts large
objects on the stack (there was some example of this posted here a few weeks ago, where
somebody had a local array of 17x17xmassive structure on the stack and was getting stack
overflows). Overall, the advantage of CArray or std::vector is that the stack space
consumed is very small (a few bytes) and the data is always on the heap.
*****
>I am trying to write a calculator of pipe
>inverts and the struct type seems to fit what I need. But I have many
>manholes and need a structure array to reference all of them.
****
If you don't know how many you have in advance, a compile-time-constant array is a Really
Bad Idea. Either you need to make it big enough to hold the absolute maximum you might
ever encounter (consuming massive stack space) or be prepared to deal with the fact that
the program is not adequate to the task because the array bounds, which must be set at
compile time, are too small. CArray::SetAt will let you set any array element, and do
bounds checking, and CArray::SetAtGrow will expand the array to the specified size.
CArray::Add will expand it until memory exhausts. There are analogous features for
std::vector.

The old-fashioned array declared on the stack is just that: old-fashioned, replaced by far
more flexible, robust, and powerful alternatives. The key here is to stop thinking like a
C programmer and start thinking like a C++ programmer.
****
>And by the way just for my knowledge. I see what you told me about
>assigning the structure array member using a structure array ptr,
>DocPtr->InletA[2]->dInver_In_1 =
>But how would a person declare a ptr to a structure array.
>Struct InletA[10];
>Inlet* InletA[10]; I don't think this is it ??
***
This declares an array of pointers to structures, but you actually have to make sure the
elements you care about are initialized with pointers that point to actual instances of
the structure, otherwise you will have serious errors in the code.

e.g.

Inlet* InletA[10];
InletA[2]->whatever = 0; // access fault if you are lucky!

The above fails because InletA[2] has no valid value (in debug mode, it is initialized to
0xCCCCCCCC if it is declared on the stack, 0xCDCDCDCD if it is on the heap)

You would have to do

Inlet * InletA[10];
InletA[2] = new InletA;
NOW you can write
InletA[2]->whatever = 0;

because there is a real object there.

But I would never write code like this. I might do

CArray<Inlet> InletA;

InletA.SetSize(10);

and now I can write

InletA[2].whatever = 0;

or I might write

std::vector<Inlet> InletA;
InletA.resize(10);

The choice of using objects or pointers to objects is another dimension of design, but
first you have to start programming in C++, not primitve 1975-style C.
joe
*****
>Inlet (*InletA)[10]; ?? How would one do this ?
>( I am at work now and do not have access to my compiler so my
> suggestions may have given me errors to answer my questions,
> but none the less would like your input on this )
>
Joseph M. Newcomer [MVP]
email: newcomer(a)flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm