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.
Related
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 have 2 buttons that have similar functions so I created a base button class and derived button classes.
The problem I'm having is that if I put a child button on a form, make some changes on the base button and rebuild it, the child button on the form does not reflect the changes. The new child button put on the form after the rebuild, it has the changed properties. Basically, the control that was already put on the form does not refresh after the base control is rebuilt.
I can't refresh the controls on designer since there are too many of them. I'm guessing that if a control is put on a form, it's properties are set(?). Maybe I'm not good enough with inheritance and missing something here.
I would like to know how to refresh the child controls that were put on the form before base control was modified.
UPDATE with Minimal, Complete, and Verifiable example:
Here is the example in VB.NET.
Public Class ButtonA
Public Sub New()
Me.Size = New Size(200, 200)
End Sub
End Class
Public Class ButtonB
Inherits ButtonA
End Class
After instance of ButtonB called btnB is put on a form, ButtonA's default size changes to
Me.Size = New Size(300, 300)
After the project is rebuilt, when I look back on the designer view, btnB's size is still (200, 200), not (300, 300) as it's base default size.
Size is just used as an example, it applies to any properties.
The problem is that you are changing values of properties that he designer, by design, must not use, once you have placed a control on the form. The designer allows you to put a control of a certain class in you form and, when first inserted will take a clue as to what the size should be from that control. It will then assign that size to that control within this form. You are at liberty, at any time, to modify the form by changing the size or location of any form within that form. The designer will keep track of all of this.
If you now modify the source code to have a different 'default' value for the size, then, that will not be reflected on the form that was created prior to the size being changed - that's because the control already exists on your form and the designer therefore no longer has to make make any changes to the size.
If you do want to override this behaviour you will have to remove the assignment of the Size property in the designer code (Form.Desisgner.cs - in C# or its VB equivalent). But beware - next time you fire up the designer it will probably put the size back in there.
I have a script editor that I'm using to code through the object model of software my company uses. It has a login form to make sure that you are able to access the system and a main form to pull the script from the object and edit using ScintillaNET.
The issue that I am having is when I pass the object to the main form and leave the login form open it runs smoothly but I want to close the login form after the main form is open. When I do this it stops immediately after opening the main form.
Here is a sample of my code. You can see for now that I have commented out the line Me.Close() as that seems to the issue.
' Close this form, show the main form and pass the M3 object to it
Dim f As FormMain = New FormMain
' Pass the M3 object to next form
f.M3System = M3System
f.Show()
'Me.Close()
any help would be awesome!!!
This is a built-in feature for VB.NET. Use Project + Properties, Application tab.
Change the Shutdown mode setting to "When last form closes".
I have an application with multiple win forms. I have noticed is that if the user has multiple screen displays and changes the application to one screen, the other forms that are called by other buttons, will open in the main display and not where my main application or form is.
How can I change this?
You can use the Form.CenterToParent Method on your Forms, they will then open up centered on your the creating Form, Or you can use the Screen Class to get the Bounds of the Display that your Main application is running on, then pass it to your created forms.
Edit:
On second thought assigning the Owner might just be enough, I don't have a dual monitor computer booted up at this time to test though
Dim frm As Form1 = New Form1()
frm.Owner = Me
frm.CenterToParent()
frm.Show()
Edit
Just had a chance to check it out. It was as I thought assigning the Owner to the new Form or using the Form.Show(IWin32Window) will open the new Form on the same screen as the originating Form.
frm.Show(Me)
Looks like the CenterToParent property is protected now.
According to the MSDN link
Do not call the CenterToParent method directly from your code. Instead, set the StartPosition property to CenterParent.
If the form or dialog is top-level, then CenterToParent centers the form with respect to the screen or desktop