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...
Related
I have a form, when I right click it, you can select properties, which opens a settings Form that interacts directly with the previous form, ie, it can change the color, size and other properties of the the original form. I want Several Copies of the original form running.
As it Stands, all the new forms take their settings from the same My.settings.
1st How Can I Save all the settings from each copy of the original form, Separately?
right now im using Form1.show(), and dim newform as form1 = new form1.
2nd, how can I make sure the settings form is interacting only with their respective original form?
right now, all the new settings Forms are only interacting with the same original form.
I have a jumble of data types saved, about 10 settings, and 2 of them are Specialized.string to save matrixes
Firstly, I recommend not using My.Settings in this case, since it only serves the purpose of storing global settings.
You'll need to store the settings in the Form1 class for each instance seperately, these could then be saved to a file or in the registry.
To your second question, in the settings form class, add a parameter to the Form.Show method, which is used to pass the instance of a main form object to the settings form.
This will tie a newly opened settings form to a particular main form. This could look like this:
Class SettingsForm
Shadows Sub Show(parentForm As Form1)
'The parentForm paramter will be the Form1 instance which will be controlled by this settings window
End Sub
End Class
You then just call the SettingsForm.Show method and pass the instance of the Form1 object you want to control to the method.
My problem pertains to a COM add-in for Microsoft Excel. The use case is as follows: 1. User clicks the add-in's button on the ribbon. 2. A form window appears. 3. User interacts with the form window and clicks an OK button. 4. Various reports are generated, while a progress bar on the form window shows progress. 5. At the end of the process, the form window closes.
The process works as designed on the first run, but after the form window has been closed there is no way to start a new "session." From the user's perspective the add-in button becomes non-responsive. When run in debug mode from Visual Studio, clicking the add-in button a second time generates an error message: "Cannot access a disposed object."
Clearly something is wrong with the way I have hooked everything up, but I haven't been able to find a simple description of how to do it correctly. Here is what I have:
In a public class a number of public (or "global" variables) are declared; the form is also declared and instantiated here:
Public Class GlobalVariables
Public Shared FormInstance As New MyFormDesign
End Class
The reason for declaring the form as a public object is to be able to be able to send progress values from various different subs and functions. The GlobalVariables class is imported by all modules that require it.
Behind the ribbon button is a single line of code:
FormInstance.Show()
Clicking the button instantiates and shows the form as intended. To keep things simple we can ignore the bulk of the code; simply clicking the "Cancel" button will trigger the problem. The code behind the "Cancel" button is straightforward:
Me.Close()
GC.Collect()
After closing the form it is no longer possible to create a new instance, per the error message cited above.
I don't really understand what is going on here, but it looks to me like the GlobalVariables class, once created, persists until the end of the Excel session. If that is correct the problem could presumably be cured by instantiating the form in a standard module. Instead of attempting to revive a form that has been disposed, the add-in would just create a new instance each time the user clicks the button. But if I go that route I can't figure out how to send progress values from other subs back to the form. It seems like a Catch-22.
There has got to be a way to both (a) create the form as a public object, and (b) create and destroy a new instance each time the add-in is run. Right? What am I doing wrong?
It has been a long journey, but I finally found out how to build the functionality described in my question. I will post the answer here, as it may help others in the future.
The challenge was to declare a form as Public in order to make it accessible throughout the project, so that subs and functions can send progress updates back to the form.
The problem was that the form, when declared and instantiated as described in my question, can only be created once per Excel session.
The solution is to declare the form as Public without instantiating it, then access it via a Public ReadOnly Property.
First, declare FormInstance as a public variable without instantiating it:
Public FormInstance As MyFormDesign
Second, define a Public ReadOnly Property. This establishes a way to call the form:
Public ReadOnly Property CallMyForm() As MyFormDesign
Get
Return FormInstance
End Get
End Property
Third, the ribbon button's Click event instantiates and shows the form:
Private Sub Button1_Click(sender As Object, e As RibbonControlEventArgs) Handles Button1.Click
FormInstance = New MyFormDesign
FormInstance.Show()
End Sub
Now a new form instance will be created each time the ribbon button is clicked, but the form can still be accessed via the CallMyForm property.
Instead of ...
FormInstance.BackgroundWorker1.ReportProgress(i)
... we will use:
CallMyForm.BackgroundWorker1.ReportProgress(i)
This solves the conundrum laid out in the question.
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.
I am looking for a way to be able to use the tab control on my main form and transfer all 3 other forms onto that main form that has the tab control. Problem being is that some functions are named the same in each of the septate forms i want to bring into just one for the tab control.
Is there a way to separate the code for each form ON the main form of the tab control so i don't have to rename/recode each of the 3 forms i want to place on the main form of the tab control?
As an example:
Form1 has a function called upDateMe()
form2 has a function called upDateMe()
So now the main form would look like this:
Friend Class frmPW
Function sub upDateMe()
blah blah
End Function
Function sub upDateMe()
blah blah
End Function
so when i put both form1 and form2 code on the main form where the tab control is, its going to tell me that i have a duplicate function name.
Thanks for any help!
David
Why not add the what as a parameter to the update-sub?
I don't know what the functions are doing, but assuming that they load some data and bind Controls to it, you could provide a parameter that you can use to separate the logic/datasource.
Another possibility would be to simply rename the subs so that you can distinguish their purpose.
Last but not least you could encapsulate the whole business logic and databinding in one class/UserControl and instantiate it for every ex-form.
Consider User Controls.
There are plenty of examples available. You might want to start with this walkthrough on MSDN:
Walkthrough: Authoring a Composite Control with Visual Basic
One thing to watch for: The project type you'll create is actually 'Windows Forms Control Library', not 'Windows Control Library' like the 'Creating the Project' section, step 2 says.
You would create a User Control for each form in your project. Each of these UCs can be added to a tab on your main form and can be treated like any other standard Windows control.
For example - you can create a UC for your form1 called ctlForm1 and add it to Visual Studio's Toolbar. You can use the WinForm designer to drop an instance of your control in a tab page on your new main form and call it, say, ucForm1. To call the UC's upDateMe method you would simply call:
ucForm1.upDateMe()
You can thne create a UC for your form2, add it to your Toolbar and drop an instance of it on a different tab page on your main form (calling that instance ucForm2). You can call that control's upDateMe method like this:
ucForm2.upDateMe()
You'll want to learn about User Controls; they're a great way to encapsulate logic, events and UI elements into descrete, reusable components.
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