I'm quite new to vb.net, and windows forms developement as a whole, so this might all be very basic, but here goes.
I would like to open a new form from some other form, and pass some selected object from a control on that form to the new form. The sensible way to do this, I thought, was as a parameter to the forms constructor. Now I know that the visual studio GUI creates partial classes for my forms, that hold the properties that I can drag onto there in the designer. I assume it also holds a default constructor. Since it might do all sorts of stuff that is needed to initialise the form, I figured I should call it from my custom constructor ala
public sub new(byval my_parameter as Foo)
Me.new()
Me.my_parameter = my_parameter
do_some_initialisation()
end sub
That clearly wasn't it, because it can't find a default constructor. The thing is, visual studio goes trough great lengths to prevent me from seeing the generated constructor, so I know how to access it. This leads me to believe that I am actually doing it wrong, and should have set out on some different path, as the path you are forced in to usually is the sensible thing to do, which I usualy find out way too late.
So how should I be doing something like this?
This is a fairly simple example.
This goes into your "main" form (the one you want to call your new form from):
Dim childForm1 As New form2Name(item)
childForm1.Text = "Title of your new form"
Call childForm1.Show()
form2Name(item) breaks up like "form2Name" is the name of the form you want to open and "item" is the parameter to be passed.
In your new form (form2Name) add this code:
Public Sub New(ByVal item As String)
InitializeComponent() ' This call is required by the Windows Form Designer.
MsgBox(item)
End Sub
You can do whatever else you need in your form.
Hope this helps.
For VB.Net I think the call you are after is
MyBase.New()
Your derived form class automatically inherits the default constructor for System.Windows.Forms.Form. This default constructor is invoked automatically before your derived constructor code executes. The reason you can't find any code for the default constructor is because the derived class does not specialize the default constructor. If you wish to define your own default constructor, you may. You can also define a constructor without parameters.
You code should work fine if you remove this line:
Me.New()
Related
I understand what the InitializeComponent() does in the background - it creates the form and all the controls that were added in the designer. However, what I have not found is WHEN you would add a Public Sub New() constructor to a form and what you would add to that versus the Load() sub.
I did find that this is a good place to put my BackColor and BackGroundColor settings since they are user-preference and stored in Settings. What else should I put in there? I have always used the Load() sub to do any work with controls. Examples: Adding handlers, loading comboboxes, setting DGV columns, loading DataTables) Should I be doing that in the New constructor? Does it make any difference?
The Load event is fired just before the form is displayed for the first time.
... but that might not be right away. It's possible — common, even, in certain environments — to create a form and have it available to code long before the form is ever shown on screen. The form might even never be shown on screen.
Take, for example, a settings form, where properties are defined in the class that map to user preference fields on the form. Someone might decide to build an application that looks directly at a known form object instance to read preferences, but if the user never goes to change anything that form might never display to the screen, and the Load event would never fire. That's just one example, and whether or not it's a good idea is another story; it's enough to know I've seen it happen in real code.
However, what I have not found is WHEN you would add a Public Sub
New() constructor to a form and what you would add to that versus the
Load() sub.
Here is an example to answer (the core?) question of yours. Suppose you have a Form to edit a product. Let's call it ProductEditForm. Your use case for this Form is to edit the values of an existing Product in your system. How would you tell this form what product to edit? By requiring the passing of a Product object when you instantiate the form. For example, in the code page of ProductEditForm:
Private _product As Product = Nothing
Public Sub New(ByVal p As Product)
' This call is required by the Windows Form Designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
_product = p
' Now, you can reference the values of passed-in product "p" via variable _product, anywhere in this Form.
End Sub
And you would instantiate ProductEditForm like this:
Dim p As New Product
p.ID = 1234
p.Name = "Widget"
p.Cost = "10.00"
p.SalePrice = "30.00"
Dim f As New ProductEditForm(p)
In this example, you are making good use of code in Sub New(). You could also, perhaps, assign the values to _product to TextBox controls, check values in _product to set colors or fonts, etc.
(Side note: this method of requiring a target object in the constructor completely avoids the way-too-common bad habit of some WinForms programmers to pass data between Forms via public form properties or global variables.)
If you're talking about Windows Forms, I've found that it's best to do your UI setup in Load rather than in the constructor (New), because sometimes if you do it in New then certain things won't be initialized yet and you'll run into null reference exceptions.
So lets say I have a form1 that opens on startup. Form1 contains a textbox named textbox1 and a button named button1. When I click button1 this is code that is called:
Class form1
Sub CapLetters () 'buttons click event
Dim myObj as new MyObject
MyObj.CapAllLetters
Textbox1.text = MyObj.GetThoseCapLetters
End sub
End class
Now, I know this is kinda dumb, but my main question is, since im creating a new object, the textbox1 will not be available in my object unless I specifically call it like:
Form1.textbox1.text
Is this good practice or is there a better way? Now in my program I have about 10 textbox and comboboxs I need my object to use. I know i could do something like this:
MyObj.CapAllLetters (textbox1.text)
But that doesn't seem like a good idea to pass that many values in a method.
I think I need a way for my object to gather all the info upon initating it?
Thoughts?
You basically seem to be asking whether it's OK to use the default instance of a form. The answer is yes. In recent versions of VB, each form that has a parameterless constructor also has a default instance, which is a single instance that can be accessed anywhere and at any time via the class name. This feature exists because it makes it easier for beginners to access forms from different places in a project without having to worry about passing references to forms around that project.
In the case of the startup form in a project, it is always the default instance of its type, assuming that you haven't disabled the application framework. That means that you can always access your startup form anywhere in your project using the default instance of its type.
Now, you'll find that experienced developers rarely use default instances. That's because they are never required and rarely add value if you know what you're doing. That means that you can always access your application's startup form is reasonable locations throughout your project without using the default instance.
I've got a form that needs to inherit a base form. The problem is that the base form has a menu strip. The form I'm working on should not have any kind of menu on it.
How can I hide the inherited menu strip? Alternatively, how can I exchange the inherited menu for something I construct myself in the new form?
In the base form class, extract the menu strip creation code from InitializeComponent() into a virtual method, and then override that method in the new form. The drawback of this is you lose visual designer support for the menu in the base form.
I found another way, which I actually ended up using.
If you don't need to show the menu again in that particular form, you can initialize all the components as usual. Then simply make a public method like HideMainMenu() in your base class, that you call after all Init's are run, which only does this:
Public Sub HideMainMenu()
Me.Menu = Nothing
End Sub
You could probably show the menu again by doing
Public Sub ShowMainMenu()
Me.Menu = Me.myPrivateMainMenu
End Sub
But I haven't tried that...
I am seeing code like "Unload frmMain" where from what I can tell frmMain is the type/module name, and I don't think it could also be simultaneously a variable name of the "ObjFrmMain" sort. Nevertheless, this command does successfully induce the form in question to unload.
So is the data type being used as an alias for its single existing instance? Or maybe for all of its instances?
Does VB6 do similar things to data types other than those derived from Form?
Yes, VB6 has odd object behavior. It gives you some shortcuts for dealing with form objects.
Load frmMain
...will load a single instance of that form under that variable name. In fact:
frmMain.lblSomeLabel.Caption = "some caption"
... will load that instance. However:
frmMain.SomeStringMember = "some value"
... will not load the form object (meaning the window itself) but you can access these variables, so in essence, the name of the form is a global variable.
You can, however, create new instances:
Dim newForm As MyForm
Set newForm = New MyForm
newForm.Show vbModal
That will actually create a new instance of MyForm, load it and show it, so you can have multiple instances of one form.
Also beware of the oddness in the New keyword:
Dim newObject As New MyClass
Set newObject = Nothing
newObject.SomeStringProperty = "some value"
This works without an "Object Reference Not Set ..." error. When you declare a reference variable using the As New syntax, you can destroy the object by setting it to Nothing and then reference that variable again and it will create a new instance.
In fact that's what's really going on with the forms. There is an implicit:
Dim frmMain As New frmMain
Personally I prefer not to use the As New syntax because it's confusing and dangerous. It also has a performance penalty, vs. this:
Dim newObject As MyClass
Set newObject = New MyClass
... but you're stuck with it for the forms.
What's happening when you call Unload frmMain is that it unloads the window (and all the controls) so all the data in those are gone, but the object frmMain is still hanging around. Therefore even after you unload it, you can still access any member variables and properties. However, if anything references any control on the form, it will trigger an implicit Load frmMain. This is the source of a lot of subtle programming errors in VB6, especially when you're trying to shut down.
Yes, it's a special functionality in VB6 and earlier. I normally tried to avoid doing it, since I saw it more as a source of confusion rather than a help.
The following comment In Visual Basic 6.0 and earlier versions, a special default instance of each form is automatically created for you, and allows you to use the form's name to access this instance. is taken from this MSDN page: Working with Multiple Forms in Visual Basic .NET: Upgrading to .NET
I would like to create a partial class for my form. I have a lot of events, and it gets messy, so I would like to break up sections into their own files.
The problem: When I create a partial class of my form, say:
Partial Public Class Form1
End Class
Visual Studio decides I need another form for this partial class.
Questions:
1. How do I create a partial class for a form?
2. If I cant do that, how can I break up all the events in my form into different files?
Yeah, it does. As soon as you drop a control on this phantom form, you'll get the design-time code (InitializeComponent) generated into that source code file. This is compatibility behavior for .NET 1.x, it didn't support the Partial keyword. Which will break the build, there are now two of them. It is somewhat avoidable with careful clicking, but you know it's going to happen sooner or later.
Other things go wrong too btw, the designer can no longer track an event handler when you move it from one file to another. And will readily let you add another, a much trickier source of bugs.
This just doesn't work very well, abandon hope of relying on it to solve your problem.
The generic diagnostic is that a convoluted user interface begets convoluted code. But that ship has sailed, no doubt. A more structural solution is pursuing the MVC model, separating the data from the view of the data. You'll still have a lot of event handlers but they won't do anything more than calling a method of a class that does the real work. Whose source code can of course live in another source code file. The typical hangup is that Windows Forms has no support whatsoever built in for this, you have to craft it by hand. Nothing similar to the MVVM model in WPF.
Something that can work well is isolating control + code into a separate UserControl. You have to do so carefully though, you don't want to have to add a bunch of properties and events that expose internal controls.
Sometimes I create partial classes for better readibility, especially when I have very large classes.
But when I click on the partial class, then the VS IDE will open the form editor showing me an empty form. If I do not care, than I could damage the main form (it seems to be a bug of VS 2008/2010)
A possibility could be using DesignerCategoryAttribute Class
Mark the partial class with the attribute "code".
<System.ComponentModel.DesignerCategory("code")>
Partial Class Form1
End Class
In this way when you click on the file, you open the class in the code editor.
Of course this will apply to all files, also to the main form file.
If you want to edit again your form in the form editor, you have to quote the attribute:
'<System.ComponentModel.DesignerCategory("code")>
Some more details here.
While it does not answer the original question, I found using regions made my code a little more manageable/readable.
#Region "RegionA"
#End Region
I orginally called this method a "hack", thus the comment below.
Not sure what you mean be "Visual Studio decides you need another form", however, are you sure the new Form1 partial class is declared in the corresponding original namespace?
All partial classes for a given .NET type must be declared in the same namespace of course (whatever files they're stored on).
I appreciate the answers given by Hans and I'm not disputing these at all. It is curious though that in Visual Studio 2010, when you create a form called say Main you get a Main.designer.vb which is a partial class. It says 'Partial Class Main' at the top. This class doesn't open a form when clicked. It also includes reference to Event Handlers. So I was wondering how do they get around this? Is there a way to create one of these 'special' partial classes that work as we would expect.
I noticed that when I created a Form Partial class, that the icon went from a class icon to a form icon. The icon associated with the Main.designer.vb file looks like a class icon with a arrow.
what worked for me (VS 2010) was naming Form1 class, already saved in Form1.vb with its own designer (Form1.Designer.vb) as:
Public Class Main 'saved in Form1.vb
VS updated the name in the designer as:
Partial Class Main 'saved in Form1.Designer.vb
then I created another "partial class" with the same name:
Partial Class Main 'saved in Main.vb
Whether I am editing Form1.vb or Main.vb VS shows me on the top navigation pan all the routines, functions, subs, even background workers and timers. For event handlers, to avoid the loophole mentioned earlier (you click on a control in the layout designer and a brand new event handler will be created in the original Form1.vb) I go:
Partial Public Class Main 'in Form1.vb file
Private Sub SomeControl_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles SomeControl.Click
Call SomeControlClick(sender, e)
End Sub
End Class
Partial Public Class Main 'then in Main.vb file
Private Sub SomeControlClick(ByVal sender As Object, ByVal e As System.EventArgs)
'blah blah
End Sub
End Class