Calculating progress bar percentage - excel

I know there are endless posts for this but as my math skills are -100 I am getting issues in calculating the correct percentage. Below I have the loop that runs and then the sub that attempt to calculate the percentage. The issue is that the width of the label is wrong and for recordset with tiny numbers as 2 all is crewed up :-)
LOOP CODE
'loop until the end of the recordset
Do While Not Glob_RecSet.EOF
'inner loop to get each record fields
For FieldCount = 0 To Glob_RecSet.Fields.Count - 1
Glob_Sheet.Range(GLobWorkSheetRange).Offset(loopCounter, FieldCount).value = Glob_RecSet.Fields(FieldCount).value
Next
'start progress bar calculations the form show and unload is called on the form code apply changes button
RunProgressBar loopCounter, TotalRows, "Runningquery for " & Glob_RecSetRunning
'Next record
Glob_RecSet.MoveNext
'advance counter
loopCounter = loopCounter + 1
Loop
SUB CODE FOR PROGRESS BAR
Public Sub RunProgressBar(loopCounter As Variant, TotalRecords As Variant, FormTitle As String)
Dim LblDonewidth As Variant
Dim ProgBarCaption As Variant
Dim ProgresPercentage As Variant
If (TotalRecords < 100) Then
TotalRecords = 100
End If
ProgresPercentage = Round(loopCounter / TotalRecords * 100, 0)
'to avoid to give the progress bar a percentage greater than 100
If (ProgresPercentage > 100) Then
ProgresPercentage = 100
End If
ProgBarCaption = Round(ProgresPercentage, 0) & "%"
FrmProgBar.Caption = FormTitle
FrmProgBar.LblDone.Width = ProgresPercentage * 2
FrmProgBar.LblText.Caption = ProgBarCaption
'The DoEvents statement is responsible for the form updating
DoEvents
End Sub

I found the asnwer; the main issue was that I was not passing the corrent total of records in the recordset; this is solved by adding the line below before opening the recordset
'Clinet-Side cursor
Glob_RecSet.CursorLocation = adUseClient
then I found this example of progress bar here from which i took the correct logic for the progress bar percentage calculation.
Below the whole code
Sub InitProgressBar(maxValue As Long)
With FrmProgBar
.LblDone.Tag = .LblRemain.Width / maxValue
.LblDone.Width = 0
.LblText.Caption = ""
End With
End Sub
Public Sub RunProgressBar(loopCounter As Variant, formTitle As String)
Dim LblDonewidth As Variant
Dim ProgBarCaption As Variant
Dim ProgresPercentage As Variant
LblDonewidth = FrmProgBar.LblDone.Tag * loopCounter
ProgresPercentage = Round(FrmProgBar.LblDone.Width / FrmProgBar.LblRemain.Width * 100, 0)
ProgBarCaption = ProgresPercentage & "%"
'to avoid to give the progress bar a percentage greater than 100
If (ProgresPercentage > 100) Then
ProgresPercentage = 100
End If
FrmProgBar.Caption = formTitle
FrmProgBar.LblDone.Width = LblDonewidth
FrmProgBar.LblText.Caption = ProgBarCaption
End Sub
which is used as follow
TotalRecords = Glob_RecSet.RecordCount
'init progressbar
InitProgressBar (TotalRecords)
'loop until the end of the recordset
Do While Not Glob_RecSet.EOF
. . . .
'The DoEvents statement is responsible for the form updating
DoEvents
'start progress bar calculations the form show and unload
'is called on the form code apply changes button
RunProgressBar loopCounter, "Runningquery for " & Glob_RecSetRunning

Related

Results of a vba function not refreshed

I am creating a spreadsheet for a client to manage his ALM. I developped it under Excel and VBA, request of my client.
One sheet "Data" calculates all the vba functions. If i calculate manually each cell all works fine, but if i run the macro it did not.
Do you have a solution? I can post the entire file if needed, for a better investigation.
At the beginning all the calculation where in excel cell, but i created dedicated function for each table, because the file was too big when saved.
Public Sub Main()
Dim i, nb_tableaux As Integer
Dim j, lignemax, BarWidth As Long
Dim ProgressPercentage As Double
Dim echeancier, nomtableau As String
Dim ws_data As Worksheet
Dim c As Range
Me.ProgressLabel.Caption = "Initialisation terminée. "
Set ws_data = Sheets("Data")
lignemax = ws_data.Range("DATA").Rows.Count
Application.ScreenUpdating = True
Application.EnableEvents = True
nb_tableaux = 17
For i = 1 To nb_tableaux
echeancier = tab_Tableaux(i, 0)
nomtableau = tab_Tableaux(i, 1)
Me.ProgressLabel.Caption = "En cours : " & echeancier
ws_data.Range(nomtableau).Calculate
'With Worksheets("Data")
For j = 1 To lignemax
For Each c In ws_data.Range(nomtableau).Rows(j)
formulaToCopy = c.Formula
c.ClearContents
c.Value = formulaToCopy
DoEvents
Next
Me.ProgressLabel.Caption = "En cours : " & echeancier & ", " & Format(j / lignemax, "0.0%") & " completed"
Me.Repaint
Next j
'End With
Me.Bar.Width = i * 200 / nb_tableaux
Me.Bar.Caption = Format(i / nb_tableaux, "0%") & " completed"
Next i
Application.ScreenUpdating = False
Application.EnableEvents = False
End Sub
after taking into account the recommandations you gave me for my previous answers, the code works better, but still not for some of the ranges.
My issue come from a wrong calculation of a argument in the fonction.
In fact, I use ligne=activecell.row - 8, to get the ligne of the range to calculate. But it works if i do it manually, as the actual cell is activated, but not when i call the function many times, as i can not activate each cell, it will be too long for the spreadsheet.
How can i get ligne calculated, with the correct address of the cell where the function is written?
I hope i am clear enough. Sorry for my English.
Public Function Taux_Mois(ByVal mMois As Range, ByVal sScenario As Range)
Dim ligne As Long
ligne = ActiveCell.row - 8
Select Case (Range("DATA[Flag]").Cells(ligne).Value = 0) Or (Range("DATA[frequence fixing]").Cells(ligne).Value = 0)
Case True
Taux_Mois = 0
Exit Function
Case False
Dim index_taux As Integer
Dim ajust As Long
index_taux = CInt(Range("DATA[Indexation ID]").Cells(ligne).Value)
If index_taux = 1 Then
ajust = 0
Else
Dim ajust1, dernierfixingt0, freqfixing As Integer
dernierfixingt0 = Range("DATA[Dernier fixing t0]").Cells(ligne).Value
freqfixing = Range("DATA[frequence fixing]").Cells(ligne).Value
ajust1 = (Int((mMois.Value - dernierfixingt0) / freqfixing) * freqfixing)
ajust = Worksheets("Market Data").Range("Taux_" & sScenario.Value).Offset(12 + dernierfixingt0 + ajust1, 1 + index_taux).Value
End If
Taux_Mois = Range("DATA[facteur taux (TVA, base)]").Cells(ligne).Value * (ajust + Range("DATA[Spread / Taux]").Cells(ligne).Value / 10000)
Exit Function
End Select
End Function

Endless VBA Loop UNLESS I step through the code

I have a userform with 6 list objects. All of the list objects have named range rowsources. Clicking any one item in any one list will reference a chart on a spreadsheet and clear contents of any item's cell that does not belong with what was selected (explained better at the bottom of this, if you're interested). All of my list objects only have "After Update" triggers, everything else is handled by private subs.
Anyway, there's a lot of looping and jumping from list to list. If I run the userform normally, it endlessly loops. It seems to run through once, and then acts as though the user has again clicked the same item in the list, over and over again.
The odd thing is, if I step through the code (F8), it ends perfectly, when it's supposed to and control is returned to the user.
Does anyone have any thoughts on why that might be?
EDIT: I didn't originally post the code because all of it is basically a loop, and there's 150+ lines of it. I don't understand how it can be the code if stepping through makes it work perfectly, but allowing it to run regular makes it endless loop. Anyway, here's the code:
Option Explicit
Dim arySelected(6) As String
Dim intHoldCol As Integer, intHoldRow As Integer
Dim strHold As String
Dim rngStyleFind As Range, rngStyleList As Range
Private Sub UserForm_Activate()
Set rngStyleList = Range("Lists_W_Style")
Set rngStyleFind = Range("CABI_FindStyle")
End Sub
Private Sub lstStyle_AfterUpdate()
If lstStyle.ListIndex >= 0 Then
arySelected(0) = lstStyle.Value
Call FilterCabinetOptions(Range("Lists_W_Style"), Range("CABI_FindStyle"), 0)
End If
End Sub
Private Sub lstWood_AfterUpdate()
If lstWood.ListIndex >= 0 Then
arySelected(1) = lstWood.Value
Call FilterCabinetOptions(Range("Lists_W_Wood"), Range("CABI_FindWood"), 1)
' lstWood.RowSource = "Lists_W_Wood"
End If
End Sub
Private Sub cmdReset_Click()
Range("Lists_S_Style").Copy Destination:=Range("Lists_W_Style")
Call RemoveXes(Range("Lists_W_Style"))
Range("Lists_S_Wood").Copy Destination:=Range("Lists_W_Wood")
Call RemoveXes(Range("Lists_W_Wood"))
Range("Lists_S_Door").Copy Destination:=Range("Lists_W_Door")
Call RemoveXes(Range("Lists_W_Door"))
Range("Lists_S_Color").Copy Destination:=Range("Lists_W_Color")
Call RemoveXes(Range("Lists_W_Color"))
Range("Lists_S_Glaze").Copy Destination:=Range("Lists_W_Glaze")
Call RemoveXes(Range("Lists_W_Glaze"))
Range("Lists_S_Const").Copy Destination:=Range("Lists_W_Const")
Call RemoveXes(Range("Lists_W_Const"))
Range("Lists_S_DrawFrontConst").Copy Destination:=Range("Lists_W_DrawFrontConst")
Call RemoveXes(Range("Lists_W_DrawFrontConst"))
End Sub
Private Sub FilterCabinetOptions(rngList As Range, rngFind As Range, intAry As Integer)
Dim intListCntr As Integer, intFindCntr As Integer, intStyleCntr As Integer
If intAry = 0 Then
Call FindStyle(arySelected(intAry))
Else
'Save the List item.
For intListCntr = 1 To rngList.Rows.Count
If rngList.Cells(intListCntr, 1) = arySelected(intAry) Then
rngList.Cells(intListCntr, 3) = "X"
' Call RemoveNonXes(rngList)
Exit For
End If
Next intListCntr
'Save the column of the Find List.
For intFindCntr = 1 To rngFind.Columns.Count
If rngFind.Cells(1, intFindCntr) = arySelected(intAry) Then
'Minus 2 to allow for columns A and B when using Offset in the below loop.
intHoldCol = rngFind.Cells(1, intFindCntr).Column - 2
Exit For
End If
Next intFindCntr
'Find appliciple styles.
For intStyleCntr = 1 To rngStyleFind.Rows.Count
If Len(rngStyleFind.Cells(intStyleCntr, intHoldCol)) > 0 Then
Call FindStyle(rngStyleFind.Cells(intStyleCntr, 1))
End If
Next intStyleCntr
End If
Call RemoveNonXes(rngStyleList)
Call RemoveNonXes(Range("Lists_W_Wood"))
Call RemoveNonXes(Range("Lists_W_Door"))
Call RemoveNonXes(Range("Lists_W_Color"))
Call RemoveNonXes(Range("Lists_W_Glaze"))
Call RemoveNonXes(Range("Lists_W_Const"))
Call RemoveNonXes(Range("Lists_W_DrawFrontConst"))
End Sub
Private Sub FindStyle(strFindCode As String)
Dim intListCntr As Integer, intFindCntr As Integer
For intListCntr = 1 To rngStyleList.Rows.Count
If rngStyleList.Cells(intListCntr, 1) = strFindCode Then
rngStyleList.Range("C" & intListCntr) = "X"
Exit For
End If
Next intListCntr
For intFindCntr = 1 To rngStyleFind.Rows.Count
If rngStyleFind.Cells(intFindCntr, 1) = strFindCode Then
intHoldRow = rngStyleFind.Cells(intFindCntr).Row
Exit For
End If
Next intFindCntr
If Len(arySelected(1)) = 0 Then Call FindStyleOptions(Range("CABI_FindWood"), Range("Lists_W_Wood"))
If Len(arySelected(2)) = 0 Then Call FindStyleOptions(Range("CABI_FindDoor"), Range("Lists_W_Door"))
If Len(arySelected(3)) = 0 Then Call FindStyleOptions(Range("CABI_FindColor"), Range("Lists_W_Color"), Range("Lists_W_Wood"))
If Len(arySelected(4)) = 0 Then Call FindStyleOptions(Range("CABI_FindGlaze"), Range("Lists_W_Glaze"), Range("Lists_W_Wood"))
If Len(arySelected(5)) = 0 Then Call FindStyleOptions(Range("CABI_FindConst"), Range("Lists_W_Const"))
If Len(arySelected(6)) = 0 Then Call FindStyleOptions(Range("CABI_FindDrawFrontConst"), Range("Lists_W_DrawFrontConst"))
End Sub
Private Sub FindStyleOptions(rngFind As Range, rngList As Range, Optional rngCheckList As Range)
Dim intListCntr As Integer, intFindCntr As Integer
Dim intStrFinder As Integer, intCheckCntr As Integer
Dim strHoldCheck As String
Dim strHoldFound As String, strHoldOption As String
'Go through the appropriate find list (across the top of CABI)
For intFindCntr = 1 To rngFind.Columns.Count
strHoldOption = rngFind.Cells(1, intFindCntr)
strHoldFound = rngFind.Cells(1, intFindCntr).Offset((intHoldRow - 1), 0)
If Len(strHoldFound) > 0 Then
If rngCheckList Is Nothing Then
For intListCntr = 1 To rngList.Rows.Count
If rngList.Cells(intListCntr, 1) = strHoldFound Then
Call AddXes(rngList, strHoldFound, "X")
Exit For
End If
Next intListCntr
Else
intStrFinder = 1
Do While intStrFinder < Len(rngFind.Cells(1, intFindCntr).Offset((intHoldRow - 1), 0))
strHoldCheck = Mid(rngFind.Cells(1, intFindCntr).Offset((intHoldRow - 1), 0), intStrFinder, 2)
intStrFinder = intStrFinder + 3
For intCheckCntr = 1 To rngCheckList.Rows.Count
If strHoldCheck = rngCheckList(intCheckCntr, 1) And Len(rngCheckList(intCheckCntr, 3)) > 0 Then
Call AddXes(rngList, strHoldOption, "X")
intStrFinder = 99
Exit For
End If
Next intCheckCntr
Loop
End If
End If
Next intFindCntr
End Sub
Private Sub AddXes(rngList As Range, strToFind As String, strX As String)
Dim intXcntr As Integer
For intXcntr = 1 To rngList.Rows.Count
If rngList.Cells(intXcntr, 1) = strToFind Then
rngList.Cells(intXcntr, 3) = strX
Exit For
End If
Next intXcntr
End Sub
Private Sub RemoveNonXes(rngList As Range)
Dim intXcntr As Integer
For intXcntr = 1 To rngList.Rows.Count
If Len(rngList(intXcntr, 3)) = 0 Then
rngList.Range("A" & intXcntr & ":B" & intXcntr) = ""
Else
rngList.Range("C" & intXcntr) = ""
End If
Next intXcntr
End Sub
Private Sub RemoveXes(rngList As Range)
rngList.Range("C1:C" & rngList.Rows.Count) = ""
End Sub
Explanation:
Imagine you had 6 lists with different automobile conditions. So Make would be one list with Chevy, Ford, Honda... Model would be another with Malibu, Focus, Civic... But you'd also have Color Blue, Red, Green... So if your user wants a Green car, the program references an inventory list and gets rid of any Makes, Models, etc... not available in green. Likewise the user could click on Civic from the Model list and it would elminate all but Honda from the Make and so on. That's what I'm trying to do anyway.
Without seeing the code it's tough to tell. When you run the script, the 'AfterUpdate' event may be getting triggered over and over, causing the endless loop. Try using a counter to limit the update to one change and have it exit the loop once the counter is greater than 0.

Custom right-click menu - OnAction works straight away rather than on button-press

I'm creating a custom menu in Excel which consists of various sub-menus. It's for picking various machinery items and there's about 250 possible outcomes.
In any case, I've got the menu built and want it so that the .Caption is entered into the cell when the menu is used. I've put the .OnAction into the relevant buttons but, unfortunately, the .OnAction activates when the file is opened, not when the button is clicked. As such, all 250-odd .Captions are quickly entered into the same cell in quick succession.
Quick edit - the important bit is towards the bottom of the BuildMenus, where the .OnAction calls the function AddStuff. I know this is running on the Workbook_Activate which is why it runs straight away but everywhere else I've looked online does it the same way.
Private Sub Workbook_Activate()
BuildMenus
End Sub
Private Sub BuildMenus()
'Workbook_SheetBeforeRightClick(ByVal Sh As Object, ByVal Target As Range, Cancel As Boolean)
Dim AmountOfCats As Integer
Dim ThisIsMyCell As String
ThisIsMyCell = ActiveCell.Address
'this is where we would set the amount of categories. At the moment we'll have it as 15
AmountOfCats = 15
Dim cBut As CommandBarControl
Dim Cats As CommandBarControl
Dim SubCats As CommandBarControl
Dim MenuDesc As CommandBarButton
On Error Resume Next
With Application
.CommandBars("Cell").Controls("Pick Machinery/Plant...").Delete
End With
Set cBut = Application.CommandBars("Cell").Controls.Add(Type:=msoControlPopup, Temporary:=True)
cBut.Caption = "Pick Machinery/Plant.."
With cBut
.Caption = "Pick Machinery/Plant..."
.Style = msoButtonCaption
End With
Dim i As Integer
Dim j As Integer
Dim k As Integer
Dim SC As Integer
Dim AmountOfMenus As Integer
SC = 1
Dim MD As Integer
MD = 1
Dim MyCaption As String
For i = 0 To AmountOfCats - 1
Set Cats = cBut.Controls.Add(Type:=msoControlPopup, Temporary:=True)
Cats.Caption = Categories(i + 1)
Cats.Tag = i + 1
For j = 0 To (SubCatAmounts(i + 1) - 1)
Set SubCats = Cats.Controls.Add(Type:=msoControlPopup, Temporary:=True)
SubCats.Caption = SubCatArray(SC)
SubCats.Tag = j + 1
AmountOfMenus = MenuAmounts(SC)
For k = 0 To AmountOfMenus - 1
Set MenuDesc = SubCats.Controls.Add(Type:=msoControlButton)
With MenuDesc
.Caption = MenuArray(MD)
.Tag = MD
MyCaption = .Caption
.OnAction = AddStuff(MyCaption)
End With
MD = MD + 1
Next
SC = SC + 1
Next
Next
On Error GoTo 0
End Sub
Function AddStuff(Stuff As String)
Dim MyCell As String
MyCell = ActiveCell.Address
ActiveCell.Value = Stuff
End Function
OnAction expects a string value: instead you are calling your AddStuff sub while creating your menu...
.OnAction = "AddStuff """ & MyCaption & """"
is what you want (assuming I got my quotes right)
I was making a mistake with my AddStuff - I was calling it as a function when instead it should have been a macro (or a regular sub). A slight modification to Tim Williams' .OnAction code
MyButton.OnAction = "AddStuff(""" & MyButton.Caption & """)"
did the trick.

Could Anyone Show List of Button Face Id in Excel 2010

I would like to create costum menu button using VBA in my excel 2010 file using predefined excel button that use face id. In my case i would like to use "lock" and "refresh" icon, but doesn`t know the face id for that icon. could anyone show or point me the list of button and face id used in excel 2010?
The following Sub BarOpen() works with Excel 2010, most probably also many other versions also, and generates in the Tab "Add-Ins" a custom, temporary toolbar with drop-downs to show the FaceIDs from 1 .. 5020 in groups of 30 items.
Option Explicit
Const APP_NAME = "FaceIDs (Browser)"
' The number of icons to be displayed in a set.
Const ICON_SET = 30
Sub BarOpen()
Dim xBar As CommandBar
Dim xBarPop As CommandBarPopup
Dim bCreatedNew As Boolean
Dim n As Integer, m As Integer
Dim k As Integer
On Error Resume Next
' Try to get a reference to the 'FaceID Browser' toolbar if it exists and delete it:
Set xBar = CommandBars(APP_NAME)
On Error GoTo 0
If Not xBar Is Nothing Then
xBar.Delete
Set xBar = Nothing
End If
Set xBar = CommandBars.Add(Name:=APP_NAME, Temporary:=True) ', Position:=msoBarLeft
With xBar
.Visible = True
'.Width = 80
For k = 0 To 4 ' 5 dropdowns, each for about 1000 FaceIDs
Set xBarPop = .Controls.Add(Type:=msoControlPopup) ', Before:=1
With xBarPop
.BeginGroup = True
If k = 0 Then
.Caption = "Face IDs " & 1 + 1000 * k & " ... "
Else
.Caption = 1 + 1000 * k & " ... "
End If
n = 1
Do
With .Controls.Add(Type:=msoControlPopup) '34 items * 30 items = 1020 faceIDs
.Caption = 1000 * k + n & " ... " & 1000 * k + n + ICON_SET - 1
For m = 0 To ICON_SET - 1
With .Controls.Add(Type:=msoControlButton) '
.Caption = "ID=" & 1000 * k + n + m
.FaceId = 1000 * k + n + m
End With
Next m
End With
n = n + ICON_SET
Loop While n < 1000 ' or 1020, some overlapp
End With
Next k
End With 'xBar
End Sub
Have a look here:
Face ID's
Its an addin for MS excel. Works for excel 97 and later.
Modified previous answer to create numerous toolbars with sets of 10 icons. Can change code (comment/un-comment) number of toolbars (performance may be slow on slower machines)
The last icon number for Office 2013 that I could find was 25424 for OneDrive
Sub FaceIdsOutput()
' ==================================================
' FaceIdsOutput Macro
' ==================================================
' =========================
Dim sym_bar As CommandBar
Dim cmd_bar As CommandBar
' =========================
Dim i_bar As Integer
Dim n_bar_ammt As Integer
Dim i_bar_start As Integer
Dim i_bar_final As Integer
' =========================
Dim icon_ctrl As CommandBarControl
' =========================
Dim i_icon As Integer
Dim n_icon_step As Integer
Dim i_icon_start As Integer
Dim i_icon_final As Integer
' =========================
n_icon_step = 10
' =========================
i_bar_start = 1
n_bar_ammt = 500
' i_bar_start = 501
' n_bar_ammt = 1000
' i_bar_start = 1001
' n_bar_ammt = 1500
' i_bar_start = 1501
' n_bar_ammt = 2000
' i_bar_start = 2001
' n_bar_ammt = 2543
i_bar_final = i_bar_start + n_bar_ammt - 1
' =========================
' delete toolbars
' =========================
For Each cmd_bar In Application.CommandBars
If InStr(cmd_bar.Name,"Symbol") <> 0 Then
cmd_bar.Delete
End If
Next
' =========================
' create toolbars
' =========================
For i_bar = i_bar_start To i_bar_final
On Error Resume Next
Set sym_bar = Application.CommandBars.Add _
("Symbol" & i_bar, msoBarFloating, Temporary:=True)
' =========================
' create buttons
' =========================
i_icon_start = (i_bar-1) * n_icon_step + 1
i_icon_final = i_icon_start + n_icon_step - 1
For i_icon = i_icon_start To i_icon_final
Set icon_ctrl = sym_bar.Controls.Add(msoControlButton)
icon_ctrl.FaceId = i_icon
icon_ctrl.TooltipText = i_icon
Debug.Print ("Symbol = " & i_icon)
Next i_icon
sym_bar.Visible = True
Next i_bar
End Sub
Sub DeleteFaceIdsToolbar()
' ==================================================
' DeleteFaceIdsToolbar Macro
' ==================================================
Dim cmd_bar As CommandBar
For Each cmd_bar In Application.CommandBars
If InStr(cmd_bar.Name,"Symbol") <> 0 Then
cmd_bar.Delete
End If
Next
End Sub
I put together my own list of button face ID's. I used Excel VBA code to test all the toolface numbers up to 100,000. There were faces for ID numbers up to almost 34,000. Most of these had duplicates, which make them harder to look through. I compared all the faces to each other using a VBA arraylist, and only kept the first instance of each one. I think this file shows all the toolfaces with their numbers, but it only shows each one once:
https://www.dropbox.com/s/7q7y7uf3tuy02uu/FaceID%20Excel%202021.pdf?dl=0
short script writes ten (loop set for 10) FaceID's add. as entry into toolbar Tab "Add-In" and with "Benutzerdefinierte Symbolliste löschen" - you erase this add entry ( mark and right mouse click) - works with excel 2010/2013
Sub FaceIdsAusgeben()
Dim symb As CommandBar
Dim Icon As CommandBarControl
Dim i As Integer
On Error Resume Next
Set symb = Application.CommandBars.Add _
("Symbole", msoBarFloating)
For i = 1 To 10
Set Icon = symb.Controls.Add(msoControlButton)
Icon.FaceId = i
Icon.TooltipText = i
Debug.Print ("Symbole = " & i)
Next i
symb.Visible = True
End Sub
script provides on worksheet name of controls excel 2010/2013
Sub IDsErmitteln()
Dim crtl As CommandBarControl
Dim i As Integer
Worksheets.Add
On Error Resume Next
i = 1
For Each crtl In Application.CommandBars(1).Controls(1).Controls
Cells(i, 1).Value = crtl.Caption
Cells(i, 2).Value = crtl.ID
i = i + 1
Next crtl
End Sub
I found it in this location what i`m looking at
http://support.microsoft.com/default.aspx?scid=kb;[LN];Q213552
The table contain the Control and id (Face Id) used in excel. So for "Refresh" button the face id is 459, but it only work on the id less than 3 digit.
and this generator(by input start face id and end face id) then click show button faces, you get the list of icon on the range (to download it must login first)
http://www.ozgrid.com/forum/showthread.php?t=39992
and this for Ribbon Toolbar
http://www.rondebruin.nl/ribbon.htm

Excel Slider Control: How could I limit the sum of all sliders to, say, 100?

See image for clarity.
I have 5 variables (A, B, C, D and E), each of which can range from 0-100. I need the sum of all these variables to be 100 at all times, not more, not less. However, the way it is set up currently, if I change variable A from 21 to, say, 51, the total becomes 130.
How could I set this up such that if I change one variable, the others could automatically compensate for that increase or decrease, such that the total is always 100?
Use the Slider Change events, so that when one slider changes value the others are scaled so values sum to 100
Example code, using 3 sliders - you can scale it to allow for as many sliders as you want
Private UpdateSlider As Boolean
Private Sub ScaleSliders(slA As Double, ByRef slB As Double, ByRef slC As Double)
Dim ScaleFactor As Double
If (slB + slC) = 0 Then
ScaleFactor = (100# - slA)
slB = ScaleFactor / 2
slC = ScaleFactor / 2
Else
ScaleFactor = (100# - slA) / (slB + slC)
slB = slB * ScaleFactor
slC = slC * ScaleFactor
End If
End Sub
Private Sub ScrollBar1_Change()
Dim slB As Double, slC As Double
' UpdateSlider = False
If Not UpdateSlider Then
slB = ScrollBar2.Value
slC = ScrollBar3.Value
ScaleSliders ScrollBar1.Value, slB, slC
UpdateSlider = True
ScrollBar2.Value = slB
ScrollBar3.Value = slC
UpdateSlider = False
End If
End Sub
Private Sub ScrollBar2_Change()
Dim slB As Double, slC As Double
If Not UpdateSlider Then
slB = ScrollBar1.Value
slC = ScrollBar3.Value
ScaleSliders ScrollBar2.Value, slB, slC
UpdateSlider = True
ScrollBar1.Value = slB
ScrollBar3.Value = slC
UpdateSlider = False
End If
End Sub
Private Sub ScrollBar3_Change()
Dim slB As Double, slC As Double
If Not UpdateSlider Then
slB = ScrollBar1.Value
slC = ScrollBar2.Value
ScaleSliders ScrollBar1.Value, slB, slC
UpdateSlider = True
ScrollBar1.Value = slB
ScrollBar2.Value = slC
UpdateSlider = False
End If
End Sub
Note that sliders data type in integer, so you may need to allow for rounding not summing to exactly 100
Thx Chris for posting your solution. To scale it to six, I've made this. I'm no VBA expert, this code is not yet really clean or great. but it might help someone.
Private UpdateSlider As Boolean
Private Sub ScaleSliders_arr(slider_value As Double, ByRef other_sliders() As Double)
Dim scale_factor As Double
Dim total_other_sliders As Double
Dim element As Variant
Dim i As Integer
Dim other_sliders_arr_length As Long
For Each element In other_sliders
total_other_sliders = total_other_sliders + element
Debug.Print total_other_sliders
Next element
' when all other values are 0
If total_other_sliders = 0 Then
ScaleFactor = (100# - slider_value)
other_sliders_arr_length = ArrayLength(other_sliders)
i = 0
For Each element In other_sliders
other_sliders(i) = ScaleFactor / other_sliders_arr_length
i = i + 1
Next element
Debug.Print other_sliders_arr_length
' When other sliders have >0 as a total sum
Else
ScaleFactor = (100# - slider_value) / total_other_sliders
' Adjust other sliders according to current value
i = 0
For Each element In other_sliders
other_sliders(i) = other_sliders(i) * ScaleFactor
i = i + 1
Next element
End If
End Sub
Private Sub AdjustSliderByMagic(this_slider As Variant)
Dim slider_value As Double
Dim other_sliders() As Double
Dim cell_locations() As Variant
Dim other_sliders_arr_size As Integer
Dim value As Variant
Dim i As Integer
Dim k As Integer
' which cells contain the values - this also determines number of rows
cell_locations = Array("HiddenTable!B2", "HiddenTable!B3", "HiddenTable!B4", "HiddenTable!B5", "HiddenTable!B6", "HiddenTable!B7")
' size of the others is minus 2 because A) counting starts at 0 B) one slider is the current one which is not the other
other_sliders_arr_size = ArrayLength(cell_locations) - 2
' need to size the other sliders array
ReDim other_sliders(other_sliders_arr_size)
' start loops with 0's
i = 0
k = 0
' Determine the value of this slider and of the other sliders
For Each value In cell_locations
If this_slider = cell_locations(i) Then
slider_value = Range(cell_locations(i)).value
Else
other_sliders(k) = Range(cell_locations(i)).value
k = k + 1
End If
i = i + 1
Next value
' use function to determine slider values
ScaleSliders_arr slider_value, other_sliders
UpdateSlider = True
' start loops with 0's
i = 0
k = 0
' change the values of the other sliders
For Each value In cell_locations
If this_slider = cell_locations(i) Then
'do nothing
Else
Range(cell_locations(i)).value = other_sliders(k)
k = k + 1
End If
i = i + 1
Next value
End Sub
Private Sub ScrollBar1_Change()
Dim this_slider As Variant
' what is the connected field of this slider
this_slider = "HiddenTable!B2"
AdjustSliderByMagic (this_slider)
End Sub
Private Sub ScrollBar2_Change()
Dim this_slider As Variant
' what is the connected field of this slider
this_slider = "HiddenTable!B3"
AdjustSliderByMagic (this_slider)
End Sub
Private Sub ScrollBar3_Change()
Dim this_slider As Variant
' what is the connected field of this slider
this_slider = "HiddenTable!B4"
AdjustSliderByMagic (this_slider)
End Sub
Private Sub ScrollBar4_Change()
Dim this_slider As Variant
' what is the connected field of this slider
this_slider = "HiddenTable!B5"
AdjustSliderByMagic (this_slider)
End Sub
Private Sub ScrollBar5_Change()
Dim this_slider As Variant
' what is the connected field of this slider
this_slider = "HiddenTable!B6"
AdjustSliderByMagic (this_slider)
End Sub
Private Sub ScrollBar6_Change()
Dim this_slider As Variant
' what is the connected field of this slider
this_slider = "HiddenTable!B7"
AdjustSliderByMagic (this_slider)
End Sub
Function ArrayLength(arr As Variant) As Long
On Error GoTo eh
' Loop is used for multidimensional arrays. The Loop will terminate when a
' "Subscript out of Range" error occurs i.e. there are no more dimensions.
Dim i As Long, length As Long
length = 1
' Loop until no more dimensions
Do While True
i = i + 1
' If the array has no items then this line will throw an error
length = length * (UBound(arr, i) - LBound(arr, i) + 1)
' Set ArrayLength here to avoid returing 1 for an empty array
ArrayLength = length
Loop
Done:
Exit Function
eh:
If Err.Number = 13 Then ' Type Mismatch Error
Err.Raise vbObjectError, "ArrayLength" _
, "The argument passed to the ArrayLength function is not an array."
End If
End Function

Resources