.NET Forum / Windows Forms / WinForm General / June 2006
"Global" objects
|
|
Thread rating:  |
BobRoyAce - 08 Jun 2006 07:14 GMT 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?
Kevin Spencer - 08 Jun 2006 12:58 GMT .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";
 Signature HTH,
Kevin Spencer Microsoft MVP Professional Chicken Salad Alchemist
A lifetime is made up of Lots of short moments.
>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 [quoted text clipped - 9 lines] > 3) How would I refer to them from other forms that are shown separate > from the main form? BobRoyAce - 09 Jun 2006 04:47 GMT 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?
Kevin Spencer - 09 Jun 2006 13:32 GMT > 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?
 Signature HTH,
Kevin Spencer Microsoft MVP Professional Chicken Salad Alchemist
A lifetime is made up of Lots of short moments.
> 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 [quoted text clipped - 20 lines] > 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? BobRoyAce - 09 Jun 2006 19:27 GMT 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?
Kevin Spencer - 09 Jun 2006 20:44 GMT >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 [quoted text clipped - 14 lines] > > Do you understand? Kevin Spencer - 09 Jun 2006 21:17 GMT 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
 Signature HTH,
Kevin Spencer Microsoft MVP Professional Chicken Salad Alchemist
A lifetime is made up of Lots of short moments.
>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 [quoted text clipped - 14 lines] > > Do you understand? BobRoyAce - 10 Jun 2006 04:10 GMT 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?
Kevin Spencer - 12 Jun 2006 15:53 GMT 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?
 Signature HTH,
Kevin Spencer Microsoft MVP Professional Chicken Salad Alchemist
A lifetime is made up of Lots of short moments.
> 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 [quoted text clipped - 30 lines] > to them from any form, etc. in the project. Is there an equivalent way > to do this in the .NET world? BobRoyAce - 15 Jun 2006 15:06 GMT 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!
Stoitcho Goutsev (100) - 08 Jun 2006 14:06 GMT 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.
 Signature HTH Stoitcho Goutsev (100)
>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 [quoted text clipped - 9 lines] > 3) How would I refer to them from other forms that are shown separate > from the main form? Kevin Spencer - 09 Jun 2006 13:40 GMT 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.
 Signature HTH,
Kevin Spencer Microsoft MVP Professional Chicken Salad Alchemist
A lifetime is made up of Lots of short moments.
> BobRoyAce, > [quoted text clipped - 17 lines] >> 3) How would I refer to them from other forms that are shown separate >> from the main form? Stoitcho Goutsev (100) - 09 Jun 2006 18:56 GMT 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.
 Signature Stoitcho Goutsev (100)
> 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 [quoted text clipped - 37 lines] >>> 3) How would I refer to them from other forms that are shown separate >>> from the main form?
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|