Excel VBA - UserForm_Activate not triggering on Show - vba

I am working in Excel 2010 on a large project. Part of it checks for balanced amounts in several different files and if any of them are out of balance, a form pops up with a list of clickable links to the files in question and the expected values. Up until recently, this has been working fine, but this morning I see that the Activate procedure isn't being called when I Show the form. If I click elsewhere and then click on the form again, the Activate procedure runs as it should. It just doesn't run the first time. I even tried stepping through the code and it's not called at all.
As you can see, I didn't change the name of the default "UserForm_Activate". I will say that I have added a lot of code to the project since the last time this functionality worked, but none of that code relates to this form or the procedure that calls it. (That being said, I still plan on looking for a connection there.)
The form is set up to be non-modal. I also have another form that I use as a user interface that is also non-modal and therefore stays visible when this form pops up. The OOBItems object in the code is a Dictionary object with the expected value as the data and the file paths as the keys. LabelLink is a class that makes the links clickable.
Option Explicit
Private LinkLabel() As LabelLink
'***********************************************************************
'Name: UserForm_Activate
'Description: Adds links as clickable labels to form when it is shown.
'
'Revision History:
'Date Author Changes made
'***********************************************************************
Private Sub UserForm_Activate()
Const LABELHEIGHT As Integer = 10
Const LABELSTARTTOP As Integer = 40
Const LABELWIDTH As Integer = 428
Const LABELLEFT As Integer = 6
Const MCSLABEL As String = "Forms.Label.1"
Const BLUE As Long = 16711680 'RGB(0, 0, 255)
Const PURPLE As Long = 16711935 'RGB(255, 0, 255)
Dim Links() As String
Dim LinkStart As Integer, LinkEnd As Integer
Dim LinkCount As Integer, LinkNum As Integer
'split the tag back into links
Links() = Split(Me.Tag, "||")
LinkStart = LBound(Links)
LinkEnd = UBound(Links)
LinkCount = LinkEnd - LinkStart + 1
ReDim LinkLabel(LinkStart To LinkEnd)
'loop through links to add them as labels
For LinkNum = LinkStart To LinkEnd
'create new instance of the LabelLink class
Set LinkLabel(LinkNum) = New LabelLink
'add the label to the form
Set LinkLabel(LinkNum).mObjLbl = Me.Controls.Add(MCSLABEL)
'format the label
With LinkLabel(LinkNum).mObjLbl
.Caption = Links(LinkNum)
.Height = LABELHEIGHT
.Left = LABELLEFT
.Width = LABELWIDTH
.Top = .Height * LinkNum + LABELSTARTTOP
.Font.Underline = True
.Font.Size = 8
'trying to fix the issue of the clicked link text going back to
'blue if you click on the main form then click back to this form
If .ForeColor = PURPLE Then
.ForeColor = PURPLE
Else
.ForeColor = BLUE
End If
End With
Next LinkNum
'refresh form
Me.Repaint
End Sub
'***********************************************************************
'Name: DisplayOOBLinks
'Description: Takes an array of links and opens the form to show them.
'
'Revision History:
'Date Author Changes made
'***********************************************************************
Public Sub DisplayOOBLinks(OOBItems As Object)
Const FORMBASEHEIGHT As Integer = 72
Const LABELHEIGHT As Integer = 12
Dim LinkCount As Integer
Dim TotalsText As String
'get count of OOB items
LinkCount = OOBItems.Count
'resize userform for the number of links to show
Me.Height = LinkCount * LABELHEIGHT + FORMBASEHEIGHT
'resize the expected totals textbox for the number of OOB items
Me.Controls("ExpectedTotals").Height = (LinkCount + 1) * LABELHEIGHT
'add OOB values to expected totals textbox text
Me.Controls("ExpectedTotals").text = "Expected totals:" & vbLf & Join(OOBItems.Items, vbLf)
'preload the form so that the tag can be set
Load Me
'join the links into one string to assign to the tag since forms don't take
'any parameters
Me.Tag = Join(OOBItems.Keys, "||")
'show the form
Me.Show
End Sub
This is the entirety of the code module for the form in question.
Can anyone see why the Me.Show line would show my form, but not run the Activate procedure? I guess I could call it specifically after I show the form but I'd rather not use a workaround when I shouldn't have to.

Well, since no one (including me) could come up with anything better, I'll share my workaround and give it another few days before I close the question.
After I show the form, I call the activate procedure manually: UserForm_Activate. It works, but I'd still like to know why it isn't triggering automatically.

Related

is there a way of changing the Font properties of a text box in access VBA on a continuous form?

I am writing an app that send reports to a word document, this is done by the usage of data that is displayed on a continuous form, one form has the data that display's the selected headers that will be printed onto the report, the user can then change the font style by the usage of the windows font window.
This all works fine, what I want to do now is then update the text box on the continuous form with the font styles that have been stored in the data table, so that the user can see the font and style they have selected.
I have tried various approaches to no success, the last method I have tried I will post below in code.
dim i as integer
Private Sub Form_Load()
i = 0
End Sub
Private Sub Form_Current()
i = i + 1
Me.txtchapterName.Tag = "ctrl" & i
End sub
Function SetFieldProperties()
Dim rst As Recordset
Dim ctrl As TextBox
Set rst = Me.Recordset
Set ctrl = Me.ActiveControl
If rst.RecordCount > 0 Then
If ctrl.Tag = Me.txtchapterName.Tag Then
ctrl.ForeColor = Nz(Me![TextFontColour], 0)
ctrl.FontName = Nz(Me![TextFontName], "Calibri")
ctrl.FontSize = Nz(Me![TextFontSize], 14)
ctrl.FontUnderline = Nz(Me![TextFontAlign], 0)
ctrl.FontBold = Nz(Me![TextFontBold], False)
End If
End If
End Function
Private Sub txtchapterName_AfterUpdate()
SetFieldProperties
End Sub
To some extent this works, but it will update all of the controls on the form with the font style.
For clarity my question is in the title of the post...
Thank you all in advance.
Mark.

VBa - Userform show same combobox on two pages

So I have a multipage where I want the same button to show on both pages. I could place it outside but the border from the multipage is so ugly so I tried to place everything on the pages. Unfortunately you can't name the items (such as the combobox) with the same name. Is there a workaround to remove the borders and just show the page names or have the same name on the item on two pages?
Had some fun with this goal.
Consider this UserForm in Editor with TabStrip, 2 frames and some other controls.
Frames are named from Frame0, Frame1, etc.
Assuming the Frame0 is the location reference and first to display when UserForm is displayed, code below will be what you want.
Code:
Option Explicit
Private Sub TabStrip1_Change()
Dim i As Long, lActiveTabIndex As Long
lActiveTabIndex = Me.TabStrip1.Value
For i = 0 To Me.TabStrip1.Tabs.Count - 1
Me.Controls("Frame" & i).Visible = (i = lActiveTabIndex)
Next i
End Sub
Private Sub UserForm_Initialize()
Dim i As Long
With Me
.Height = 288 ' Adjust to your desired height
' Align all FrameX top/left to same as Frame0, delete Caption and SpecialEffect
For i = 0 To Me.TabStrip1.Tabs.Count - 1
With Me.Controls("Frame" & i)
.Top = Me.Frame0.Top
.Left = Me.Frame0.Left
.Caption = ""
.SpecialEffect = fmSpecialEffectFlat
End With
Next i
End With
' Ensure frame for first tab is displayed
TabStrip1_Change
End Sub
Userform first load (didn't save screenshot, neither the workbook, sorry).
Next tab clicked:

Scroll to view buttons where there are a varying number of buttons

I have selected a Sentence.
1) The sentence can vary.
2) I have split each word of the sentence.
The code below creates a list of Word array from Selection.
Sub Seperate_Words()
Dim WrdArray() As String
WrdArray() = Split(Selection)
For i = LBound(WrdArray) To UBound(WrdArray)
strg = strg & vbNewLine & WrdArray(i)
Next i
MsgBox strg
End Sub
Now I want to add a Search button in front of each word.
In every situation, the length of a sentence would change and Userforms are Pre-specified that's why I can't use them.
Following Image shows how output should be
Now problem I am facing is adding a scroll bar in frame which dynamically changes if needed.
I have found a very interesting solution to this:
Create a UserForm (I have named mine "frmSearchForm")
Create a Frame on it (mine is "framTest")
Create a Classmodule and name it "clsUserFormEvents"
Add this Code to it:
Public WithEvents mButtonGroup As msforms.CommandButton
Private Sub mButtonGroup_Click()
'This is where you add your routine to do something when the button is pressed
MsgBox mButtonGroup.Caption & " has been pressed" 'Just Example Code
End Sub
Then in the ThisDocument Module, add this code:
Dim mcolEvents As New Collection
Sub createButtonsOnForm()
Dim Cmd As msforms.CommandButton
'create instance of class
Dim cBtnEvents As clsUserFormEvents
'array for selection
Dim wordArr() As String
'get selection into array
wordArr = Split(Selection, " ")
Dim i As Integer
'counter for the top position of buttons
Dim topcounter As Integer
topcounter = 10
'loop through array
For i = LBound(wordArr) To UBound(wordArr) Step 1
'create button
Set Cmd = frmSearchForm.framTest.Controls.Add("Forms.CommandButton.1", "Test")
'Adjust properties of it
With Cmd
.Caption = wordArr(i)
.Left = 100
.Top = topcounter
.Width = 50
.Height = 20
End With
'Instantiate Class
Set cBtnEvents = New clsUserFormEvents
'Add cmd to event in class
Set cBtnEvents.mButtonGroup = Cmd
'Add buttonevent to collection so it won't get deleted in next iteration of the loop
mcolEvents.Add cBtnEvents
'increase the top position
topcounter = topcounter + 25
Next i
'show userform
frmSearchForm.Show
End Sub
Then if you run this sub, the selection gets splitted into the array, a button for every element is created(selection part as caption) and if you press the button, the method inside the class gets called, where you can use the mButtonGroup.Caption propery to get the value of button.
Example:
I have selected the Words "Test1" and "Test2", now when I run the Sub, the Form opens with 2 Buttons(Test1 and Test2):

Adding OptionButtons to the Userform programatically in VBA Excel

I am very new to VBA programming.
My scenario is I will get a list of String values I need these values to be displayed to the user using radio buttons on a small window so that whenever the user selects any value by clicking on the radio button I should be able to get that value in the VBA code. I searched for adding options button in the user form in the internet I got some solution which use GUI method of creating option buttons. But I need it done through program. I found a helpful thread in stackoverflow (How can I dynamically add a radio button on a form using VBA ) I used this but still I am unable to get any label or button on the user form, a plain userform will be displayed. So anybody please give information regarding this.
The code is :
Sub Button1_Click()
lResult As Variant ' this is a array which contains string vaues to be dispayed as radio button.
' Some operatin is done here to get the list of values in lResult
Dim rad As Variant
Set rad = UserForm1.Controls.Add("Forms.OptionButton.1", "radioFoo", True)
rad.Caption = "bar"
rad.Left = 10
rad.Width = 10
rad.Top = 10
End Sub
UserForm1 is the userform which I created using Insert option in VBA menu bar.
I tried to add a single button on the userform. I did not use initialize function on userform. There is button on excel sheet Button1 I am calling this function on clicking that button.
Thank you
If you have a form named UserForm1 that contains a button named CommandButton1
You can set the Initialize method for your UserForm to programmatically create a group of radio buttons
Private Sub UserForm_Initialize()
Dim OptionList(1 To 3) As String
Dim btn As CommandButton
Set btn = UserForm1.CommandButton1
Dim opt As Control
Dim s As Variant
Dim i As Integer
OptionList(1) = "Option 1"
OptionList(2) = "Option 2"
OptionList(3) = "Option 3"
For Each s In OptionList
Set opt = UserForm1.Controls.Add("Forms.OptionButton.1", "radioBtn" & i, True)
opt.Caption = s
opt.Top = opt.Height * i
opt.GroupName = "Options"
UserForm1.Width = opt.Width
UserForm1.Height = opt.Height * (i + 2)
i = i + 1
Next
btn.Caption = "Submit"
btn.Top = UserForm1.Height - btn.Height + (0.5 * opt.Height)
btn.Left = (UserForm1.Width * 0.5) - (btn.Width * 0.5)
UserForm1.Height = UserForm1.Height + btn.Height + (0.5 * opt.Height)
End Sub
Private Sub CommandButton1_Click()
Dim i As Integer
For i = 0 To UserForm1.Controls.Count - 1
If UserForm1.Controls(i) Then
SelectedOption = UserForm1.Controls(i).Caption
End If
Next
UserForm1.Hide
End Sub
If you want to pull your list from a sheet you can change
Dim OptionList(1 To 3) As String
OptionList(1) = "Option 1"
OptionList(2) = "Option 2"
OptionList(3) = "Option 3"
to pull from a range like this
Dim OptionList() as Variant
OptionList = Range("A1:A3")
In your "button_onclick()" procedure stored in a module add this code:
'This is set by the code in UserForm1
Public SelectedOption As String
Sub Button1_OnClick()
UserForm1.Show
MsgBox SelectedOption
End Sub
Which gets you this result:
And when you click submit a message box will pop up showing you which option was selected
Remember in using option buttons, your option buttons need to share the same GroupName.
Your control Name is only there for you to refer it back for changing/reading.
Your Caption is a string that appear on your userform to the users.
Your GroupName is a string that allows Excel to recognize the option buttons are linked together.
So, if opt1's GroupName is "1" while opt2's GroupName is "2", then you will be able to select both since they are in different Groups.
Private Sub UserForm_Initialize()
Dim opt1 As Control, opt2 As Control
Set opt1 = UserForm1.Controls.Add("Forms.OptionButton.1", , True)
With opt1
.Name = "radioFoo"
.GroupName = "1"
.Caption = "Option 1"
End With
Set opt2 = UserForm1.Controls.Add("Forms.OptionButton.1", , True)
With opt2
.Name = "radioFoo2"
.GroupName = "1"
.Caption = "Option 2"
.Left = 100
End With
End Sub
EDIT:
From seeing your edited post and your comment...
No, you don't need to have UserForm_Initialize() method.
It is an Excel-VBA functionality called Event.
What it's used for is specifying the userform to do something when userform is initialized (first started).
Similarly from your code, Button1_Click() is an event as well.
Since you are telling Excel to do the following at the event where Button1 is clicked by the user...
Anyways, let me briefly explain to you what option buttons do.
A group of option buttons forces the user to select only one option out of options given by the program.
And an option button in VBA only allows you to create one option. So, if you want to create 2 options, you must create 2 option buttons.
But there is just one problem: what if you want to create 2 groups of option buttons so that the user can select 2 separate options? For example, food and drinks?
VBA presents us a property of an option button called GroupName. GroupName allows VBA to distinguish between separate groups of option buttons.
Therefore, in every option button you create, it is essential that you initialize its GroupName value. If you see any implementation of option button without GroupName, you are playing with fire.
So, let's finally take a look at your code:
Sub Button1_Click()
' Some operatin is done here to get the list of values in lResult
Dim rad1 As Control, rad2 As Control
Set rad1 = UserForm1.Controls.Add("Forms.OptionButton.1", "radioFoo1", True)
rad1.Caption = "bar"
rad1.Left = 10
rad1.Width = 10
rad1.Top = 10
rad1.GroupName = "Group1"
Set rad2 = UserForm1.Controls.Add("Forms.OptionButton.1", "radioFoo2", True)
rad2.Caption = "foo"
rad2.Left = 10
rad2.Width = 10
rad2.Top = 50
rad2.GroupName = "Group1"
End Sub
Just one thing:
- As I've implicitly mentioned before, an option button with only one option does not mean anything. If you are looking for on/off kind of functionality, you might as well go with checkbox. So, I've created another option button defining it to be in the same group as the first option button you've created (rad1).
Hope it helps.
Cheers,
kpark
Be sure to select the best answer when the question/problem has been answered/solved.
Thanks.

Word crashes on removing a Shape with VBA from a header

(disclaimer: i'm not a VBA programmer by occupation)
Attached to buttons in the Ribbon I have code to toggle the company logo in a Word Document.
One button for the logo type A, a second button for logo type B and a third for no logo (logo is preprintend on paper)
First I remove the logo with removeLogo and then i add it the requested logo with setLogoAt.
The first button click is fine (e.g. for Logo Type A), a logo is added to the header of the document. When i click an other button (e.g for Logo Type B) Word crashes (probably on removing the current logo)
What is wrong with my code (or less probably: with Word?)
Sub setLogoAt(left As Integer, path As String)
Dim logoShape As Shape
Dim anchorLocation As Range
Dim headerShapes As Shapes
Set logoShape = ActiveDocument. 'linebreks for readability
.Sections(1)
.Headers(wdHeaderFooterPrimary)
.Shapes
.AddPicture(FileName:=path, LinkToFile:=False,
SaveWithDocument:=True, left:=0,
Top:=0, Width:=100, Height:=80)
logoShape.name = "CompanyLogo"
logoShape.RelativeHorizontalPosition = wdRelativeHorizontalPositionPage
logoShape.RelativeVerticalPosition = wdRelativeVerticalPositionPage
logoShape.Top = CentimetersToPoints(0.1)
logoShape.left = CentimetersToPoints(left)
End Sub
Sub removeLogo()
Dim headerShapes As Shapes
Set headerShapes = ActiveDocument.Sections(1).Headers(wdHeaderFooterPrimary).Shapes
Dim shapeToDelete As Shape
If (headerShapes.Count > 0) Then
If Not IsNull(headerShapes("CompanyLogo")) Then
Set shapeToDelete = headerShapes("CompanyLogo")
End If
End If
If Not (shapeToDelete Is Nothing) Then
shapeToDelete.Delete
End If
End Sub
edit
I steped trough my code. All is fine until I reach the line shapteToDelete.Delete in removeLogo. Here Word crashes hard, even while debugging. I'm using Word 2007 (and that is a requirement)
edit2
I cleared all macros, all normals.dot, all autoloading templates, then created a new document with the two routines above and this test method:
Sub test()
setLogoAt 5, "C:\path\to\logo.jpg"
removeLogo
setLogoAt 6, "C:\path\to\logo.jpg"
End Sub
When I run test it crashes in removeLogo at shapeToDelete.Delete.
Edit 3
I 'solved' the problem by first making the headers/footers view the active view in Word, then deleting the Shape and then returning to normal view. Very strange. It works but as a programmer I'm not happy.
Another potential solution is to try and select the shape first and then delete the selection:
shapeToDelete.Select
Selection.Delete
You would probably want to switch off screen updating if this works, else you'll get flickering as Word moves around the document.
I've experienced this problem before and normally with an automation error: "The object invoked has disconnected from its clients". I haven't yet found a solution.
However a good workaround is to hide the shape rather than delete it.
So:
shapeToDelete.Visible = False
This works:
I only have 2 boxes to hide so this isn't generic
Private Sub btnPrint_Click()
Dim hdrShapes As Shapes
Dim S As Shape
Dim aTohide(2) As String
Dim iNdx, i As Integer
iNdx = 0
' Hide buttons and print
Set hdrShapes = ActiveDocument.Sections(1).Headers(wdHeaderFooterPrimary).Shapes
' GET BUTTON NAMES (ACTUALLY TEXT BOXES
For Each S In hdrShapes
If S.Type = msoTextBox Then
aTohide(iNdx) = S.Name
iNdx = iNdx + 1
End If
Next
' now hide , use the arrays as the for each statement crashes
For i = 0 To 1
hdrShapes(aTohide(i)).Visible = msoFalse
Next
' print it
With ActiveDocument
.PrintOut
End With
' and unhide the buttons
For i = 0 To 1
hdrShapes(aTohide(i)).Visible = msoTrue
Next
Set hdrShapes = Nothing
End Sub