Visibility/Access of Class Members

L

Liz

ok, this is really simple stuff, or it should be ... but I'm stuck

In a Windows Forms app, I have something resembling this:

Form1.cs
========

namespace NS

Class Form1
{
public Label label1

...

private void func1()
{
iterateRows();
}
}

Class1.cs
=========

using NS;

public class GetLabel
{
private void iterateRows()
{
Form1.label1.Text = "some value";
}

}


the method iterateRows is really performing a loop operation and I just want
to show values in label1 with each iteration; simple, right ? should be
but label1 is not visible from here ... I cannot find anything in the docs
which makes clear to me why Form1.label1.Text is not visible ... this kind
of stuff used to be a piece of cake in "C" .. but I've been away from coding
for awhile ...

I suppose I could pass a reference to label1 to iterateRows() and make this
work ? but I'd rather not and I'd like to understand why this won't work
in any case

Thanks for any help/explanations ...

L
 
B

Bob Grommes

Liz,

If I don't miss my guess, the real problem is Form1, not label1. As
coded, label1 would be visible, since you declared it public. But it's
within Form1.

Form1 is a class name. It's not an instance variable. In other words
it's the blueprint for the thing, not the thing itself.

At runtime, somewhere in your code, you're creating an *instance* of the
form, along the lines of:

Form myForm = new Form1();

The myForm reference -- or a copy of it -- is what you need to reference
in your GetLabel class:

myForm.label1.Text = "some value";

The thing is, myForm will just be a local variable to the code it's
created by, and also not visible in another class. That's what
encapsulation is all about.

Some solutions:

1) Send the form instance to the constructor of GetLabel:

Form myForm = new Form1();
GetLabel labelGetter = new GetLabel(myForm);

In the GetLabel constructor, store that instance in a private member of
GetLabel, then you can reference it later in GetLabel method code:

this.theForm.label1.Text = "Whatever";

2) Have a public property of GetLabel accept a Form:

GetLabel labelGetter = new GetLabel();
labelGetter.TheForm = new Form1();

3) Another possibility is to store the form instance in a third class,
either a form manager or a global objects cache. This is actually
closest conceptually to what you're probably thinking of. For example:

public class Globals {
public static Form TheForm;
public static string ConnectionString;
// other stuff as needed
}

....
// wherever you create the form
Globals.TheForm = new Form1();
....
// within your GetLabel class
Globals.TheForm.label1.Text = "whatever";

Notice that since we're dealing with static members, we can address them
through the class name without having to have an instance of Globals.

All of the above code is off-the-cuff and untested, no warranty as to
merchantability or fitness for a particular purpose, yadda-yadda, but
this should get you heading in the right direction.

Two final and unrelated words of advice:

a) I would not expose form controls as public fields. Expose only what
must be publicly accessible, and use a name that is descriptive. For
example, code a public *property* named, say, RowLabelText, and have its
getter and setter reference label1.Text. Keep label1 protected.

b) GetLabel describes an action, not a thing, and so is a lousy name for
an object (which is a thing). How about LabelGetter?

--Bob
 
L

Liz

Liz,

If I don't miss my guess, the real problem is Form1, not label1. As
coded, label1 would be visible, since you declared it public. But it's
within Form1.

Form1 is a class name. It's not an instance variable. In other words
it's the blueprint for the thing, not the thing itself.

that much I get ... but even if I do this:

============================================================
public class Form1
{
public Form myForm; // as a class declaration

.....
}

static void Main(){
Form myForm = new Form1();
Application.Run(myForm)
}
============================================================

even then ... in the IDE, if I type "myForm." the code completion does not
show my public var "label1" ... this I don't understand; the IDE should
"understand" that label1 WILL be a member var of the object when it's
instantiated, no ? so it should let me make an assignment to it I would
think ... but it won't ... (no, I don't think the IDE is broken but I have
not yet figured out what I'm doing wrong here)

I CAN make this work simply by passing a reference to label1 to the
iteration method in the other .CS file .. but I don't like not being able to
do a seemingly simple thing like reference label1 from my .CS with all the
methods I'm going to use

The rest of your suggestions give me some things to think about ... thanks
for the input and please feel free to add any other thoughts !

Liz
 
J

Jon Skeet [C# MVP]

Liz said:
that much I get ... but even if I do this:

============================================================
public class Form1
{
public Form myForm; // as a class declaration

....
}

static void Main(){
Form myForm = new Form1();
Application.Run(myForm)
}
============================================================

even then ... in the IDE, if I type "myForm." the code completion does not
show my public var "label1" ... this I don't understand; the IDE should
"understand" that label1 WILL be a member var of the object when it's
instantiated, no ?

No, because you've declared myForm to be of type Form, not type Form1.
The compiler can't know that the value won't be a reference to an
instance of a completely different form.

Declare it as Form1 (preferrably renaming it to a more useful name, of
course) and it should start working. Having said that, it's not good
practice to have public variables in the first place.
 
B

Bob Grommes

I'm not sure why you're declaring a class member named myForm within the
Form1 class definition. As coded, it would produce a null Form
reference within the Form1 instance, and would be of no use.

If label1 is still declared as a public field, myForm.label1 should be
able to be referenced. Don't conclude it's not from the absence of
Intellisense, in my experience that's only 80% reliable until the
project has had a recompile (or sometimes it starts working on its own
after some IDE background thread has caught up with reality). It's not
a good sign though. Is code completion failing in Main() or somewhere else?

You can always test by putting some dummy reference in Main() like
myForm.label1.Text = "foo"; just before the Application.Run, and
compiling. If you get no error, I'd bet that Intellisense will start
working thereafter. If you get a compile error, then you know that
you've got some other problem, and the actual compiler error will tend
to lead you to the source of the trouble.

--Bob
 
L

Liz

No, because you've declared myForm to be of type Form, not type Form1.

true ... thank you; but the other code file still does not "see" the public
var label1 even though the namespace in which it "lives" is referenced
The compiler can't know that the value won't be a reference to an
instance of a completely different form.

well, the compiler should know that it's either of type Form or of type
Form1, no ?
Declare it as Form1 (preferrably renaming it to a more useful name, of
course) and it should start working. Having said that, it's not good
practice to have public variables in the first place.

well, I'll leave the "religious" aspects of coding alone ;) this is just a
sandbox so I can nail down the functional characteristics of the language
....
 
L

Liz

Bob Grommes said:
I'm not sure why you're declaring a class member named myForm within the
Form1 class definition. As coded, it would produce a null Form
reference within the Form1 instance, and would be of no use.

correct ... it doesn't need declaration there
If label1 is still declared as a public field, myForm.label1 should be
able to be referenced. Don't conclude it's not from the absence of
Intellisense, in my experience that's only 80% reliable until the
project has had a recompile (or sometimes it starts working on its own
after some IDE background thread has caught up with reality). It's not
a good sign though. Is code completion failing in Main() or somewhere else?

You can always test by putting some dummy reference in Main() like
myForm.label1.Text = "foo";

sure .. and that works, in the Class block, anyway .. but not in the other
..cs file which references the namespace containing the class Form1 ...

I still don't really get why I cannot perform the same test with
Form1.label1.Text = "foo"; ... what's the deal ? does the **IDE** actually
create an instance of the class when you type in Form1 myform = new Form1()
?? and if it hasn't seen a "new" yet it doesn't know that label1 is a
member of the class ?

static void Main()
{
myForm.label1. .... // code completion does not work here
Form1 myForm = new Form1();
myForm.label1.Text = "XXX"; // but works here ...
Application.Run(myForm);
}


L
 
J

Jon Skeet [C# MVP]

Liz said:
true ... thank you; but the other code file still does not "see" the public
var label1 even though the namespace in which it "lives" is referenced

The namespace is pretty irrelevant. Could you post a short but complete
program which demonstrates the problem? If you've got a variable of
type Form1, you should be

See http://www.pobox.com/~skeet/csharp/complete.html for details of
what I mean by that.
well, the compiler should know that it's either of type Form or of type
Form1, no ?

No - it could be any other form, as far as the compiler knows.
well, I'll leave the "religious" aspects of coding alone ;) this is just a
sandbox so I can nail down the functional characteristics of the language
...

It's still worth learning best practices at the same time though.
 
J

Jon Skeet [C# MVP]

Bob Grommes said:
I'm not sure why you're declaring a class member named myForm within the
Form1 class definition. As coded, it would produce a null Form
reference within the Form1 instance, and would be of no use.

If label1 is still declared as a public field, myForm.label1 should be
able to be referenced.

No it shouldn't - note that myForm is just a Form variable, not a Form1
variable.
 
B

Bob Grommes

The IDE knows about the Form1 type. It doesn't need to create a
design-time instance, it just is aware of the metadata that defines
those types. (The designer probably does create a design time instance,
but that's separate from the code editor AFAIK).

At any rate ... I don't understand your final question, "I ... don't ...
get why I cannot perform ... Form1.label1.Text = "foo"". If that wasn't
a slip of the keyboard then I must return to my original explanation:
Form1 is a type, not an instance. The only thing you can access through
the type are static members. Instance members must be addressed through
an instance. Form1.label1 is not a valid construct because label1 is an
instance member.

If on the other hand you're asking (given a Form1 instance named myForm)
why "myForm." doesn't auto-complete, but "myForm.label1." *does* offer
you auto-complete options -- I don't know offhand. I've seen stuff like
this intermittently and just chalk it up to One Of Those Things.
Sometimes it clears up on its own (because some background parsing
thread caught up with what you're typing) and sometimes it Just Won't
Work, At Least Not Today.

--Bob
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top