Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question appears to be off-topic because it lacks sufficient information to diagnose the problem. Describe your problem in more detail or include a minimal example in the question itself.
Closed 8 years ago.
Improve this question
In VB, I create 2 forms in 1 project. In form1, I put 3 buttons with different value. In form2, I only put 1 textbox with no value.
My question is, how if I press one of the button in form1, the form2 is automatically opened and the value from the button that I press automatically added to the form2 textbox?
Add following code into your button handler. You can double click button and add the code into event handler which is automatically created:
'Here we are creating actual object and passing string into it constructor method
Dim instanceOfForm2 = new Form2("String value from Form1!")
instanceOfForm2.Show() ' Showing form
In Form2 we need to tweak our constructor to accept one parameter:
Public Sub New(someValue as String)
InitializeComponents() 'This is always first row in form constructor
TextBox1.Text = someValue 'And put that text into textbox...
End Sub
In VB6 you can do all the stuff that lardymonkey has in place, but you don't have to. The most concise way to do what you want is this. First, make your three command buttons in Form1 into a control array. To do this, give them all the same name (I'll use "cmdMyButtons" in my example), and set their index properties to 0, 1, and 2. Then do this in Form1's code window:
Option Explicit
Dim ButtonText(2) As String
Public Sub Form_Load()
ButtonText(0) = "First Button Text"
ButtonText(1) = "Second Button Text"
ButtonText(2) = "Third Button Text"
End Sub
Public Sub cmdMyButtons_Click(Index As Integer)
With Form2
.txtMyTextBox.Text = ButtonText(Index)
.Show vbModal
End With
End Sub
Now, I like lardymonkey's idea of showing modally, so I put it in here as well. However, several things in his code sample aren't intrinsically necessary to do what you want, and create overhead, so I pulled them out:
You don't need to make a property; you can just set the text
directly as I have here.
You don't need to create a form variable; you can just reference the form directly as I have here.
You don't have to load the form explicitly; the form gets
automatically loaded as soon as you set the variable (by the way,
the Show method also automatically loads the form, too--you only use
Load when you want to have the form loaded into memory before you do
anything to it).
If you close the modal form it will be
automatically unloaded. However, unloading a form doesn't set any
object variables referencing it to nothing. Therefore, frmDetail
will not be Nothing when you check it, you will unload a form that
isn't loaded. While this doesn't throw an error (the statement is ignored), I wouldn't do it anyway. So, you don't
need any of the "make sure the form is destroyed" code.
And now, for a short lecture on the whole business of always explicitly destroying object variables:
There is a longstanding argument about whether you need to explicitly set all your local object variables to Nothing before exiting a subroutine in VB6. I don't agree with this at all; VB takes care of this automatically when the variables go out of scope. As far as I can see, the reason that people believe that they have to do this is that the scope finalizer doesn't collect garbage in any particular order, and sometimes two interacting COM objects need to be destroyed in a particular order due to poor coupling architecture. In such a case, you do indeed need to clear the objects in the correct order to work around intermittent bugs, so the myth developed that VB's garbage collection is buggy and needs to be circumvented by always manually destroying object variables.
Frankly, the idea that a programmer is going to always do this and never forget is naive. So I persist in disagreeing with it; the developers of VB6 put a lot more thought and effort into developing the scope finalizer than any programmer is going to put into circumventing it.
Without knowing the specific version of the software you are using we cant answer, we can answer it if you provide the correct version
In .net it's a simple as creating the form then passing the value over.
Friend objForm2 as New Form2
Private Sub button1_Click(ByVal sender As System.Object, ByVal e as System.EventArgs) Handles button1.Click
objForm2 = new Form2()
TextBox1.Text = value
End Sub
This would be a way of doing it in VB6. I've left the error handling up to you.
I've made the assumption that the name of the text box is txtOutput on form2.
In Form2 add the following:
Option Explicit
Public Property Let OutputText(ByVal strOut As String)
txtOutput.Text = strOut
End Property
In Form1 add the following:
Option Explicit
Private Sub Command1_Click()
DisplayForm "1"
End Sub
Private Sub Command2_Click()
DisplayForm "2"
End Sub
Private Sub Command3_Click()
DisplayForm "3"
End Sub
Private Sub DisplayForm(ByVal strValue As String)
'Declare the variables
Dim frmDetail As Form2
'Create the form
Set frmDetail = New Form2
'Load the form and display it modal
Load frmDetail
frmDetail.OutputText = strValue
frmDetail.Show vbModal, Me
'Make sure the form is destoryed
If Not frmDetail Is Nothing Then
Unload frmDetail
Set frmDetail = Nothing
End If
End Sub
Make sure you comment the code and if you need some error handling then add it. If you need help with the VB6 functions you can find it here MSDN VB6 Reference
Related
I have a bunch of TextBox-Button pairs on a form. When the button is clicked I want to insert the value of the text box into a database. The name TextBoxes and Buttons follow a naming standard, for example Value1Tb - Value1Cmd and Value2Tb - Value2Cmd.
My problem is that since I want to do the same for every button I would like the possibility to write a Sub like:
Private Sub AnyButton_Click(sender As CommandButton)
Dim tb As TextBox
Set tb = GetTBByName(s.Name)
PutValueToDatabase(s.Name,tb.Text)
End Sub
But I cannot find a way to point the Click-event of a Button to a different sub than the standard Name_Click().
Anybody know a way around this, that doesn't involve me writing 50 or so different Name_Click() subs?
If you are OK to use Form Controls rather that ActiveX, as it looks as though you may be at the moment, then Chris' solution seems good.
However if you need ActiveX CommandButtons then you are unable (as the VBA compiler will tell you, "Procedure declaration does not match...") to have parameters in the callback for the click event, and you are unable to raise the event from multiple objects, although you do of course know which button raised the event (since the relationship is 1 CommandButton = 1 Sub).
So... I would go with something like:
Private Sub Value1Cmd_Click()
Call TheMethod(Value1Cmd)
End Sub
Private Sub Value2Cmd_Click()
Call TheMethod(Value2Cmd)
End Sub
Private Sub TheRealMethod(sender As CommandButton)
' Do your thing '
Dim tb As TextBox
Set tb = GetTBByName(s.Name)
PutValueToDatabase(s.Name,tb.Text)
' Etcetera... '
End Sub
Requires a stub for each button, so some copying and pasting to begin with, but then easy to maintain etcetera as all _Click event callbacks are pointing at the same method...
Edit:
E.g.
Sub AutoWriteTheStubs()
Dim theStubs As String
Dim i As Long
For i = 1 To 10
theStubs = theStubs & "Private Sub Value" & CStr(i) & "Cmd_Click()" & vbCrLf _
& " Call TheMethod(Value" & CStr(i) & "Cmd)" & vbCrLf _
& "End Sub" & vbCrLf & vbCrLf
Next i
Debug.Print theStubs
End Sub
It seems that what you want is to get the name of the clicked button. If you are creating buttons like this:
(where 'i' increments in a loop)
Set btn = Sheet1.Buttons.Add( , , , ,)
With btn
.OnAction = "btnSub"
.Caption = "Upadate"
.Name = "btn" & CStr(i) & "Cmd"
End With
and then defining a generic "private sub btnSub()" for all the buttons, you could at least get the name of the button that was clicked using Application.Caller. Something like:
Private Sub btnSub()
Dim ButtonName As String
ButtonName = Application.Caller
MsgBox ("Hello:" & ButtonName)
End Sub
Hope it helps!
I decided to make this an answer because I am doing something similar and I confirmed that it works.
You can store the OLEobjects in a Collection, of arbitrary size, containing Custom Class Objects that include the OLEobjects and associations and the events that you need. Thus you can completely avoid any code stubs.
Create a Custom Class to bind the Button and TextBox pairs.
Declare the Button object WithEvents.
Include your call-back in the exposed button event handler in the Class Module.
Put a Public routine in a Standard Module to initialise a Collection of these Custom Class objects by spanning the Form Controls. You can also use this to Add the controls programmatically as a 'reBuild' option. The Collection can be inside another Class Module with all of the management routines, but it needs to be Instantiated and loaded in a Standard Module.
Put a public routine in a standard module to receive the call-backs with whatever context you need. This can also be in a Worksheet Module if it makes for better encapsulation. You can use late binding to reference the callback or CallByName.
You need to bear in mind that the Module of the Form will recompile every time you add a control, so you have to be careful where you put your code.
My application has the controls directly on the Worksheet Surface, so I can't put the the Collection Class in, or source any initialisation of the Collection from the Worksheet module. This would amount to self modifying code and it grinds excel to a halt.
I dreamed this idea up through bloody-minded idealism (not necessarily a good thing) but, of course, I was not the first one to think of it as you can see here. #Tim Williams explains it in his answer. You can also google VBA Control Array Events to see plenty of similar examples including an excellent article by #SiddharthRout. In line with the VB6 analogy, he uses an Array instead of a Collection to achieve the same result.
I'll try to post some code later. My application is a bit different so it will take a lot of work to trim it down, but the principle is the same.
The other thing to bear in mind is that VBE really struggles with this type of thing so don't worry if it is loading up you processors. After you re-start with VBE off, all will be fine.
I have this same situation, and I just have a click event for every button that is a wrapper to the function I want to call. This also allows you to pass sheet-specific parameters if you need to.
Example:
Public Sub StoreButton_Click()
' Store values for transaction sheet 3/27/09 ljr
Call StoreTransValues(ActiveSheet)
End Sub
I just published (Open Source) the Event Centralizer for MSForms.
Citation: "The Event Centralizer for MSForms is a VBA programming tool that allows all sorts of custom grouping when writing handlers for the events occurring in UserForms.
With the Event Centralizer for MSForms, it is easy for example to have all TextBoxes react the same way when the Enter event occurs, or all except one, or only those with a particular Tag value.
Thanks to its events logs system, the Event Centralizer for MSForms is a powerful learning and debugging help."
I can't explain here how it works. I tried to do it on the site.
Set the event to =FunctionName(parameter).
A bit late but this may help someone else:
If you have a function called OpenDocumentById(docID as integer), then each control calling the function would have in the event the following:
cmd1's On Click event:
=OpenDocumentById([DocID])
cmd2's On Click event:
=OpenDocumentById([DocID])
etc...
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 3 years ago.
Improve this question
I have 2 forms in a program in which i want to pass the input from form 1 to form 2. I want to close form 1 after form 2 is opened so that the program stops running when all forms stop showing up on the screen; However, i won't be able to close form 1 if i want to pass the value into form 2 (If i can in classes, please let me know) so i hide it after form 2 is open. This, consequently, results in the program running forever even after form 2 is closed, form 1 staying hidden. I want to close form 1 whenever the user closes form 2 using the Close (X) button. Any idea how to implement this? Also, i have already set the program to 'When the last form closes'.
Also, i have already set the program to 'When the last form closes.
You almost there.
As I understand, you are keeping the Form1 opened but hidden to access it's members and/or objects from Form2 and that is the only reason why you are keeping Form1 opened.
You could solve this problem by creating a parameterized constructor in Form2 to pass whatever you need from Form1 before you close it.
Sub New(obj1FromForm1 As obj1Type, obj2FromForm1 As obj2Type, ....)
Me.New 'Don't forget this!
'Update Form2 accordingly...
End Sub
Where the obj1Type, obj2Type are the types of the passed objects. (Integer, String, ...etc.)
You could also declare class variables to refer to these objects if you need to access theme later somewhere in your Form2's code.
Private obj1 As obj1Type
Private obj2 As obj2Type
Sub New(obj1FromForm1 As obj1Type, obj2FromForm1 As obj2Type, ....)
Me.New
obj1 = obj1FromForm1
obj2 = obj2FromForm1
End Sub
Now back to your Form1. Make sure that you Close it once you create Form2 using its new parameterized constructor and showing it.
Private Sub Form2Caller()
Dim f As New Form2(obj1 As obj1Type, obj2 As obj2Type, ....)
f.show
Me.Close() 'and not Me.Hide nor Me.Visible = False.
End Sub
One last point. Don't pass disposable objects to Form2 that will be disposed by Form1 when you close it.
Good luck.
I am building an application using one of our vendors interfaces, which is required to keep a status message box updated.
I have some events which I have handled for testing using a Message box, but now I come to pass these messages to the display box I get nothing.
Public Shared Sub PageAirHandler(ChannelNum As Integer, Index As Integer, ChannelType As CLARITYCOMLib.ChannelType, PageName As String) Handles Status1.AirStatusChanged
MessageBox.Show(PageName)
ControlPanel.AirStatusBox.Text = PageName
End Sub
The messagebox dutifully displays the PageName string, but the textbox does nothing... even if I replace the PageName String variable with "test"
ControlPanel.AirStatusBox.Text = "test"
I get no activity, no errors, nada.
I have googled around, but every example I can find seems to show the same code.
I have recreated the textbox, tried buttons, labels and other objects with the same result.
Setting up a button click handler to update any of these works as expected.
Apologies if this is a noob blunder, but it's driving me nuts!
Using dlg As New Form2
dlg.ShowDialog()
End Using
Things like:
Form2.Show()
Form2.Show(Me)
Form2.ShowDialog()
Form2.ShowDialog(Me)
SHOUL BE AVOIDED but are possible, because Vb.net creates an implicit instance.
But the problem is, this instance gets killed if
the call has be done.
There you can see that a class instance of a project does not interact correctly
in some cases.
As Hans Passant said, your call to set the textbox text
targets the wrong instance.
Invoking to user controls is also possible with:
Me.invoke(sub()
TextBox1.Text = "Blabla"
End sub)
I am working on a project that has WebBrowsers in Other Forms;
I wrote the code below to control these WebBrowsers; but I need the code to recognize (Declare) the WebBrowsers of these forms.
Dim openForm As Form = Nothing
For Index As Integer = My.Application.OpenForms.Count - 1 To 0 Step -1
openForm = My.Application.OpenForms.Item(Index)
If openForm IsNot Me Then
MyWebBrowser.navigate("http://www.google.com/") ' PROBLEM IN THIS LINE
End If
Next
My Module created them as below:
Module MMMBrowser
Dim frmNew As New Form
Dim MekdamBrowser As New WebBrowser
Other info gleaned from comments:
there is form factory of some sort which creates new frmNew
there are many of these open at a time, which is the reason for the backwards loop thru OpenForms to find the last one.
The MekdamBrowser reference is an attempt to refer to the browser on the form.
The easy things is to provide a way for outsiders to tell the form to navigate somewhere using a new Sub, and let the form drive the browser control. This probably eliminates the need for a global MekdamBrowser reference. In the browser form add something like this:
Public Sub GotoNewURL(url As String)
myWebBrowserName.navigate(url)
End Sub
This procedure only exists for Form1 not the generic Form type, so we need to change how you find the form to use. Your existing loop is wonky. It will only ever find the last instance of a form which is not the current form. If you add a third form type, it wont work well:
Dim lastBrowserFrm As Form1 ' use the class name!
' this will try to get the last Instance of Form1
lastBrowserFrm = Application.OpenForms.OfType(Of Form1)().LastOrDefault
' LastOrDefaultcan return nothing if there are none,
' so test
If lastBrowserFrm IsNot Nothing Then
lastBrowserFrm .GotoNewUrl("www.stackoverflow.com")
Else
' create a new one, I guess
End If
Your loop was not considering that there could be other form types in the collection which are not Form1 or even if a new browser form was the last one created! This is more important now because GotoNewURL is only available on Form1 instances.
I changed the name to lastBrowserFrm to reflect what is really going one - it will just find the last one created. If you are trying to work with a specific instance, you need to provide a way to track the ones you create such as with a List(of Form1) or use the Name property so you can tell one from the other. As is, you do not a way to get back a specific form instance.
I'm running a VBA macro from SolidWorks. The form doubles as an input for two types of document. In the UserForm.Initialize subroutine I'm changing the name of the UserForm's Caption depending on which document type is open. Whenever I do this though, the program reruns UserForm.Initialize, and when it's all done, it carries on from where it left of, effectively running twice.
Does anyone know a way around this bizarre behaviour? I tried putting the FormName.Caption command into its own Sub but the result is the same.
Many thanks.
I can't replicate the problem and I don't know what SolidWorks is, so that may have something to do with it. Perhaps you can post a made-up example that shows Initialize being called twice.
My guess would be that it's related to auto-instantiating variables. When you use UserForm1, you are instantiating an object variable called UserForm1 that points to an object, also called UserForm1. It's similar to using the New keyword in a Dim statement. You never defined UserForm1 (the variable), but VBA did and the first time you use it, it instantiates automatically.
You should try to use the Me keyword when working inside the userforms class module (userforms are classes just like other objects except that they have a user interface element). In the Initialize event, say
Me.Caption = "blah"
instead of
UserForm1.Caption = "blah"
It could be (just a theory that I wasn't able to prove) that the flag that gets set to say "I'm pointing to a real instance" isn't set by the time you change the Caption property, and that by using the auto-instantiating variable UserForm1, you are forcing another instantiation.
Even better, don't use auto-instantiating variables, convenient though they are (and don't use the New keyword in a Dim statement either). You can control when your variables are created and destroyed and it's a best practice. Something like this in a standard module
Sub uftst()
Dim uf As UserForm1
Set uf = New UserForm1 'you control instantiation here
'Now you can change properties before you show it
uf.Caption = "blech"
uf.Show
Set uf = Nothing 'overkill, but you control destruction here
End Sub
Note that if the ShowModal property is set to False that the code will continue to execute, so don't destroy the variable if running modeless.
As Dick suggested, you should be able to stop the behavior by making sure to use me.caption instead of Userform1.caption.
Here's a way you can replicate the issue for those who are curious:
Create a Userform (Userform1) make sure you set ShowModal to false or you won't be able to see this.
In a module add the following:
Option Explicit
Sub ShowUserForm()
Dim uf As UserForm1
Set uf = New UserForm1
End Sub
In UserForm1 list the following code:
Option Explicit
Private Sub UserForm_Initialize()
UserForm1.Caption = "I'm UserForm1!" 'This will call the Initialize method of Userform1 not Me.
Me.Caption = "I'm Me!"
Me.Show
End Sub
Run ShowUserForm. You now have two Userforms with different captions.
Incidentally, if you have an Initialize method like I displayed adding Set uf = Nothing to the ShowUserForm sub actually fails to close either form.