"Global" objects

B

BobRoyAce

I am new to .NET and am wondering how to accomplish having certain
global objects that could be referenced by other forms/user controls. I
have a main form on which I show various user controls depending on
which item the user clicks on in the NavBar on the left of the form.
Each user control has certain program functionality on it. What I want
to do, for example, is have certain object variables on the main form
(e.g. m_oEventLogger) that I can reference from my user controls, or
from other forms that I might show.
1) How do I declare these in the code for the Main form?
2) How would I refer to them from user controls shown on the main form?

3) How would I refer to them from other forms that are shown separate
from the main form?
 
K

Kevin Spencer

..Net is object-oriented. This is the most important thing to remember and
understand, as I explain the answers to your questions. Once you "get" how
object-oriented programming works, it should all become very clear and easy
to understand.
1) How do I declare these in the code for the Main form?

The "Main form" is a class, or rather, an instance of a class. The code that
you write is all part of the Form's class definition. You'll notice that the
code for the form is all contained within a block that defines a class which
inherits System.Windows.Forms.Form, the base class for all .Net Windows
Forms. Your class definition is an extension of the Form class, which is a
class of its own, with all the aspects of the inherited class, plus whatever
you add to it.

These "global objects" are also class instances, which will be members of
the Form class you define. They can be fields or properties. Here's an
example of a field:

private string _DefaultStatusMessage = "Ready";

Here's an example of a property (which has a get and a set accessor):

public string DefaultStatusMessage
{
get { return _DefaultStatusMessage; }
set { _DefaultStatusMessage = value; }
}
2) How would I refer to them from user controls shown on the main form?

The Controls in the form are also members of the form. Since they are all
members of the same class, you simply refer to the other class member
directly. Example:

Status1.Text = _DefaultStatusMessage; // Status1 is a StatusStrip
3) How would I refer to them from other forms that are shown separate
from the main form?

The "main form" is the main thread of the Application. Whatever other forms
you may create will be created as class instances in that Form, and opened
from that Form. As they are declared as members of the Form, you can assign
values to them directly from the Form, by referencing the class name, or
from the member Form by referencing the Form that spawned it. To reference
the form created from the "main form" you just reference the field or
property name:

private Form2 _Form2;

_Form2 = new Form2();
_Form2.Show();

To reference the "main form" you must create a reference to it in the
spawned Form. This can be done in any of several ways. If it is an Owned
Form, you can refer to the Onwer property. You can create a field in the
spawned Form and set it to the Form that spawns it. The main idea is the
same. There must be a reference to the spawning Form in the spawned Form:

Form1 code:

private Form2 _Form2;

_Form2 = new Form2();
OwnedForms.Add(_Form2);
_Form2.Show();


Form2 code:

Owner.DefaultStatusMessage = "Hello from Form2";

--
HTH,

Kevin Spencer
Microsoft MVP
Professional Chicken Salad Alchemist

A lifetime is made up of
Lots of short moments.
 
S

Stoitcho Goutsev \(100\)

BobRoyAce,

If you need methods, properties, events, etc accesible from anywhere without
the requirement to have a reference to an object that you need to declare
these memebers as static. When a member is static it can be accessed by
prefixing the member name with the type name e.g. MyType.MyProperty.
 
B

BobRoyAce

Thanks Kevin for the eplanations. I tried doing what I understood you
to be saying to do, but seem to be having a problem. I should start by
letting you know that what I'm doing is SHOWing user controls with
their parent being a panel on the main form. At design time, of course,
their parent/owner does not exist because I don't assign it until
runtime. Anyway...

In MainForm.vb (for the form I named frmMain in the designer) I have
code near the top of the module as follows:
Private g_oStreetAddressCleaner As StreetAddressCleaner

Public Function GetStreetAddressCleaner() As StreetAddressCleaner
Return g_oStreetAddressCleaner
End Function

Then, in the New method for frmMain, I have the following code:
g_oStreetAddressCleaner = New StreetAddressCleaner

In the New method of the User Control, let's say, frmEdit, I have code
as follows:
m_oStreetAddressCleaner = frmMain.GetStreetAddressCleaner

Well, when the last line of code above executes, it ends up resulting
in the New method of frmMain being called again. What am I doing wrong?
 
K

Kevin Spencer

Well, when the last line of code above executes, it ends up resulting
in the New method of frmMain being called again. What am I doing wrong?

I'm not even sure where to start. First, I think that what you understood me
to be saying was not what I was saying at all. It can be difficult to
communicate by writing. What I was doing was explaining how .Net does
Windows Forms in an object-oriented way, and answering your questions. I
began by explaining that *understanding object-oriented programming* is the
single most important thing for you to do, and then went on to address each
question individually.

Your first question was
1) How do I declare these in the code for the Main form?

I explained how the Form is a class which contains other classes as members,
and that members (fields or properties, which I gave a simple example of)
are "global" within the scope of the Form, which is the main thread of the
application. What you wrote back was that you tried doing what you
understood me to be saying to do. Well, I didn't say to *do* anything, other
than to make a concerted effort to *understand* what you're doing before you
start to do it. I did explain and illustrate how fields and properties are
created. Your private field was a correct implementation of a class field.
But your public function was not a property at all. It did not have a get
method or a set method. It was a function that returns the private field.

Okay, in your first paragraph you mentioned that at design time the parent
doesn't exist because you assign it at run-time. Well, actually, nothing
exists until you assign it at run-time. What the designer does is write code
for you, which executes when the application runs. What you see at
design-time is an approximate visual representation of how these class
instances will appear after they are created at run-time. My point here is
that there is no difference whether Visual Studio writes the code or you do.
The rules for the code are still the same.

As an aside, you might benefit from taking a look at the code in the
InitializeComponent method called by your Form class at Design-time (as well
as at run-time). This should give you an idea of how to write good Form
control initialization code, which you will need for creating your Controls
and Forms later at run-time.

Another point here is that you should never say "I have the following code"
unless you can explain what it does. Programming is the process of writing a
set of instructions. The instructions are derived from a set of human
requirements, originally expressed as human ideas in human language.
Programming is the process of translating these humand ideas into
instructions, in a language that a computer can understand. The first
prerequisite for writing such instructions is, of course, planning what the
instructions should be in human ideas. So, when you show me some lines of
code you're having problems with, and do not tell me what they are supposed
to do, it is like showing me some Spanish that is not correctly translated,
but expecting me to know what it is supposed to mean. If I am very good at
what I do, I may be able to make an educated guess. But programming is not
guesswork, and it facilitates the communication process between us if you
can give me the ideas with the code. If you do not fully comprehend what
exactly you want to do, you shouldn't start writing code (until you have
settled that).

Now, in your first paragraph, again, there is something I do not understand:
what I'm doing is SHOWing user controls with
their parent being a panel on the main form. At design time, of course,
their parent/owner does not exist because I don't assign it until
runtime. Anyway...

There are several communication difficulties here. I understand that you do
not want to display the User Controls (which are Controls, like any other
System.Windows.Forms.Control) unless there is certain user action. What I do
not understand is your use of the term "parent/owner". A Control does not
have an Owner property. It has a Parent property, which is the Control which
contains it. This is achieved by adding the Control to the Controls
Collection of the Parent Control. Again, a look at the InitializeComponent
method (and all of the Designer code) generated by Visual Studio should
help. Let me just give you the basics:

1. A Control is declared as a field in a Form class, or as a variable in a
method.
2. The Control's properties are initialized, and the Control is
instantiated.
3. Another Control is declared in much the same way as number 1.
4. That other Control is initialized and instantiated.
5. That other Control is added to the first Control's Controls Collection.

In this way, the Form class represents a hierarchy of Controls contained
within Controls contained within Controls, and so on, with the Form itself
at the "root" of the "tree."

Controls can also be removed from the Controls Collection of other Controls.
This makes the Control disappear, which is not the same as either making it
invisible, or destroying/disposing it.

Does that help any?

--
HTH,

Kevin Spencer
Microsoft MVP
Professional Chicken Salad Alchemist

A lifetime is made up of
Lots of short moments.
 
K

Kevin Spencer

He did not mention anything about being able to reference a class without a
reference to a class instance. In fact, static objects *do* require a
reference to a class (except for static classes). They do *not* require a
reference to an *instance* of a class. If you're thinking of VB Modules, the
reference is implied, and Modules are generally something to be avoided, put
there to make the transition to true object-orientation easier for VB6
developers, and tend to break object-orieted principles of encapsulation
when misused, which is often. Static objects are problematic, and should be
used very sparingly. In BobRoyAce's case, static objects are *not*
necessary, and should be avoided.

This is a simple matter of scope. By "global" he is referring to "global to
all objects within the Main Thread" which is a Form instance. All Form
members, and the objects contained in them are global to all other objects
in the Form, one way or another, unless they are declared as private or
protected to a class instance that they reside in (other than the Form
itself), such as in a Control inside another Control in the Form. The access
modifier is all that is needed to expose them to the rest of the objects in
the Form.

--
HTH,

Kevin Spencer
Microsoft MVP
Professional Chicken Salad Alchemist

A lifetime is made up of
Lots of short moments.
 
S

Stoitcho Goutsev \(100\)

Kevin, what are you talking about?

What is static object, reference to a class?
Objects are instances of a type. they cannot be static. Classes are types
you can't have reference to a class; you have references to objects.
Firgive me for saying this, but non of the thing you said actually make
sense; at least not to me.
This is a simple matter of scope. By "global" he is referring to "global
to all objects within the Main Thread" which is a Form instance.

Unless you keep reference to the form in some static field I don't see how
the main form is global at all.

And what is "gloabal to the main thread" if something is global it should be
accesible from all threads, unless there are some speciall requirements.
 
B

BobRoyAce

I thought it was clear what the code was doing...at least clear enough.
The Main form is declaring an object (doesn't really matter what it
does for purposes of this discussion) and a public method to return it.
In the code in the UserControl I showed, it is merely trying to set a
reference to that Main form's object by calling the method which
returns it. So, my question is what am I doing wrong that is resulting
in New method of the Main form being called again when I do this?

I understand about how all the controls on the Main form are created at
run time in the InitializeComponent method. However, the user controls
I am talking about are NOT created there. They are later created, in
response to a user clicking on a NavBar item, at which time they are
shown with their Parent being set to a panel on the main form. In other
words, these user controls are NOT placed onto the panel on the Main
form at design time. If they were, I'm thinking the process of doing
what I'm trying to do would be slightly different and easier.

Do you understand?
 
K

Kevin Spencer

BobRoyAce said:
I thought it was clear what the code was doing...at least clear enough.
The Main form is declaring an object (doesn't really matter what it
does for purposes of this discussion) and a public method to return it.
In the code in the UserControl I showed, it is merely trying to set a
reference to that Main form's object by calling the method which
returns it. So, my question is what am I doing wrong that is resulting
in New method of the Main form being called again when I do this?

I understand about how all the controls on the Main form are created at
run time in the InitializeComponent method. However, the user controls
I am talking about are NOT created there. They are later created, in
response to a user clicking on a NavBar item, at which time they are
shown with their Parent being set to a panel on the main form. In other
words, these user controls are NOT placed onto the panel on the Main
form at design time. If they were, I'm thinking the process of doing
what I'm trying to do would be slightly different and easier.

Do you understand?
 
K

Kevin Spencer

Hi BobRoyAce,
I thought it was clear what the code was doing...at least clear enough.

Well, yes, as much as you posted.
The Main form is declaring an object (doesn't really matter what it
does for purposes of this discussion) and a public method to return it.

I followed that part.
In the code in the UserControl I showed, it is merely trying to set a
reference to that Main form's object by calling the method which
returns it. So, my question is what am I doing wrong that is resulting
in New method of the Main form being called again when I do this?

That is the part I could not answer. However, after going over your previous
message, I think I may have found a clue, but I'm not sure:
In MainForm.vb (for the form I named frmMain in the designer) I have

This remark was somewhat ambiguous, as you mentioned the name of the file
was "MainForm.vb" and added that "...I named frmMain in the designer"). The
reference to "the designer" was the ambiguous part, as your project probably
has many Designers in it, and you could have been talking about the Designer
for your UserControl class. After looking at that line a couple of times, I
suspect that you named the Form *class* "frmMain." Would that be correct?

If so, this could well be the cause of your app's malfunction. I can't tell
you why it misbehaved in the *way* that you described, but I *can* tell you
why it would misbehave.
In the New method of the User Control, let's say, frmEdit, I have code
as follows:
m_oStreetAddressCleaner = frmMain.GetStreetAddressCleaner

The short explanation is that you referred to the class name, not the
instance of the class. There is a huge distinction between a class and an
instance of a class. A class is a definition of what a class is. There can
be many instances of the class. An instance of a class is just a sort of
"copy" of it (it's a bit more complicated than that, but let's get the basic
stuff straightened out first). As an example, consider the following (VB)
code:

Public Sub MySub()
Dim btnA As New Button()
Dim btnB As New Button()
Dim btnC As New Button()

btnA.Text = "Button A"
btnB.Text = "Button B"
btnC.Text = "Button C"
End Sub

Note that each Button is a separate instance of the Button Class, and each
one has all the characteristics of Button, but is different, in that it has
a different Text property. Also note that you do not reference any of them
by the class name "Button."

If you did this with your Form, that would certainly cause something bad to
happen. What, I cannot say, but apparently, it caused the behavior you
observed.

Now, the rest of what I wrote was along the lines of some general good
advice which would prevent such things from happening, since I didn't see in
your message exactly what was going wrong, but did see some problematic
perceptions of things. Let me reiterate one important point all by itself:

"My point here is that there is no difference whether Visual Studio writes
the code or you do.
The rules for the code are still the same."

Now, your UserControl (I hope you didn't name it "frmEdit" because a
UserControl is *not* a Form, but the name would seem to imply that it is,
which can make things confusing later on) is just like any other Control in
the Form, just like any Control that is added to the Form in the
InitializeComponent method. The only difference is *when* it is added. Other
than that, it should be added to the Controls Collection of the Container
you want to place it in, just exactly in the same way that other Controls
are added to other Container Controls in the InitializeComponent method. My
point was (and I have done this myself before as a form of self-education),
if you look at the InitializeComponent method, you will see all of the steps
necessary to add it correctly, without causing any unforseen problems (such
as memory leaks from improper disposal), as well as seeing all of the
Microsoft Best Practices in action. It's an opportunity to learn from the
masters, and write more solid code in the process.

One final tip. Every Control has a FindForm method, which returns the
top-level Form (in your case, your instance of "frmMain") in which that
Control resides, regardless of how deeply nested it is inside any hierarchy
of Container Controls. You simply cast the return value as the type of the
Form that it is, and you can reference any public members of the form.
Here's a (VB) example:

Dim parentForm As frmMain = CType(FindForm(), frmMain)
m_oStreetAddressCleaner = parentForm.GetStreetAddressCleaner

--
HTH,

Kevin Spencer
Microsoft MVP
Professional Chicken Salad Alchemist

A lifetime is made up of
Lots of short moments.
 
B

BobRoyAce

Yes, I named the form class frmMain in the MainForm.vb file (i.e.
Public Class frmMain). Yes, I am talking about user controls and
shouldn't have mentioned the misleading name of frmEdit (just made that
up...not actually using that name...and it is misleading).

I do understand the difference between a class and instances of the
class and how confusing that distinction could cause problems. I also
agree with your point about looking at InitializeComponent as a way to
see the "right" way to do such stuff. It's kinda like recording a macro
in Microsoft Excel and then looking at the VBA code that it creates to
figure out how to do certain stuff in code, but better.

I wasn't aware of the FindForm method...thanks for that tip. However,
when I tried the code you suggested at the end of your last post, shown
below, I get an exception ("Exception has been thrown by the target of
an invocation."). The InnerException message was "Object reference not
set to an instance of an object."

Dim parentForm As frmMain = CType(FindForm(), frmMain)
m_oStreetAddressCleaner = parentForm.GetStreetAddressCleaner

I stepped through the code and found that after executing the first
line above, parentForm = Nothing. Then, a thought occurred to
me...aha...I'm executing this code in the New method or the user
control which is called before the control is ultimately assigned to
its Parent (i.e. control is instantiated/created before it is assigned
to its Parent). So, I tried putting the code in the ParentChanged event
and it worked! Now, I'm just wondering if there's a better place to put
that would make more logical sense. Any ideas?

All this being said, in the old VB6 world you could put global
variables, constants, etc. in modules outside of forms and then refer
to them from any form, etc. in the project. Is there an equivalent way
to do this in the .NET world?
 
K

Kevin Spencer

Hi BobRoyAce,

I'm wondering if you added the Control to the Panel it resides in. I believe
the FindForm looks upwards in the Controls hierarchy of the Form. That is,
each Control is added to the Controls Collection of a Parent Control (hence
the "Parent" property). Each successive Parent must also be added to the
Controls Collection of the Control it is contained in, resulting in a
hierarchy of Controls at the top of which is the Form. So, have you checked
to ensure that the hierarchy has not been broken by some Control not being
added to the Controls Collection of its Parent?

--
HTH,

Kevin Spencer
Microsoft MVP
Professional Chicken Salad Alchemist

A lifetime is made up of
Lots of short moments.
 
B

BobRoyAce

I don't believe that the "Parent chain" was broken. However, I ended up
implementing a solution by using a Module that has global objects
declared in it. Then, all of my classes, forms, controls, etc. have
access to them. Took me a while to figure it out and get there, but now
it's great!
 

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