My Action Pane keeps popping up at start when I open my Excel VSTO document-level even though I forced it to hide - excel

I created an "Excel VSTO document-level" project in Visual Studio. I have a pre designed "Action Pane" and a Tab Ribbon with a button in it to show my action pane.
My problem is my action pane keeps popping up (show) at least 1 time and quickly disappears before I do anything.
Here is my code so far in my ribbon:
Imports Microsoft.Office.Tools.Ribbon
Public Class Ribbon1
Dim actionsPane1 As New ActionsPaneControl1()
Private Sub Ribbon1_Load(ByVal sender As System.Object, ByVal e As RibbonUIEventArgs) Handles MyBase.Load
Globals.ThisWorkbook.ActionsPane.Clear()
Globals.ThisWorkbook.ActionsPane.Controls.Add(actionsPane1)
actionsPane1.Hide()
Globals.ThisWorkbook.Application.DisplayDocumentActionTaskPane = False
End Sub
Dim boolAP1toggle As Boolean = True
Private Sub Button1_Click(sender As Object, e As RibbonControlEventArgs) Handles Button1.Click
If boolAP1toggle = True Then
Globals.ThisWorkbook.Application.DisplayDocumentActionTaskPane = True
actionsPane1.Show()
boolAP1toggle = False
Else
Globals.ThisWorkbook.Application.DisplayDocumentActionTaskPane = False
actionsPane1.Hide()
boolAP1toggle = True
End If
End Sub
End Class

There is no need to show or hide methods of the pane class. Instead, you need to rely on the DisplayDocumentActionTaskPane property which is set to true to display the Document Actions task pane; set to false to hide the Document Actions task pane.
Private Sub Button1_Click(sender As Object, e As RibbonControlEventArgs) Handles Button1.Click
If boolAP1toggle = True Then
Globals.ThisWorkbook.Application.DisplayDocumentActionTaskPane = True
boolAP1toggle = False
Else
Globals.ThisWorkbook.Application.DisplayDocumentActionTaskPane = False
boolAP1toggle = True
End If
End Sub

I found my mistake:
Globals.ThisWorkbook.ActionsPane.Controls.Add(actionsPane1)
will inevitably show Actions Pane at the time when you try to add any form to it, so I needed to add it my Button1_Click event handler

Related

Grey out optionbuttons when a specific option button is selected

I asked a similar question this morning and i got a great answer, but then i pushed a little bit further and this is where i'm kinda stuck.
This is my initial post : Unselect an entire optionbutton group if another optionbutton outside the group is selected
But now, I would like to have this:
If OptionButton1.Value = True or OptionButton2.Value = True then Grey out OptionButton4 to OptionButton11 (Also gave it a group name : "Category").
But, If OptionButton3.Value = True, then it ungreys the OptionButton4 to OptionButton11 (Group Category).
EDIT:
I did something and it worked but once I run my userform and generate a number (userform purpose), the optionbuttons stay greyed out. Here's the code i used :
Private Sub OptionButton1_Click()
OptionButton4.Enabled = False
OptionButton5.Enabled = False
OptionButton6.Enabled = False
OptionButton7.Enabled = False
OptionButton8.Enabled = False
OptionButton9.Enabled = False
OptionButton10.Enabled = False
OptionButton11.Enabled = False
End Sub
Private Sub OptionButton2_Click()
OptionButton4.Enabled = False
OptionButton5.Enabled = False
OptionButton6.Enabled = False
OptionButton7.Enabled = False
OptionButton8.Enabled = False
OptionButton9.Enabled = False
OptionButton10.Enabled = False
OptionButton11.Enabled = False
End Sub
EDIT 2:
My solution for now is to add
Unload Me
UserForm1.Show
so it reset my userform and remove the greyed optionbuttons
That looks like you solved your own issue. Another would be to create a validation sub that would actually check everything every time an option is selected. Also there is a way to access all the forms controls something like Forms('myform').controls('mycontrol') I admit I am rusty but if you iterate through you can disable enable as wanted and call that on load
Instead of unloading and reloading the form, handle the enabling/disabling.
Create a common procedure to handle the enabling/disabling so that you do not have to duplicate the code.
You can loop through all the controls which will not only reduce the lines of the code but will be easier to maintain.
Is this what you are trying?
Option Explicit
Private Sub OptionButton1_Click()
EnableOptBtns False
End Sub
Private Sub OptionButton2_Click()
EnableOptBtns False
End Sub
Private Sub OptionButton3_Click()
EnableOptBtns True
End Sub
Private Sub EnableOptBtns(enable As Boolean)
Dim ctl As Control
Dim i As Long
For i = 4 To 11
Me.Controls("OptionButton" & i).Enabled = enable
Next i
End Sub

VBA Userform works when run directly but not when run through sub

I have a UserForm that prompts the user to both to select a file (Application.FileDialog(.soFileDialogOpen)) and to click a couple of options (various checkboxes). Both of these are required, so I want the OK button to only be enabled if a file has been selected and at least one checkbox has been clicked. I have a sub (CheckAndEnable) that runs both after a file is selected and after any checkbox is clicked (I am using a class to handle this).
Here is a very simplified version of the userform code. The userform has a button called buttonOK, a button that selects a file buttonSelectFile, and a variable number of checkboxes.
Option Explicit
Dim colChkboxes As Collection
Dim intchoice As Integer, AtLeastOneChecked As Boolean, strPath As String
Private Sub buttonOK_Click()
Hide
End Sub
Private Sub buttonSelectFile_Click()
Dim intchoice As Integer
Application.FileDialog(msoFileDialogOpen).AllowMultiSelect = False
intchoice = Application.FileDialog(msoFileDialogOpen).Show
If intchoice <> 0 Then
strPath = Application.FileDialog(msoFileDialogOpen).SelectedItems(1)
labelPath.Caption = strPath
End If
CheckAndEnable
End Sub
Public Sub CheckAndEnable()
Dim ctrl As Control
' checks all checkboxes to determine if at least one is checked
AtLeastOneChecked = False
For Each ctrl In Me.Controls
If TypeName(ctrl) = "CheckBox" Then
If ctrl.Value = True Then
AtLeastOneChecked = True
Exit For
End If
End If
Next ctrl
' enable the OK button if file selected and at least one checkbox clicked
If (AtLeastOneChecked = True) And (Not IsEmpty(strPath)) And (strPath <> "") Then
buttonOK.Enabled = True
Else
buttonOK.Enabled = False
End If
End Sub
Private Sub UserForm_Initialize()
buttonOK.Enabled = False
' declare vars
Dim ctrl As Control
Dim obj As clsCheckBox
Set colChkboxes = New Collection
' set each checkbox to CheckBox Class that handles if checkbox is
clicked.
For Each ctrl In Me.Controls
If TypeName(ctrl) = "CheckBox" Then
Set obj = New clsCheckBox
obj.AssignClicks ctrl
colChkboxes.Add obj
End If
Next ctrl
End Sub
I also have a Class Module clsCheckBox with the following code, which calls CheckAndEnable whenever a checkbox is clicked.
Private WithEvents chkbox As MSForms.CheckBox
Public Sub AssignClicks(ctrl As Control)
Set chkbox = ctrl
End Sub
Private Sub chkbox_Change()
Call MyUserform.CheckAndEnable
End Sub
When I run the UserForm directly, everything works beautifully. The problem is that when I call the userform in a module (this userform is part of a series of userforms in a larger script), the CheckAndEnable script runs when checkboxes are clicked but doesn't enable the OK button as it is supposed to. I have researched this extensively but haven't been able to find anything. Any help would be appreciated!
When you call MyUserform.CheckAndEnable in the chkbox_Change() sub of clsCheckBox, it runs the CheckAndEnable script for the default instance of the UserForm. Since the module showing the userform was creating a non-default instance of the form, the CheckAndEnable script was failing because it had no knowledge of what checkboxes/variables/etc had been changed in the non-default version.
Thanks to Mathieu Guindon for the solution. See UserForm1.Show for more details.

VB right click copy/paste in multipage

Let me preface my question with the fact that I am self taught, so please provide as much detail as possible and bear with me if I need you to explain differently or multiple times.
I created a notation/email generating tool for my team using Microsoft Visual Basic 7.0. The only complaint that I received on it was that many of them are not used to hot keys so they depend on using the mouse but right click didn't work. I was able to find code that creates a pop-up for copy and paste when they use right click, and it works great on the few textboxes that are on the main form itself, however it does not work on the majority of the textboxes as they are in a Multipage.
Does anyone know how to alter the below code to work for textboxes on a Multipage? Also, before it is suggested, I did toy with the idea of moving everything out of the Multipage, however that format is the easiest as there are multiple stages and types of notes/emails that they would need to send at any time, so having tabs available for them to simply click is the most user friendly that I was able to create and that they all agreed on.
Thank you all so much in advance!
Code in the form:
Dim cBar As clsBar
Private Sub UserForm_Initialize()
On Error GoTo Whoa
Application.EnableEvents = False
Set cBar = New clsBar
cBar.Initialize Me
Letscontinue:
Application.EnableEvents = True
Exit Sub
Whoa:
MsgBox Err.Description
Resume Letscontinue
End Sub
Code in a Class Module:
Option Explicit
'Popup objects
Private cmdBar As CommandBar
Private WithEvents cmdCopyButton As CommandBarButton
Private WithEvents cmdPasteButton As CommandBarButton
'Useform to use
Private fmUserform As Object
'Control array of textbox
Private colControls As Collection
'Textbox Control
Private WithEvents tbControl As MSForms.TextBox
'Adds all the textbox in the userform to use the popup bar
Sub Initialize(ByVal UF As Object)
Dim Ctl As MSForms.Control
Dim cBar As clsBar
For Each Ctl In UF.Controls
If TypeName(Ctl) = "TextBox" Then
'Check if we have initialized the control array
If colControls Is Nothing Then
Set colControls = New Collection
Set fmUserform = UF
'Create the popup
CreateBar
End If
'Create a new instance of this class for each textbox
Set cBar = New clsBar
cBar.AssignControl Ctl, cmdBar
'Add it to the control array
colControls.Add cBar
End If
Next Ctl
End Sub
Private Sub Class_Terminate()
'Delete the commandbar when the class is destroyed
On Error Resume Next
cmdBar.Delete
End Sub
'Click event of the copy button
Private Sub cmdCopyButton_Click(ByVal Ctrl As Office.CommandBarButton, CancelDefault As Boolean)
fmUserform.ActiveControl.Copy
CancelDefault = True
End Sub
'Click event of the paste button
Private Sub cmdPasteButton_Click(ByVal Ctrl As Office.CommandBarButton, CancelDefault As Boolean)
fmUserform.ActiveControl.Paste
CancelDefault = True
End Sub
'Right click event of each textbox
Private Sub tbControl_MouseUp(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
If Button = 2 And Shift = 0 Then
'Display the popup
cmdBar.ShowPopup
End If
End Sub
Private Sub CreateBar()
Set cmdBar = Application.CommandBars.Add(, msoBarPopup, False, True)
'We’ll use the builtin Copy and Paste controls
Set cmdCopyButton = cmdBar.Controls.Add(ID:=19)
Set cmdPasteButton = cmdBar.Controls.Add(ID:=22)
End Sub
'Assigns the Textbox and the CommandBar to this instance of the class
Sub AssignControl(TB As MSForms.TextBox, Bar As CommandBar)
Set tbControl = TB
Set cmdBar = Bar
End Sub
Get ActiveControl name on a Multipage control
It's necessary to know the multipage's selected Page via a helper function (ActiveControlName) using SelectedItem property and getting the control (its name) from there. Change your button click events as follows:
Relevant button click events in class module clsBar
'Click event of the copy button
Private Sub cmdCopyButton_Click(ByVal Ctrl As Office.CommandBarButton, CancelDefault As Boolean)
Dim sACN As String
sACN = ActiveControlName(fmUserform) ' find control's name
' Debug.Print sACN & ".Copy"
fmUserform.Controls(sACN).Copy ' << instead of fmUserform.ActiveControl.Copy
CancelDefault = True
End Sub
'Click event of the paste button
Private Sub cmdPasteButton_Click(ByVal Ctrl As Office.CommandBarButton, CancelDefault As Boolean)
Dim sACN As String
sACN = ActiveControlName(fmUserform)
' Debug.Print sACN & ".Paste"
fmUserform.Controls(sACN).Paste ' << instead of fmUserform.ActiveControl.Paste
CancelDefault = True
End Sub
Helper function called by above click events
Function ActiveControlName(form As UserForm) As String
'cf Site: https://stackoverflow.com/questions/47745663/get-activecontrol-inside-multipage
'Purpose: get ActiveControl
Dim MyMultiPage As MSForms.MultiPage, myPage As MSForms.Page
If form.ActiveControl Is Nothing Then
' do nothing
ElseIf TypeName(form.ActiveControl) = "MultiPage" Then
Set MyMultiPage = form.ActiveControl
Set myPage = MyMultiPage.SelectedItem
ActiveControlName = myPage.ActiveControl.Name
Else
ActiveControlName = form.ActiveControl.Name
End If
End Function
Side note
Suggest to check for the length of selected text strings in case of empty strings to prevent from unwanted results.

Enable/Disable Ribbon Controls Independently

I've been searching many times for a solution to this and the closest solution is Ron de Bruin example but it does not cover what I need.
I am trying to do is essentially 2 things
Example: I have 4 buttons.
Button1 on Group1 and tab1
Button2 on Group1 and tab1
Button3 on Group2 and tab2
Button4 on Group2 and tab2
When the ribbon control "Button1" is clicked it will run some code and disable the "Button1" at the end.
When the ribbon control "Button2" is clicked it will run some code and the check the "Button3" and "button4" and set the opposite properties.
If button3 was true, it becomes false.
If button4 was false, it becomes true.
Can you provide some guidance to solve this
Ok, all buttons have GetEnabled events, so when ribbon activates/updates - event fired! (simple).
Callback function to this event looks like this:
Sub Button_GetEnabled(control As IRibbonControl, ByRef enabled)
'(enabled = true to enable)
enabled = EnableButtons
End Sub
So lets start! In your module with callbacks functions you need a global (global to callbacks) boolean smth like EnableButtons.
And when ribbon loads this code example fires setting flag to True:
Private Sub OnRib_Load(ribbonUI As IRibbonUI)
Set MyRibbonUI = ribbonUI
EnableButtons = True
End Sub
And on each button you need callback for GetEnabled event described above.
After that - all buttons are enabled! So what we can make here? Lets look on OnAction callback to your desired button:
Sub Button_Click(control As IRibbonControl)
EnableButtons = False
MyRibbonUI.Invalidate
'do some stuff - buttons disabled
EnableButtons = True
MyRibbonUI.Invalidate
End Sub
So Invalidate method "updates" all controls. You can try to InvalidateControl desired control (which is a more preferable way, than Invalidate, due to performance), but I think that more elegant way is to place callbacks and events only on buttons you want!
So, finally, you need reference to ribbon, boolean flag, and _GetEnabled events.
More here
Explantion provided by Commonsense help me to build that final solution for the revised question asked. Thanks
Option Explicit
Public MyRibbonUI As IRibbonUI
Public EnableButton1 As Boolean
Public EnableButton2 As Boolean
Public EnableButton3 As Boolean
Public EnableButton4 As Boolean
Public Sub OnRib_Load(ribbon As IRibbonUI)
'
' Code for onLoad callback. Ribbon control customUI
'
Set MyRibbonUI = ribbon
EnableButton1 = True
EnableButton2 = True
EnableButton3 = True
EnableButton4 = False
End Sub
Public Sub Button_GetEnabled(control As IRibbonControl, ByRef Enabled)
'
' Code for getEnabled callback. Ribbon control button
'
Select Case control.ID
Case "Button1"
Enabled = EnableButton1
Case "Button2"
Enabled = EnableButton2
Case "Button3"
Enabled = EnableButton3
Case "Button4"
Enabled = EnableButton4
End Select
End Sub
Public Sub Button1_onAction(control As IRibbonControl)
'
' Code for onAction callback. Ribbon control button
'
EnableButton1 = False
MyRibbonUI.InvalidateControl ("Button1")
End Sub
Public Sub Button2_onAction(control As IRibbonControl)
'
' Code for onAction callback. Ribbon control button
'
If EnableButton3 = False Then
EnableButton3 = True
Else
EnableButton3 = False
End If
MyRibbonUI.InvalidateControl ("Button3")
If EnableButton4 = False Then
EnableButton4 = True
Else
EnableButton4 = False
End If
MyRibbonUI.InvalidateControl ("Button4")
End Sub
Public Sub Button3_onAction(control As IRibbonControl)
'
' Code for onAction callback. Ribbon control button
'
End Sub
Public Sub Button4_onAction(control As IRibbonControl)
'
' Code for onAction callback. Ribbon control button
'
End Sub

Excel VBA: Toggle Buttons only work on right click

I'm working in Excel on a Userform. Essentially, I want a "Photoshop-esque" toolbar that floats over my spreadsheet while I work, allowing me to select and use various tools.
I've got a series of toggle buttons set up so that when one is clicked, any other toggle buttons go back to unclicked. It's a little more complicated because I have sub-buttons, if that makes sense, meaning that once I have a button clicked, I can click one of four other buttons to make my actual selection, and these four buttons are mutually exclusive from each other as well.
The weird thing: I haven't been able to get these buttons to work. Except. For some reason, when I right-click only, the buttons work like a charm. Left-click: nothing. Help please?
Sample button code:
Private Sub tMouse_MouseUp(ByVal button As Integer, _
ByVal shift As Integer, ByVal X As Single, ByVal Y As Single)
tMouse.Value = True
tActual.Value = False
tSched.Value = False
tX.Value = False
tDiam.Value = False
tCirc.Value = False
tTri.Value = False
tTrash.Value = False
tText.Value = False
End Sub
EDIT:
I tried what was suggested about printing the value of the toggle button. And my computer blew up with messageboxes. I had changed all the actions to Click() events. Apparently I was sending the computer through an infinite loop. Maybe the act of changing a button from true to false or vice versa acts like a click and triggers all the other click events?
You have events that are triggering other events. What you'll need to do is set a Boolean AllowEventsToRun variable (either at the module or public level) and set that to false at the start of your code. Run whatever you need to do, and then set it to true at the end.
The trick is to do an if statement to make sure that AllowEventsToRun is set to true before any other code is running. Be sure to initialize the Boolean to true when you load your userform (since the default value of a boolean is false. So something like this:
Option Explicit
Private AllowEventsToRun As Boolean
Private Sub ToggleButton1_MouseUp(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
If AllowEventsToRun Then
AllowEventsToRun = False
'whatever you're doing that's causing the events to chain fire
AllowEventsToRun = True
End If
End Sub
Private Sub ToggleButton2_MouseUp(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
If AllowEventsToRun Then
AllowEventsToRun = False
'whatever you're doing that's causing the events to chain fire
AllowEventsToRun = True
End If
End Sub
Private Sub UserForm_Initialize()
AllowEventsToRun = True
End Sub
source: http://www.cpearson.com/excel/SuppressChangeInForms.htm
I would recommend using an "Option" Control instead of a "Toggle" Control.
If you stick 4 options in 1 frame then only 1 of those 4 options will allow itself to be true at a time automatically. You should use the "ToggleButton" control only if you want multiple instances of it to be true at the same time.
However if you refuse to do so and just really want to use ToggleButtons. Then you could write a procedure that is executed once pressing the button that sends it's name (or something else that identifies it uniquely) as a parameter to a procedure that sets all other togglebuttons false except it.
Cheers!
Private Sub ToggleButton1_Click()
Dim s As String
s = "ToggleButton1"
Evaluate_Options s
End Sub
Private Sub ToggleButton2_Click()
Dim s As String
s = "ToggleButton2"
Evaluate_Options s
End Sub
Private Sub ToggleButton3_Click()
Dim s As String
s = "ToggleButton3"
Evaluate_Options s
End Sub
Private Sub ToggleButton4_Click()
Dim s As String
s = "ToggleButton4"
Evaluate_Options s
End Sub
Private Sub ToggleButton5_Click()
Dim s As String
s = "ToggleButton5"
Evaluate_Options s
End Sub
Private Sub ToggleButton6_Click()
Dim s As String
s = "ToggleButton6"
Evaluate_Options s
End Sub
Private Sub ToggleButton7_Click()
Dim s As String
s = "ToggleButton7"
Evaluate_Options s
End Sub
Private Sub ToggleButton8_Click()
Dim s As String
s = "ToggleButton8"
Evaluate_Options s
End Sub
Private Sub Evaluate_Options(s As String)
Dim tgl As Control
For Each tgl In UserForm1.Frame1.Controls
If InStr(tgl.Name, s) Then Set_Toggles_1 tgl
Next
For Each tgl In UserForm1.Frame2.Controls
If InStr(tgl.Name, s) Then Set_Toggles_2 tgl
Next
End Sub
Private Sub Set_Toggles_1(tglTrue As Control)
Dim tglFalse As Control
For Each tglFalse In UserForm1.Frame1.Controls
If tglFalse.Name = tglTrue.Name Then tglFalse = True Else tglFalse = False
Next
End Sub
Private Sub Set_Toggles_2(tglTrue As Control)
Dim tglFalse As Control
For Each tglFalse In UserForm1.Frame2.Controls
If tglFalse.Name = tglTrue.Name Then tglFalse = True Else tglFalse = False
Next
End Sub
Try these basic mouse event capture subs and modify to suit your needs (tMouse = toggle button name):
Private Sub tMouse_Click()
'MsgBox "tb value = " & tMouse.Value
End Sub
Note: upper sub will not work, if lower sub called
Private Sub tMouse_MouseDown(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
If Button = 1 Then MsgBox "Left"
If Button = 2 Then MsgBox "Right"
End Sub

Resources