Hi i'm having to learn VB.net for a new job having previously been a C# guy. I just come across an interesting feature of VB.net. I can reference objects on a second form that has not been instatiated!
So from Form1 i can get the text property of textbox1 on Form2 as follows
Dim txt As String = Form2.TextBox1.Text
Can anyone explain how this works? Are all forms instatiated at the start of the program and then their visibility is toggled throughout the program lifespan?
Forms in VB are a special case. The compiler generates a strongly-typed list of forms in the My.Forms object of the My namespace. Each form is exposed as a property My.Forms.TheNameOfTheForm. These properties always return valid instances – i.e. if a form hasn’t been instantiated before, it will when you first use the property.
So far, so good.
But Microsoft also made the brain-dead (!) decision of importing the properties from the My.Forms object by default, everywhere, and there’s nothing you can do. Superficially, this is for backwards compatibility reasons to VB6 but that’s nonsense since VB7 (.NET 1.0) didn’t have this feature, it only came later.
But just to clarify:
Are all forms instatiated at the start of the program …?
No, luckily not. They are instantiated the first time you access the property.
Related
I'm trying to convert WinForms app into a class library - long story short the production environment I'm working in will allow our users to make changes to DLLs but not EXEs, so I'm trying to shove an entire existing app into a DLL and then just create and show an instance of the startup object/form from a second WinForms app with the goal of creating some kind of auto-update system.
I've changed the output type of the project to Class Library, added the launcher app, etc, but attempting to build the old app as a class library throws hundreds of errors, almost all of which are Reference to a non-shared member requires an object reference.
Upon inspection, these errors are appearing everywhere in the code that the startup object/form or any of its properties or methods are referenced. Since a great many things in a WinForms application naturally reference the main form... this is problematic.
Stuff like:
If DbConn = n.DbConn_.Prod Then
miParent = mainform.MiProdReq
throws the aforementioned error upon the attempt to access mainform.MiProdReq
Am I missing some simple/obvious step here?
You are referring to the default instance of the mainform type in that code. Default instances are provided by something automagically generated when building Windows Forms Application projects. Class Library projects have no such thing as default instances, so any code that tries to use them will appear to be trying to access instance members as though they were Shared.
You need to put an instance somewhere and change your code to refer to that instead. If you use a global variable, which is not ideal itself but the simplest option for where you're at, then you can just do a Find & Replace In Files to find the references you need to change.
Note that default instances are something that most experienced developers would suggest avoiding anyway. They don't exist in C# and I've never heard complaints about that, so it's hardly onerous. They were added to VB as a convenience for beginners and migrating VB6 developers who weren't used to proper OOP.
EDIT:
I haven't tested it but you may be able to use Application.OpenForms(0) to get a reference to the startup form anywhere in your library. You could, perhaps, add a module like this:
Module Module1
Private _mainform As Form1
Public ReadOnly Property mainform As mainform
Get
If _mainform Is Nothing Then
_mainform = DirectCast(Application.OpenForms(0), mainform)
End If
Return _mainform
End Get
End Property
End Module
and then your code may even just work as it is.
This question already has answers here:
Objects implicitly instantiated in vb.net?
(2 answers)
Why is there a default instance of every form in VB.Net but not in C#?
(2 answers)
Closed 7 years ago.
Short version: A class is being used without a variable of it's type being instantiated. How?
I have a huge legacy program which was converted from VB6 to VB.net.
It is compiling and many aspects work, but there is a problem related to an MDI (Multiple-Document Interface) display. I have placed other test forms under the parent MDI form, and they display correctly. The form in question does not display. (And, of course, it is the MOST important, and a most intricately complex form. I wish I could re-write it, but there is no possibility of that.)
There is a class, MDI1, which is used over 7,000 times in the code by many, many other classes. The MDI1 class is used extensively in the form which does not display. Wherever MDI1 is used, it is always referred to as simply MDI1 or Namespace.MDI1 . As far as I can tell, it is never instantiated as an object. It is as though it is a singleton, somehow, but I see nothing making it one.
The header for the class follows:
Option Strict Off
Option Explicit On
Imports VB = Microsoft.VisualBasic
Friend Class MDI1
Inherits System.Windows.Forms.Form
Dim MDI_Activated As Boolean
Public Sub New()
MyBase.New()
InitializeComponent_Renamed()
End Sub
...
Can anyone tell me what may be going on here?
Every place where it is used that I've tried to check in Visual Studio by right clicking and selecting "go to definition" takes me right back to the class definition (line 4 of the above code), and never to a variable of type
MDI1. I have searched the entire source (using both Visual Studio and grepwin outside of Visual Studio) and can find no variable instantiated with type MDI1.
I don't understand how the calls to the class are working, without a variable of that type.
Help would be greatly appreciated.
I am using Visual Studio 2010 Professional, the latest version to which I have access.
I am assuming that you are not aware of Default Form Instance in VB.NET and hence the surprise. Its a common problem since this is missing in C#.
For winforms application in VB.NET, each form has a default instance and can be referred to simply by its form name (class name). You can ofcourse create your own instances like any other class and choose not to use the default instance.
This feature is particularly useful for legacy applications that have been migrated from VB6 to VB.NET since default form instance was a norm in VB6.
You can read more about Default Form Instances here:
https://msdn.microsoft.com/en-us/library/ms233839.aspx
And
https://msdn.microsoft.com/en-us/library/aa289529(v=vs.71).aspx
I'm a bit confused on exactly how the architecture for a VB.NET forms app actually works.
I've noticed in others' code, lines that read a little like this:
If frmMain.someBooleanProperty Then
but, I'm a bit confused because someBooleanProperty is then defined as an instance variable (i.e., as Public someBooleanProperty As Boolean in frmMain). How is it being referenced statically to access an instance variable? Is this just bad coding practice?
For reasons of backwards compatibility, VB.NET automatically creates an instance of every Form. This instance is accessible through a global property which has the same name as the Form class itself.
More details can be found here:
Why is there a default instance of every form in VB.Net but not in C#?
I tried following,
on form1,
Dim removedtabs as New Collection()
and on form2 I tried to reference it as follows,
form1.ControlCollection(removedtabs)
but it gives an error.
I am new to vb.net and never used control collection.
thanks
Remember that Forms are just ordinary classes, and members of classes are Private by default. You will at very least need to use the keyword Public instead of Dim. But let's dig a little deeper. I see a couple additional points that need to be called out:
You used the Collection object. There is no good reason to ever use this object. It exists mainly for backwards compatibility with old code. You should really be using one of the generic collection types instead.
It looks like you're using the default form1 instance. Like the Collection class, these default instances exist mainly for backwards compatibility. You should really be creating your own instances of your form objects (no one ever said you could only have one copy of a form) and sending a reference to those instances to other classes.
Rather than exposing the object directly, keep the declaration private. Add a property that only has a get section to return the collection.
I am from a c# background and am converting a vb.net windows forms app to c#.
I have a windows form called associateForm.
In code the developer references associate form like so:-
Private Sub NotifyIcon1_MouseClick(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles EMS.MouseDoubleClick
If e.Button = Windows.Forms.MouseButtons.Left Then
If associateForm.WindowState = FormWindowState.Normal And associateForm.Visible = True Then
associateForm.WindowState = FormWindowState.Minimized
Else
associateForm.WindowState = FormWindowState.Normal
End If
associateForm.Show()
associateForm.Focus()
'bring to front of other windows
associateForm.Activate()
End If
End Sub
But the thing is that associateForm is not instantiated within the class where the method is being executed. Nor is the class static. Nor does there seem to be an instance of the class anywhere in code. Can anyone shine any light on why this seems to work but when i try this in C# it is not having any of it.
VB.Net for .Net 2.0 and later has something called default form instances. When you define a form, you get an automatic instance of the form with the same name as the type.
Several things are wrong with the previous comments. Joel's is basically the correct answer, with one small caveat:
When you define a form, you get an automatic instance of the form with the same name as the type.
You actually only get the instance when you call it. Before that, no default instance is created. Thus, instance creation is deferred until the time of usage.
Using the name of the form as an instance is actually a shortcut to My.Forms.Formname, which is a compiler-generated list of properties for all forms. These properties are responsible for the object creation. It is unfortunate that Microsoft has decided to make these properties implicitly available at global scope (hence the OP's question).
This feature exists because VB.NET emulates the VB6 method of being able to refer to a default instance of a form through it's name.
Well. While this feature is inspired by previous versions of VB, it's not a direct descended. VB 7 didn't have it. Rather, it was re-introduced with VB 8 (= VB 2005).
A really really stupid "feature"
Actually, it's a dead useful feature that plays very well with VB's RAD development philosophy. As mentioned above, it's unfortunate that such a loose scoping is employed. But other than that, I'd be very interested in hearing what's so “stupid” about this feature.
Joel is correct. This feature exists because VB.NET emulates the VB6 method of being able to refer to a default instance of a form through it's name.
When you convert this to C# you need to provide global access to a a single instance named associateForm. There is a chance that something is preserved in between calls to this form. After you successfully gotten it working this way. Then you can refactor the software and test to see if a local instantiation will work the same.
I can't answer your question directly, but...
I'm guessing you're converting from VB.Net to C# manually - have you tried using an automatic convertor instead?