I have a question regarding instances of userforms.
When a userform is constructed via an object in a module (e.g. set frm = new Userform2) can I hide it and restore it by the show method even when the sub is run until the end?
Let's say:
userform creation by sub1
entering values to the userform
hiding the userform with sub2
restore userform with all values by sub3 with the show method
I got really strange behavior when testing code with show and hide methods on module level or Userform Code but what I got finally to work is that using a global variable and the following code in a standard module:
Global frm As UserForm2
Option Explicit
Sub sub1()
Set frm = New UserForm2
With frm
.Show vbModeless
End With
End Sub
Sub sub2()
With frm
.Hide
End With
End Sub
Sub sub3()
With frm
.Show vbModeless
End With
End Sub
It's often said that global variables should be avoided. Is it even possible here? Do I miss something?
What I really want is to hide the userform but keep the instance with all values set before. Here is my original code and I now realized that I mixed things up and made and error. I had an "end" in a condition and this forces the UF not to show resp. killing all instances.
#Chronocidal:
In fact I hide within code within the UserForm. This is part of a button which transfers set data from UF to sheet. But I think that is not important where to hide or?
Now it is working as I expected. Thanks to all of you
Global myfrm As FormFillInformation
Option Explicit
Sub InitUserFormGeneralInformation()
Dim chkfrm As Boolean
chkfrm = CheckFrmIsHidden
If chkfrm = True Then
myfrm.Show vbModeless
End '<<<<<========== completely wrong but overseen
Else
Set myfrm = New FormFillInformation
myfrm.Show vbModeless
End If
End Sub
Function CheckFrmIsHidden() As Boolean
Dim frm As Object
CheckFrmIsHidden = False
For Each frm In VBA.UserForms
If frm.Name = "FormFillInformation" Then
CheckFrmIsHidden = True
End If
Next
End Function
Related
I have set up a new, empty, modeless userform, to fix my problem with the least amount of code involved.
For when the workbook is opened, the following code is executed to hide Excel and show the userform. It's the only code for the workbook.
Private Sub Workbook_Open()
UserForm1.Show
If Application.Windows.Count <> 1 Then
Application.Windows("test.xlsm").Visible = False
Else
Application.Visible = False
End If
End Sub
I have an empty userform with one single button. The only code for this userform is:
Private Sub CommandButton1_Click()
Application.Windows("test.xlsm").Visible = True
Application.Visible = True
Unload Me
End Sub
The last thing is a button on the first worksheet, to start the same process as when the workbook is opened. Its code:
Sub Button1_Click()
UserForm1.Show
If Application.Windows.Count <> 1 Then
Application.Windows("test.xlsm").Visible = False
Else
Application.Visible = False
End If
End Sub
Now my problem:
When I open the workbook, the userform shows up, but excel and the active window stay visible as well. However, if I click the button on the worksheet, Excel, or the window, are hidden as they should. Also, Excel, not the userform, has focus after loading everything.
The first time I ran this, it worked fine. I suspect changing the ShowModal setting in the editor screwed it up somehow, but that's just me guessing. Anyway, it doesn't work anymore as intended, no matter the modal setting now.
If I just run
Application.Visible = False
instead of the "if"-clause, Excel still stays visible and of course so does the active window.
This is driving me nuts.
What am I missing?
Edit: Link to my test file: Test File on my Dropbox
Might have to start it twice, because when the macros are blocked at startup and only activated after excel has completely loaded, the code works as intended.
Edit: I was able to test this on an excel 2010 pc and there the problem doesn't exist. So it might have something to do with the way newer Office Apps handle stuff.
I found myself having the exact same problem - modeless form opened with Workbook_Open() event that's also supposed to hide excel app (working on Excel 2016, 32bit).
The reason why it's working with UserForm property ShowModal set to True is because: the execution is suspended - it's waiting for user to interact with the UserForm that was shown.
If we change ShowModal to False (or call UserForm.Show vbModeless) then the execution is never suspended and once we reach End Sub of our Workbook_Open(), Excel appears to set Application.Visible = True on its own.
Only solution I've found thus far is this one - basically you suspend the execution by adding an infinite loop so Excel only gets to end of this event once you get rid of (Unload/Hide) the form that was shown previously.
My version looks like this:
Private Sub Workbook_Open()
Dim App As Object
Set App = startMenu
App.Show vbModeless
While App.Visible
DoEvents
Wend
End Sub
Then just to make sure that Excel is closed once that modeless UserForm is closed I've added this:
Private Sub UserForm_Terminate()
CloseApp
End Sub
Public Sub CloseApp()
ThisWorkbook.Saved = True
If Not OtherWorkbooksOpen Then
Application.Quit
Else
ThisWorkbook.Close
End If
End Sub
Public Function OtherWorkbooksOpen()
If Application.Workbooks.Count > 1 Then
OtherWorkbooksOpen = True
Else
OtherWorkbooksOpen = False
End If
End Function
EDIT:
Solution without infinite loop - schedule hiding of Excel:
Private Sub Workbook_Open()
Dim App As Object
Set App = startMenu
App.Show
If Not OtherWorkbooksOpen Then
Application.OnTime Now + TimeValue("00:00:01"), "HideExcel"
End If
End Sub
I think the userform1.show needs to be called after the execution of if statement.
Private Sub Workbook_Open()
If Application.Windows.Count <> 1 Then
Application.Windows("test.xlsm").Visible = False
Else
Application.Visible = False
End If
UserForm1.Show
End Sub
Not an answer, but I can't post this as a comment. This works for me - the user form appears, the application is hidden. I used "<>2" as I have a personal workbook. Can you confirm what happens for you?
Private Sub Workbook_Open()
If Application.Windows.Count <> 2 Then
Application.Windows("test.xlsm").Visible = False
Else
Application.Visible = False
End If
UserForm1.Show False
End Sub
I had the same issue, but I notice the form loads before the excel file. So I put a redundancy calling the application.visible = false, in the form.
Basically after clicking anything in the form, it will call the application.visible = false and the excel window will hide.
I developed many UDFs and macros in VBA for Excel 2016. One of my macros uses an Inputbox to get data used subsequently by the macro. I want to replace the Inputbox with a user form. I have created the user form, with one text box. I want to activate the user form, populating the text box with the default data, and return the text box data to the macro when OK is selected. I have searched extensively for an end-to-end example for all the the code needed to do this, with no luck. Does an example for this simple problem exist?
Add a Property to your user form. For this answer, let us use the following code within the user form.
Public Property Get MyResult() As String
' You may want to do any manipulation here
' including converting to a number, in which case the return type should be changed (*)
MyResult = TextBox1.Text
End Property
(*) If you are doing conversion, you can have another function in your user form to disable the "OK" button until they have valid convertible data in the text box.
You also want to know if they have hit "Cancel"
Public Property Get Cancelled() As Boolean
Cancelled = pCancelled ' Declare pCancelled as a Boolean in the scope of the form
End Property
Public Sub CancelButton_Click() ' Standard click event for the button
pCancelled = True
Me.Hide
End Sub
Public Sub OKButton_Click() ' Standard click event for the button
pCancelled = False
Me.Hide
End Sub
In your calling macro
MyForm.Show ' This is modal, so will block execution until a response is provided
If Not MyForm.Cancelled Then
Debug.Print MyForm.MyResult
'Do something with MyForm.MyResult
End If
UnLoad MyForm ' assuming you do not want to re-use this form as part of your logic.
There is an example of how you can pass the value to a form and get the result back. The approach uses Scripting.Dictionary object created within standard module scope and passed to userform to allow values to be changed. So it makes possible to send the default values to userform, and keep the result values in the dictionary even after the userform is closed and unloaded. You may have multiple values, just add the necessary quantity of keys to the dictionary, e. g. oData("property1"), oData("property2"), etc.
Add a standard module to the project and put the below code into it:
Option Explicit
Sub Test()
Dim oData
' Set default value and show form
Set oData = CreateObject("Scripting.Dictionary")
oData("") = "Some default text"
UserForm1.ShowForm oData
' Wait until user close form
Do While IsUserFormLoaded("UserForm1")
DoEvents
Loop
' Output returned value
MsgBox oData("")
End Sub
Function IsUserFormLoaded(UserFormName As String) As Boolean
Dim oUF As Object
For Each oUF In UserForms
If LCase(oUF.Name) = LCase(UserFormName) Then
IsUserFormLoaded = True
Exit Function
End If
Next
End Function
Add a userform module named UserForm1 to the project, place controls as shown:
And put the below code into the userform module :
Private opData
Public Sub ShowForm(oData)
Set opData = oData
Me.TextBox1.Value = opData("")
Me.Show
End Sub
Private Sub UserForm_Initialize()
If TypeName(opData) <> "Dictionary" Then Set opData = CreateObject("Scripting.Dictionary")
End Sub
Private Sub CommandButton1_Click()
Unload Me
End Sub
Private Sub CommandButton2_Click()
opData("") = Me.TextBox1.Value
Unload Me
End Sub
currently i am programming a excel macro. The macro shows a Userform.
In the Userform the User can Select something. After the User has selected something i call Userform.Hide to Hide the Userform and to read the Selection from the Form. After the selection was read i call Unload Userform. Now the Code interacts with the selection. I want to do this in a loop but when the Code trys to show the Userform the second time. I get a exception that the Form is already displayed. I cant understand it, because i called Unload Userform. When i do it in debug mode everthing works as it should.
Userform Code
Private Sub Image1_Click()
SelectCard 1
End Sub
Private Sub Image2_Click()
SelectCard 2
End Sub
Private Sub SelectCard(number As Integer)
SelectedNumber = number
Me.Hide
End Sub
Public Sub CardSelector_Activate(Cards As Cards)
Dim c As card
For Each Key In Cards.CardDictionary.Keys
Set c = Cards.CardDictionary.Items(Key - 1)
If c.value = 1 And c.played Then
Image1.Enabled = False
End If
If c.value = 2 And c.played Then
Image2.Enabled = False
End If
Next Key
number = SelectedNumber
CardSelector.Show
End Sub
Code in the ClassModule i call this in a loop
Sub Costum(Spalte As Integer, Zeile As Integer, SpalteBeginn As Integer, Cards As Cards, CardsOpponent As Cards)
CardSelector.CardSelector_Activate Cards
Dim c As card
Dim number As Integer
number = CardSelector.SelectedNumber
Set c = Cards.CardDictionary.Items(CardSelector.SelectedNumber - 1)
SetCardAsPlaced c, Zeile, Spalte, SpalteBeginn
Unload CardSelector
End Sub
Can someone help me here ?
I am not sure if I fully understand your issue, but this is how I invoke a form using VBA. This is assuming you have a Cancel and OK button:
In the form:
Option Explicit
Private m_ResultCode As VbMsgBoxResult
Private Sub btnCancel_Click()
Call CloseWithResult(vbCancel)
End Sub
Private Sub btnOK_Click()
' Store form control values to member variables here. Then ...
Call CloseWithResult(vbOK)
End Sub
Private Sub CloseWithResult(Value As VbMsgBoxResult)
m_ResultCode = Value
Me.Hide
End Sub
Public Function ShowMe(Optional bNewLayerOptions As Boolean = True) As VbMsgBoxResult
' Set Default to Cancel
m_ResultCode = vbCancel
' Execution will pause here until the form is Closed or Unloaded
Call Me.Show(vbModal)
' Return Result
ShowMe = m_ResultCode
End Function
Then, to call it (please note that frmLayers is my own VBA form object - you would use yours):
Dim dlgLayers As New frmLayers
If (dlgLayers.ShowMe(False) = vbOK) Then
' Proceeed
End If
Does this help you with your issue? I am sorry if I have misunderstood, and I will remove my answer if needed.
Things like xxxxx_Activate etc. are event handlers called by the framework. So, for example, there is an event for activate and an event for initialize. You don't normally have to directly call these yourself if you set your code up correctly. See https://support.microsoft.com/en-us/kb/138819.
I have some option buttons set up such as stp1, stp2, stp3, etc.
I have something that which is active into a cell, I want to be able to re-activate the option button via its name which is placed in the cell B6.
I tried many thing here is an example:
Worksheets("Data").Select
[B6].Value = oSel 'Option button select
oSel.Value = True 'as to re-activate the option button
I recieved the name by using:
For Each Control In Me.Controls
If InStr(Control.Name, "stp") Then
If Control.GroupName = "Beginning" Then
If Control.Value = True Then
oSel = Control.Name
Exit For
End If
End If
End If
Next
If you could help I would really appreciate it, and if I forgot some important information or you just need more... just ask. I am fairly new to VBA and I am experimenting.
Although not entirely sure what you require, this simple 'bare-bones' example should take you forward. It allows you to select a radio button on the userform which updates cell 'B6' and also put the radio button name in cell 'B6' which updates the userform. Cell 'B6' is on a sheet called 'Data'
Assumes a userform called UserForm1 and three radio buttons named stp1, stp2 and stp3 as shown. Note that for this example you should set the ShowModal property of UserForm1 to False and set the 'GroupName' property of each radio button to 'Beginning'. Run the example by selecting/running the UserForm1 module.
Put this code in a general module.
Sub chk()
Set sel = Sheets("Data").Range("B6")
With UserForm1
For Each Control In .Controls
If Control.GroupName = "Beginning" Then
If Control.Name = sel.Value Then
Control.Value = True
Exit For
End If
End If
Next
End With
End Sub
Put this code in the UserForm1 module
Private Sub UserForm_Initialize()
Call chk
End Sub
Private Sub stp1_Click()
With Me
Sheets("Data").Range("B6").Value = "stp1"
End With
End Sub
Private Sub stp2_Click()
With Me
Sheets("Data").Range("B6").Value = "stp2"
End With
End Sub
Private Sub stp3_Click()
With Me
Sheets("Data").Range("B6").Value = "stp3"
End With
End Sub
Put this code in 'Data' Sheet module
Private Sub Worksheet_Change(ByVal Target As Range)
Dim sel As Range
Set sel = Sheets("Data").Range("B6")
If Target = sel Then
Call chk
End If
End Sub
These last pieces of code may introduce you to the concept of 'events' and how to begin using them.
I have a user-form which is made up of many subs, this is assigned as a macro to a button on the worksheet. When the user is finished with this user-form they can press a button on it which causes its visibility to become false and when entered again everything appears how it was left resulting in a save like feature.
I now need to apply this to multiple buttons on the worksheet and each user form needs to have the exact same code and same buttons but be a separate form as each individual button requires it's own save like feature. The way I was planning on doing this was to copy the existing user form and paste it many times with different names however, if a modification is required it will take a long time to carry out therefore, is there a method such as "include" which could use a base module from which all the code is accessed so that if I ever need to change anything I just do it on that one module and everything else updates via the include?
EDIT:
I now have a public function called costing() and am getting an error when I used:
Private Sub material_Change()
Call costing
End Sub
You can have multiple instances of the same form. You can use this to retain multiple sets of form values
Try this:
Create your form, as usual. Let's call it MyForm
Create several buttons on your sheet. My example uses ActiveX buttons, but Form Control buttons can be used too. Let's call them CommandButton1 and CommandButton2
In your form module, include a Terminate Sub, which includes this code
Private Sub UserForm_Terminate()
' any other code you may need...
Unload Me
End Sub
The Form buton to save/Hide the form needs to be
Private Sub btnSaveAndHide_Click()
Me.Hide
End Sub
The Sheet Button code is as follows
The code is identical for each button (and calls a common Sub), and each button has its own Static form variable.)
The Error handler is needed to deal with the case a form is not properly closed. In this case the instance no longer exists, but the local Static variable is also not Nothing
Example shows form shown as Modeless, you can change this to Modal if you want.
Private Sub CommandButton1_Click()
Static frm As MyForm
ShowMyForm frm
End Sub
Private Sub CommandButton2_Click()
Static frm As MyForm
ShowMyForm frm
End Sub
Private Sub ShowMyForm(frm As MyForm)
If frm Is Nothing Then Set frm = New MyForm
On Error GoTo EH
frm.Show vbModeless
Exit Sub
EH:
If Err.Number = -2147418105 Then
On Error GoTo 0
Set frm = Nothing
Set frm = New MyForm
frm.Show
End If
On Error GoTo 0
End Sub
End result: multiple copies of the same form, each with their own values
In responce to comment How would I access the variables inside of each user form externally
In the example above the Form instances are only accessable in the Command Button Click Handler routines, or within the Form module itself. If you can write your code in the form module, then no change is needed.
To make the Form instances available elsewhere, consider moving their declaration to Module Scope of a standard Module. You could declare them as, eg individual variables, an array (either static or dynamic), a Collection, a Dictionary. Which structure is best will depend on how you want to manage and access your form instances.
For example, a Static Array: Code in a standard Module
Option Explicit
Global MyForms(1 To 2) As MyForm
Update the CommandButton code to
Private Sub CommandButton1_Click()
ShowMyForm Module1.MyForms(1)
End Sub
Private Sub CommandButton2_Click()
ShowMyForm Module1.MyForms(2)
End Sub
Private Sub ShowMyForm(frm As MyForm) no change, same as before
The code works the same as before, but you can now access the Global variable in a standard Module
Sub Demo()
Dim i As Long
For i = LBound(MyForms) To UBound(MyForms)
If Not MyForms(i) Is Nothing Then
MsgBox "Form " & i & " Value = " & MyForms(i).TextBox1.Value
End If
Next
End Sub
You don't need an "Include" (none exists in VBA); all you need to do is create a module and make the common methods public.
For example, if you create a module and have a function like this:
Public Function Add(first As Integer, second As Integer) As Integer
Add = first + second
End Function
Then you can access it like this from another module/form/class module:
Sub test()
MsgBox Add(3, 6)
End Sub