Re: How to pass information, classes between forms in Windows Application mode [ FINALLY!]



On Jul 23, 12:56 pm, Jon Skeet [C# MVP] <sk...@xxxxxxxxx> wrote:
raylopez99 <raylope...@xxxxxxxxx> wrote:
In that case I guess it's the end of the discussion.

I did learn a few things in this exchange, which I'll summarize
below. Thanks for replying.


I have explained numerous times that the best way of helping me to help
you with your "newbie concerns" is to post short but complete programs.
This is advice I've given to many, many people on many, many occasions
- and those that have followed it have almost always been glad they've
done so. It just makes life so much easier.

I didn't realize you were such a stickler for complete code, I assumed
you were just being obstinate. I'll make a mental note of it for any
future reference.


Forms apps are certainly more *complicated* than Console apps, because
there's all the designer code involved, and basically a lot more
*stuff*. But the language itself doesn't change.

Nobody said the language changed--those are your words.

You claimed forms and console apps were "totally different". They're
not. Even the entry point is the same - a Main method.

OK, if you say so. I don't even know where "Main" is in Windows
anymore, but I assume it's there somewhere.


Including Main in a class isn't the same as creating a nested class -
and it's *always* in a class (or struct) so it's not a "convention" -
it's part of how you *have* to define methods.

Yes, but it doesn't address the issue of scope, etc. But let's move
on.


That's not declaring a class. That's declaring a variable called
myClass0, of *type* Class0. The sooner you understand that, the better.
Unless you use the "class" keyword, you're *not* declaring a class.

I need a complete program to make sure that I'm seeing exactly the same
effect as you. It's common to want to be able to reproduce an issue
before commenting on it. It's also common to want to see minimal code
that *does* reproduce an issue. Both of these are most easily achieved
with console apps, when the topics in question aren't inherently
related to GUIs - and the issues you've got here really, *really*
aren't GUI-specific.

But I wrote the program in Windows Form application mode, not console
mode. I could reproduce a Windows Form error in Console mode, I would
be quite the experienced programmer, no? I would know what the error
is, that it's not a Windows specific error, then I would write a
"Console mode" equivalent of the erroneous code, then I would post the
complete console mode code (that's erroneous) for you to comment upon
and find the solution to the error. Needless to say, this is absurd.
If I'm that competent a programmer, I would not need you or anybody
else to spot errors in my code; I would not make errors in the first
place.


And you wrote a book? What was it, self published?

I'm not going to start bragging about my book here. Suffice to say the
reviews are pretty positive so far. Look for yourself on amazon.com.
You might even want to read the free chapters which are available fromhttp://manning.com/skeet

This is a good book, an intermediate level book. Just took a look at
Chap. 6, about Iterators, and writing your own, which I just reviewed
a week or so ago online, with multiple 'yield return' statements and
the like. I probably will buy this book, it looks well done. I hate
beginner "Dummies" books, which are good but not enough depth at
times--you need a little meat, like those Scott Meyers books on
Effective C# from the late 1990s.


I understand everything that occurs in the C# language in this thread.
I don't understand some of what you're trying to communicate because
you persist in talking inaccurate terms (such as "forward references")
and posting incomplete programs.

OK, OK, I'm just a newbie. And I code for 'fun' --I have a real job.


While you fail to use correct terminology, even after being corrected
on it, communication is difficult. Likewise it is harder to comment on
a program when I'm only given half the code.

But your lingo is not conducive to the free flow of information. It's
like first having to repeat a magic number before I get an answer.
You told me the magic number five posts ago, but I forgot it.


I've recognised that you have an issue in terms of your understanding
of scope, access and what you're actually declaring.

Then say so. Do we need to get to this level of back and forth before
you comment? Finally, however, I see you do comment below. A belated
"thanks" I guess.


Study and commit to memory, I've added a few Easter Egg comments
tailored just for you Jon:

Here it is:
////////// START OF PARTIAL CLASS FORM1: Form
public partial class Form1 : Form
{
Class1 myClass1; //cannot comment out this line--compile error
<--do you know why, Jon?

Obviously - you're referring to it in the constructor.

Really? A constructor in my "book" is the stuff that appears in public
Form1 () {//here}, but I guess I'm using the wrong lingo again.
Anyway, moving right along...

However, you could move the declaration to after the contructor and
methods - or even to a different file which also declares Form1 (again
as a public partial classs).

Oh, really? OK, I just tried this and it doesn't compile (sorry in
advance about the incomplete code, but you know how that goes)...

///////start
namespace MDIForms
{
Class1 myClass1; //// error CS0116: A namespace does not
directly contain members such as fields or methods

public partial class Form1 : Form
{
//Class1 myClass1; //manditory for compiling

//////end

As you can see, the program did not compile. But I guess once again I
misunderstood, my apologies in advance. Moving right along...


Class2 mYClass2;
public Form1()
{
InitializeComponent();
// Class1 myClass1 = new Class1(100,
"heLlO1YouTube!"); //
HIDES or 'overrides' previous declaration! <--Jon, does this ring a
bell? Have you done this? If not, why not? Coding style perhaps?

It hides it, yes. I do it occasionally for parameters to constructors,
e.g.

public void Foo (int something)
{
this.something = something;

}

I try to avoid doing it in other cases, however.'

OK, I did get that, I think, as I understand the code fragment you
gave is perfectly legal (this.something=something; is not the same as
something=something;) and gives the intended result, unlike my
commented out code.


//if you pass class as above rather than below line, you
get null pointer! -wrong!! <--Jon, do you understand why this is *not*
a null pointer, but a null reference, and why most people think that's
bad, regardless of what you call it?

The default value of a reference type non-local variable is null. As
for whether it's "bad" that depends on the context. Sometimes it's
exactly what you want - but not in this case.

Understood. I even saw in a Windows App event handler some code like:

MyDialogForm01 dlgForm = new MyDialogForm01();
//stuff deleted here
dlgForm = null; // at end of code; not strictly needed, apparently
some form of manual garbage collection



"But give me complete code!" says Jon,
unaware that in Forms some of the complete code can run a few thousand
lines for even a simple windows application!

On the other hand a simple and complete WinForms application is also
feasible in only a few lines of code. For example:

using System;
using System.Drawing;
using System.Windows.Forms;

class Test
{
[STAThread]
static void Main()
{
Form form = new Form {
Controls = { new Button {Name="Button", Text="Click me"}},
Size = new Size(500, 500)
};
form.Controls["Button"].Click += delegate
{ MessageBox.Show("Hello"); };
Application.Run(form);
}


OK, when I get to that level of sophistication, maybe I won't need you
then.

}
{
Form2 frm2 = new Form2(myClass1, mYClass2); //<--do you
know, reading this thread, why we are using parametric constructors
rather than default parameterless constructors?

Yes, you're using it to pass information to Form2. As we've already
discussed, there are other ways of doing that.


True enough, though in my mind other than the example of inheriting a
form, and using a method from a class that's nested in the base class,
I really don't see how. Far more flexible to pass objects--oops, I
meant variables--to the second or derived form. I mentioned in
passing that you can maybe use delegates/events between forms, but the
more I think about it, the more I see that getting the events
recognized might be a problem. Maybe you can insert "delegate <type>
VariableName (type)" throughout both forms, and maybe that will work,
but without seeing an example I'm not about to waste an hour or two
trying to find out. Besides there's a hoary tradition in C++ of using
the constructor to pass all kinds of information--I'm sure you've seen
the 'modern' constructor format of C++ (which C# sadly does not have)
where you can, after the colon, do stuff like INTergerI(jay),
BooL(true)... etc, which is equal to INergerI = jay; BooL = true;
inside the bracket { } . If you're not familiar with C++ forget all
of this. In short, I like using the constructor to pass objects and
until I see a concrete example (other than the derived classes/ nested
classes example discussed) I'm sticking to that convention, since it
works. If it ain't broke, don't fix it.




frm2.Show();
}
////////// END OF PARTIAL CLASS FORM1 : Form

// start of Partial Class Form 2: Form
namespace MDIForms
{
public partial class Form2 : Form
{
Class2 myClass2;
Class1 myNewClass1;
public Form2() //default constructor not used
{
InitializeComponent();
myClass2 = new Class2();
myNewClass1 = new Class1(99, "new!Class1!!");
}
public Form2(Class1 passClass1, Class2 passClass2)
{

Interesting that you're not calling InitializeComponent() (or
explicitly calling "this()" as a constructor initializer). Of course,
you haven't shown what Form2 actually does, but you'll find that if you
put some controls on it in the designer, those *won't* be seen when the
form is displayed by Form1.clikMeNewFToolStripMenuItem_Click.

I did add InitializeComponent() in the parametric form, but I
accidentally deleted it when I posted the (incomplete) code in my
haste to make it more readable. Sorry.


myClass2 = passClass2;
myNewClass1 = passClass1;
}
// end of Partial Class Form 2: Form

Discussion: classes 1,2 are simple classes, that contain a public
int
and public string as members, declared OUTSIDE the Form1 class, and
are not nested to the Form1 class, nor is Form 2 inherited from
Form1, but the important point is that in C# there is 'hiding' of
previously declared classes.

Nope, the classes aren't hidden at all. To prove it, just write:

new Class1(0, "");
new Class2();

in the constructor. Obviously it won't do a lot of good (unless those
constructors do interesting things) but the fact that it compiles
proves that the classes aren't hidden.

OK, thanks for that terminology. Hiding is not the best word, perhaps
'local variable overriding of outer scope (?) variables of the same
name'. Or something like that. OK I got it. Or maybe not. Moving
right along...


You *could* effectively hide them (so they'd need to be referenced in a
more explicit way, e.g. with the "global" namespace alias) by genuinely
declaring Class1 and Class2 inside Form1, e.g.

public partial class Form1 : Form
{
class Class1 {}
class Class2 {}

Class1 myClass1; //cannot comment out this line--compile error
Class2 mYClass2;

// Code as before

At that point variables myClass1 and mYClass2 would refer to the nested
classes; the constructor call to Class1 within the Form1 constructor
wouldn't compile as the appropriate constructor signature hasn't been
declared, and clikMeNewFToolStripMenuItem_Click wouldn't compile
because the types of the Form2 constructor call would be incompatible.


Interesting, thanks. You left out an important detail--when there is
ambiguity, how can you tell the compiler to 'override' the ambiguity
and compile? In C# there is no easy way, for example, if the Class1
and Class2 variables are in namespace XYZABC, you can maybe write
XYZABC.Class1 myClass1; etc., but everytime I tried this it doesn't
compile (Update: I see the 'this' pointer below solves some
ambiguity)

This is illustrated by the line //
Class1 myClass1 = new Class1(100, "heLlO1YouTube!"); //HIDES or
'overrides' previous declaration!

That's not hiding a class. That's hiding an instance variable with a
local variable.

OK, terminology noted, thanks.


{Jon would argue this is imprecise terminology: it's "declaring a
variable called myClass0, of *type* Class0.", and would then point out
that "hiding" is an imprecise word to use, since you are declaring and
defining and instantiating a new local variable that has the same name
as the variable "myClass0".

Hiding is a perfectly fine word to use - but saying you're declaring a
class when you're actually declaring a variable is hugely misleading.

OK, noted. If this is in your book I'll study it, but likely if not
I'll forget it five minutes after I post this thread.


Feel free. If you see me make a mistake, I'll welcome the correction.

Every computer science book--and even the good ones--have errors, so I
appreciate your modesty.


Even though myClass1 is declared outside the default constructor of
Form1, it is 'overridden' (I am using imprecise terminology, but if
you're astute you will understand the problem) by the second
declaration of myClass1 inside the default constructor of Form 1.

Indeed. Although you could still access it as "this.myClass1". If you
change this line:
myClass1 = new Class1(100, "heLlO1YouTube!");
to
this.myClass1 = new Class1(100, "heLlO1YouTube!");

in the Form1 constructor and uncomment this line:
Class1 myClass1 = new Class1(100, "heLlO1YouTube!");

then the program will work without a NullReferenceException.

Wow! You're good. I played around and see what you're talking about,
even though I'm not 100% clear on why the 'this' pointer (the object
calling 'Form1()') makes such a difference, but maybe the object/
variable understand to use the 'outermost' Class1. Leaving out the
'this' pointer did give a nice compiler warning CS0136, which, had I
seen it before, would have obviated having to write this entire thread
(though I guess I would not have learned as much).

// here is what I played with, comments from compiler and hoving
ToolTips

this.myClass1 = new Class1(100, "heLlO1YouTube!"); //Class1
Form1.myClass1

//failure to use 'this' above gives a compiler error: error CS0136: A
local variable named 'myClass1' cannot be declared in this scope
because it would give a different meaning to 'myClass1', which is
already used in a 'parent or current' scope to denote something else

Class1 myClass1 = new Class1(100, "FUYouTube!");//equivalent to class
MDIForms.Form1 (local only)
//never used since local and goes out of scope

// end of playing



And, using this 'wrong' way, you will get a null reference passed to
Form2, which is not correct.
And, commenting out the line "Class1 myClass1; //cannot comment out
this line--compile error" is even worse--you will get a compile error
rather than a runtime error, since myClass1 inside the normal default
constructor of Form1 will be treated as a local variable that cannot
be seen elsewhere.

Indeed, although I'd argue that a compile-time error is *better* rather
than an execution time error. If we wanted to leave all errors until
execution time, we'd use a dynamic language...

OK.


If you understand this simple example you're half way towards
understanding the problem in this thread. {assuming you even care
about understanding the problem rather than pushing your own agenda}

Your simple example doesn't show anything that couldn't be more easily
shown in a complete console app.

Assuming I knew what the problem was.

Thanks for your analysis. I will buy your book and hope to catch you
later in another thread...

RL
.