2

In Visual Basic 2008 Express, I would like to reference a form. However, when I enter Dim mainmenu as New MainMenu, this creates a new instance. When I want to change a label in form main menu by using a button in form 2, I have to do the following:

    Dim mainmenu As New MainMenu
    Dim pitchint As Integer
    pitchint = Val(Pitch_txt.Text) 'simple way will filter out trailing non-numerics input
    If pitchint > 720 Then
        pitchint -= 720
    ElseIf pitchint > 360 Then
        pitchint -= 360
    End If

    Pitch_txt.Text = pitchint '<--put this line here will solve your "070" issue
    mainmenu.Pitchlbl.Text = pitchint
    If Pitch_txt.Text.Length <> 0 Then
        If Pitchlbl.Text <> Pitch_txt.Text Then
            Pitchlbl.Text = Pitch_txt.Text
        End If
    End If

    Dim yawint As Integer
    yawint = Val(Yaw_txt.Text) 'simple way will filter out trailing non-numerics input
    If yawint > 90 Then
        yawint -= 90
    End If
    If yawint < -90 Then
        yawint += 90
    End If

    Yaw_txt.Text = yawint '<--put this line here will solve your "070" issue

    If Yaw_txt.Text.Length <> 0 Then
        If Yawlbl.Text <> Yaw_txt.Text Then
            Yawlbl.Text = Yaw_txt.Text
        End If
    End If

This will create a new instance of the form main menu. Then, when I insert the lines mainmenu.Yawlbl.Text = yawint and mainmenu.Pitchlbl.Text = pitchint, nothing happens. I do not get an error nothing. Please help. Thanks in advance.

Dave H
  • 653
  • 12
  • 22
  • Is this code located inside a method of MainMenu? – djv Jul 16 '13 at 15:32
  • Obviously it isn't or he wouldn't have this problem. He clearly states that he is calling it from Form2. – Douglas Barbin Jul 16 '13 at 15:44
  • @DouglasBarbin He would still have the problem since he didn't Show() mainmenu. Calling the instance the same name as the class doesn't help (you probably didn't know VB is case insensitive, unlike C#). Glad to help. – djv Jul 16 '13 at 15:53
  • @DanVerdolino If application framework is enabled and MainMenu is the startup form, there is no need to call .Show() on it. – Douglas Barbin Jul 16 '13 at 16:07
  • @DouglasBarbin assuming MainMenu is the startup form. – djv Jul 16 '13 at 17:08

3 Answers3

2

My solution has been mischaracterized and there has been some confusion about the various possible approaches to answer this question, so I've edited my original post to compare and contrast the three major approaches discussed at length on this page.

Solution 1: Use VB.NET default form instances

Put this line after Dim mainmenu As New MainMenu:

mainmenu.Show()

You will probably have two MainMenu forms. This is because VB allows you to reference a static instance of a form simply by using its class name. So you can just be saying i.e. MainMenu.Property = value and it will operate on the static instance created by VB.

Try removing the line Dim mainmenu As New MainMenu. This might be all you need to do (as long as you Show() the form) since you called your reference the same as the class name.

Solution 2: Follow the Singleton design pattern (simplified version, no thread safety)

The singleton design pattern makes sure there can only be one instance of a class. Putting this code into your MainMenu will surely cause some errors to appear on your screen.

Public Class MainMenu

    ' static (shared) instance of this class
    Private Shared _instance As MainMenu

    ' function which returns the static instance
    ' with lazy initialization (constructor is called once GetInstance is 
    Public Shared Function GetInstance() As MainMenu
        If _instance Is Nothing Then
            _instance = New MainMenu()
        End If
        Return _instance
    End Function

    ' private constructor to restrict instantiation of this class (only allowed in GetInstance)
    Private Sub New()
        ' This call is required by the designer.
        InitializeComponent()
        ' Add any initialization after the InitializeComponent() call.

    End Sub

    ' all the rest of your original MainMenu code here

End Class

The way you fix the errors is by using a variable to hold a reference to the instance of MainMenu. Simply, replace mainmenu with a variable like myMainMenu and before you use the form, put this:

Dim myMainMenu As MainMenu = MainMenu.GetInstance()
myMainMenu.Show()

Solution 3: Create your own instance(s)

There are differences between solution 1 and solution 3 . In solution 1, you will only have one instance of the form if you use only default instances. This solution allows you to have any number of instances! You probably don't need this, but here goes...

You will make a new instance of MainMenu called myMainMenu again but this time you call the constructor directly.

Dim myMainMenu As New MainMenu()
myMainMenu.Show()

Wherever you call the form by the name mainmenu, replace that with myMainMenu. Did I mention that we call it myMainMenu instead of mainmenu because we don't want to use the same name as the class name? (VB is case-insensitive so mainmenu = MainMenu, but this is easily confusing because of the default instance. The compiler uses context to determine if we are talking about the class itself or the class' default instance...) Using the classname only works when you reference the default static instance as in solution 1.

The attractive thing about this solution is that you can have multiple instances of MainMenu alive at the same time. So you can put this after:

Dim myMainMenu2 As New MainMenu()
myMainMenu2.Show()

And voila, you have two MainMenu open. But you probably didn't need two!

Summary

Why are there so many methods?

Well, the first method was added to attract VB6 programmers to VB.NET because that was how it was done in VB6! To be accurate, it can be done this way in VB6, but some programmers with half a brain still chose to follow method 3. But the default instance method was so widespread because it was so easy for the casual programmer to use - especially all those laymen using it inside VBA!

The second method is preferred in some instances, but not in others. It is a simple implementation of the Singleton design pattern. Check out the link in Douglas Barbie's answer for a decent explanation of it and some examples in Java. It lists a good summary of all the times you'd need to use it - a logger which should only have once instance, a configuration loader, a factory or other instance generator - these are probably out of your scope, but maybe simply think about a Print dialog in Microsoft Office. There only needs to be one Print window open at a time. This is a good example of it. Does your code require this? Following the pattern allows for this behavior, but there is some additional configuration as I put in the example. If the App is simple enough, it probably doesn't need it.

The third is a quintessential example of object oriented programming (a good place to start is understanding OOP; this knowledge alone should give you the tools to solve problems like this in the future). It uses a class called MainMenu, which you created, and uses potentially more than one instance of the class, called objects. An advantage of this is that you can have two object instances of the same class, but the objects have properties with different values. Think two instances of class Car, one has Car.Make = "Ford", the other Car.Make = "Chevy". Both cars, but they differ by property values. This differs from the first two only by the fact that you can have multiple instances. If we want to get technical, you can merge solution 1 and 3 and use the default instance and make your own instances, but this is warned against by just about everyone who discusses it (Google "VB.NET form default instance" for more on that matter).

In the end, if you are coding something on a small scale, it's whatever works reliably for you.

djv
  • 15,168
  • 7
  • 48
  • 72
  • It's never a good idea to refer to elements, properties, member variables, etc. by referring to the form class. You might think that the form's elements are static ("Shared" in VB), but they are not (unless you explicitly declare them as such). In fact, there is an instantiation going on that VB hides from you, so each instance of the form has its own members. – Douglas Barbin Jul 16 '13 at 16:11
  • "It's never a good idea to refer to elements, properties, member variables, etc. by referring to the form class." - where did you read this? Microsoft intentionally included default instances of forms to accommodate VB6 programmers. There is nothing wrong with them if they suit the project. Using them may suit OP if he is in this camp. This solution is simple enough for any novice programmer – djv Jul 16 '13 at 16:49
  • The default instance feels a lot like `Public Shared DefaultInstance As Form1 = New Form1()` with the only noticeable difference being when it is instantiated. This method instantiates when the application starts, whereas the default instance instantiates when `Form1.Anything` is first referenced. You can't remove the "Shared" modifier because it will create infinite instances. This is why it feels static. However, I haven't been able to find an answer either way online. Using static for simplicity sake. – djv Jul 16 '13 at 17:03
  • i was working in the `console application` mode however when i made the change to `windows forms application` mode these requirement to state `dim mainform as new mainform` are no longer needed. any particular reason for this? the error when changing between the two is `Reference to a non-shared member requires an object reference.` – Oliver Hands Jul 17 '13 at 14:59
  • @OliverHands I assumed you were working with a winforms app. In Visual Studio, click the Project Menu >> *Project Name* Properties... In the Application tab, what is the "Startup form"? – djv Jul 17 '13 at 15:31
  • This is a much better answer now that it has been edited. I am changing my downvote to an upvote. – Douglas Barbin Jul 18 '13 at 12:19
  • @DanVerdolino My start up form is Mainform.@DouglasBarbin thanks for the vote much appreciated. PS it has been solved but thanks for all the help. – Oliver Hands Jul 18 '13 at 16:04
  • Please share how you solved it to help anyone with the same problem in the future. – djv Jul 18 '13 at 16:20
1

Check out the singleton pattern: http://www.oodesign.com/singleton-pattern.html

Make sure that you never instantiate more than one instance of the MainMenu form, then refer to that form whenever you want to. You may have to instantiate it at a larger scope, such as the "Main" Sub (or whatever the VB equivalent is to public static void Main() in C#).

EDIT: In a VB Windows Forms project, if you don't feel comfortable disbling the application framework, you can still instantiate any other forms from your startup form. If MainMenu is your startup form, then be sure to set MainMenu as the owner of Form2. So it might look like:

Public Class MainMenu

  Public Sub Foo()
    ' This is wherever you instantiate Form2
    Dim frm2 As New Form2()
    ' There are a few ways to declare frm2's owner:
    frm2.Owner = Me
    frm2.Show(Me)
  End Sub
End Class

Then in Form2:

Public Class Form2
  Private Sub Form2_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
    Dim pitchint As Integer
    pitchint = Val(Pitch_txt.Text) 'simple way will filter out trailing non-numerics input
    If pitchint > 720 Then
        pitchint -= 720
    ElseIf pitchint > 360 Then
        pitchint -= 360
    End If

    Pitch_txt.Text = pitchint '<--put this line here will solve your "070" issue

    ' refer to MainMenu as Form2's owner:
    Me.Owner.Pitchlbl.Text = pitchint

    ' Etc...
  End Sub
End Class
Douglas Barbin
  • 3,595
  • 2
  • 14
  • 34
  • There is no stock equivalent to C# `Main()`, and form instantiation is also handled differently between the two. – djv Jul 16 '13 at 15:41
  • 1
    @DanVerdolino Even if you were correct (which you're not), the point remains the same. You follow a Singleton pattern and only allow one instance of each form. – Douglas Barbin Jul 16 '13 at 16:02
  • 2
    @DanVerdolino You can create a `Main` method in VB, just like in C# by simply disabling the "application framework" in the project settings. Admittedly, it's probably more advanced than Oliver is comfortable with, at this point, but to say that there is "no stock equivalent" is, at the very least, misleading. – Steven Doggart Jul 16 '13 at 16:02
  • I am currently working in a predominantly VB.NET solution with application framework disabled and am using a sub Main, but as you said Oliver probably won't be doing that. It doesn't come stock with a new VB.NET app. Is that not clear? – djv Jul 16 '13 at 16:24
  • Most advise against using both methods concurrently (instantiating forms, default instances) for reasons related to your 2nd edit. So you added a label to Form2's default instance `Controls` property. I don't expect this change to be present in new instances of Form2 because it was done to a static instance, not as a modification to class Form2. See Hans Passant's answer here for a pretty good representation of VB.NET default instance in C#: http://stackoverflow.com/questions/4698538/there-is-a-default-instance-of-form-in-vb-net-but-not-in-c-why (it's static) – djv Jul 16 '13 at 18:43
  • 1
    By the way, I *always* disable application framework and create instances of forms in VB.NET, unless the scale of the project is small enough not to warrant it. But there is a time and place for both. – djv Jul 16 '13 at 18:48
  • @DanVerdolino The label that reads, "Added in code to both forms" was in fact added to both forms (Form2 and frm2), but it only shows up on one. By the way, I think you need to reexamine your definition of "static". If a class is static, then by definition, there can only possibly be one instance of that class, and it is referred to by the class name. It is literally impossible to instantiate a static class. If a member variable or method is static, then it is the same across all instances of the class. Changing it in one instance changes it in all instances of the class. – Douglas Barbin Jul 17 '13 at 00:44
  • I recommend always instantiating your Forms, because it gives you control over what happens. It follows the OOP model and it's just better practice. Referring to it by the class name (even though it is an instance of the class that VB instantiates behind your back) just feels too much like VB6, in my opinion. – Douglas Barbin Jul 17 '13 at 00:46
  • The control wasn't added to both forms. This is the last time I'll explain this: In the line `Form2.Controls.Add(label)`, `Form2` refers to the *VB generated static default instance of class Form2*, not to the class `Form2`. Hence it is only added to the static instance `Form2`. Yes, it's confusing, and this is why it's suggested by Microsoft to code either using default instances exclusively (usually for VB6 coders), **or** using user generated instances exclusively. According to the web, it has been difficult for a C# coder to comprehend. You are not alone! – djv Jul 17 '13 at 15:38
  • @DouglasBarbin I see what you mean and I must admit I was stumped at first, but I can answer this question. This has nothing to do with the default instance. Create an additional form, `Dim frm3 As New Form2` and then `frm3.Controls.Add(label)`. You will see that label is no longer on `frm2` after adding it to `frm3`. You can inspect at runtime the `frm2.Controls` collection before and after adding the label to frm3 - it disappears! When `label` is added to frm3, frm2's reference to label becomes null... or something along those lines. But it's got nothing to do with the default instance. – djv Jul 17 '13 at 21:17
  • Thank you for your suggestion. However, I am sticking with Singleton pattern as a better solution than using the VB6-style generated instance. – Douglas Barbin Jul 17 '13 at 22:01
  • @DanVerdolino I did delete the non-relevant bits, however. Feel free to downvote the other (correct) answer that is extremely similar to mine. If your answer solved his problem, he would have marked it as the answer. – Douglas Barbin Jul 18 '13 at 00:17
  • Doesn't matter to me if it's accepted. However I modified mine because this was very unclear, and there was a lot of information going around. – djv Jul 18 '13 at 00:44
0

You need to pass the reference to MainMenu form into Form2. So Form2 might look something like this (code for illustrative purposes only. Not complete):

Public Class Form2
    'This will hold a reference to the MainMenu instance
    Private mainMenuForm As MainMenu

    Public Sub New(parent As MainMenu)
        mainMenuForm = parent
    End Sub

    Private Sub SomeMethod()
        mainmenuForm.Pitchlbl.Text = "Some new value"
    End Sub
End Class

And then in the MainMenu form when you need to show Form2:

Dim frm2 As New Form2(Me)     'Pass the reference to the current MainMenu form to Form2
frm2.Show()
Chris Dunaway
  • 10,974
  • 4
  • 36
  • 48