VB.net/Excel- "Backwards" tab index For Each iteration with textboxes - vb.net

I have a form with 3 textboxes and 1 button.
textbox1 has tab index 0, and it's text = 1
textbox2 has tab index 1, and it's text = 2
textbox3 has tab index 2, and it's text = 3
I want to iterate thru the textboxes and place their values into cells so that...
range("A1").value = txtbox1.text (ie: A1 = "1")
range("A2").value = txtbox2.text (ie: A2 = "2")
range("A3").value = txtbox3.text (ie: A3 = "3")
but what I am getting is...
range("A1").value = txtbox1.text (ie: A1 = "3")
range("A2").value = txtbox2.text (ie: A2 = "2")
range("A3").value = txtbox3.text (ie: A3 = "1")
I have tried reversing the tab index for the text boxes, but it doesn't change the "backwards iteration".
Is there something I can do to change this so that the loop runs from lowest tab index to highest?
Thanks!
Public Class Form1
Private Sub Button1_Click_1(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim objExcel As New Microsoft.Office.Interop.Excel.Application 'Declaring the object.
objExcel.Visible = True 'Setting Excel to visible.
Dim cntrl As Control
With objExcel
.Workbooks.Add() 'Adding a workbook.
.Range("A1").Select() 'Selecting cell A1.
End With
'Form contains 3 text boxes, with one number in each (1,2,3), and one button to fire the code in this sub.
For Each cntrl In Me.Controls 'For every control on the form...
If TypeOf (cntrl) Is TextBox Then 'If the control is a textbox, then...
With objExcel
.ActiveCell.Value = cntrl.Text 'place the control's text in the active cell and...
.ActiveCell.Offset(1, 0).Activate() 'offset down one row.
End With
End If 'If the control is not a textbox (if it's the button), do nothing.
Next 'Go to the next control.
objExcel = Nothing 'Release the object.
GC.Collect() 'Clean up.
End Sub
End Class

Sounds like it may be the way Excel is iterating over the controls. Have you tried this to see what the output is?
Dim objExcel As New Microsoft.Office.Interop.Excel.Application 'Declaring the object.
objExcel.Visible = True 'Setting Excel to visible.
Dim cntrl As Control
With objExcel
.Workbooks.Add() 'Adding a workbook.
.Range("A3").Select() 'Selecting cell A3.
End With
'Form contains 3 text boxes, with one number in each (1,2,3), and one button to fire the code in this sub.
For Each cntrl In Me.Controls 'For every control on the form...
If TypeOf (cntrl) Is TextBox Then 'If the control is a textbox, then...
With objExcel
.ActiveCell.Value = cntrl.Text 'place the control's text in the active cell and...
.ActiveCell.Offset(-1, 0).Activate() 'offset up one row.
End With
End If 'If the control is not a textbox (if it's the button), do nothing.
Next 'Go to the next control.
objExcel = Nothing 'Release the object.
GC.Collect() 'Clean up.

Using a For Each loop implies that you don't care what order you are doing them in.
What I would do is use the "tag" property of the control to hold the row number you'd like the answer in:
For Each cntrl In Me.Controls
If TypeOf (cntrl) Is TextBox Then
objExcel.ActiveSheet.Cells(cntrl.Tag, 1).Value = cntrl.Text
End If
Next
You could also add a hidden panel on the form that contains only your key textboxes, then use a traditional FOR loop instead of a 'For Each':
Dim i as integer
Dim myBox as textBox
For i = 1 to 3
set myBox = Me.Panel1.Controls(i)
objExcel.ActiveSheet.Cells(myBox.Tag, 1).Value = myBox.Text
Next i
It should go through in Tab order. You can even skip the typecheck, because you already know what it is.

Related

How to paste clipboard value into a specific ComboBox on a UserForm?

I copy a name in the first column of the active row on a spreadsheet to the clipboard.
I launch a UserForm by the name CommandsUserForm.
The UserForm is overlaid with multiple pages or tabs, so it defaults to the first tab (this is desired).
On this tab, there is a ComboBox by the name DPComboBox.
I want to paste the value in the clipboard into the ComboBox after the userform is launched.
Userform with the ComboBox highlighted.
Sub Show_Quick_Commands()
DPName = ThisWorkbook.ActiveSheet.Cells(ActiveCell.Row, 1).Value
Set DPNameforQ = New DataObject
DPNameforQ.SetText DPName
DPNameforQ.PutInClipboard
CommandsUserForm.Show vbModeless
End Sub
I tried DPComboBox.PasteSpecial Transpose:=True, but that breaks the code and requests a debug.
For example:
Sub Show_Quick_Commands()
Dim frm As CommandsUserForm
Set frm = New CommandsUserForm
frm.DPName = ActiveCell.EntireRow.Cells(1).Value 'set before showing the form
frm.Show vbModeless
End Sub
Userform:
Option Explicit
Private m_DPName As String
Private Sub UserForm_Activate()
Dim i As Long, v
'put some dummy data in the combobox
For i = 1 To 10
Me.DPComboBox.AddItem "ClientPartner_" & Format(i, "000")
Next i
Me.DPComboBox.Text = m_DPName 'set the value
End Sub
'call this before showing the form
Property Let DPName(nm As String)
m_DPName = nm
End Property

Loop through checkboxes to see which ones are checked

I have a userform that contains 10x check boxes.
Each of these check boxes should have a value. Say check box 1 should contain value "Medicine", check box 2 should contain value "Water".
I am giving the users the option to check any of these and press submit. On pressing submit, I would like to check which check boxes were ticked and combine the values.
I.e. if user ticks only check box 1 and 2, then the output will be "MedicineWater".
Rather than doing 10 nested IF statements and then doing all the possible permutations, which would take very long. I wonder if it is possible to loop through the check boxes and see which one is ticked (marked as True) and then store the value that should be assigned to it.
My simplified code is:
Private Sub Submit_Click()
Dim i as Long
Dim Val1 as String
Dim Val2 as String
Dim Array()
Dim Final as String
Val1 = "Medicine"
Val2 = "Water"
For i = 1 to 2
If Me.CheckBox & i = True Then
Array = Val & i
Final = Join(Array, "")
End If
Next i
Msgbox (Final)
End Sub
Can someone please advise me how to do this properly?
Thanks
I believe the following will do what you expect:
Private Sub Submit_Click()
Dim Final As String
Dim ctrl As Control
For Each ctrl In Me.Controls
'loop through controls in your UserForm
If TypeName(ctrl) = "CheckBox" Then 'if control is a CheckBox then
If ctrl.Value = True Then 'if the checkbox is checked
Final = Final & ctrl.Caption 'add the caption to the variable
End If
End If
Next ctrl
MsgBox (Final)
End Sub
UPDATE:
If what you need is to assign the caption of the given checkbox to a variable, you can do so like below, using an Array to store values for each checkbox, this example will only store values for checkboxes that are checked:
Private Sub Submit_Click()
Dim Final() As String
Dim ctrl As Control
Dim counter As Integer
counter = 0
For Each ctrl In Me.Controls
'loop through controls in your UserForm
counter = counter + 1
ReDim Preserve Final(counter)
If TypeName(ctrl) = "CheckBox" Then 'if control is a CheckBox then
If ctrl.Value = True Then 'if the checkbox is checked
Final(counter) = ctrl.Caption 'add the caption to the variable
End If
End If
Next ctrl
End Sub

VBA excel change value of multiple checkboxes

I have a big row of 250 ActiveX checkboxes of which I want to change the values all back from checked to unchecked, is there a way to put that in a macro?
checkbox names are just Checkbox3 to Checkbox253
An easy way:
Private Sub UnchkAllBox()
For i = 3 to 253
Controls("Checkbox" & i).Value = False
Next i
End Sub
In this way, the names of the checkboxes are very important. Use it only you named your checkboxes orderly.
Other way:
Private Sub UnchkAllBox2()
Dim Ctrl As Control
For Each Ctrl In Me.Controls
If TypeName(Ctrl) = "CheckBox" Then Ctrl.Value = False
Next Ctrl
End Sub
In this case, you don't have to concern the names. However, it will uncheck all checkboxes in your form.
Both methods mentioned are assuming all checkboxes were placed in a userform. For checkboxes in a worksheet, excel stores them in a OLEObjects collection instead of Controls. Therefore, the code should be re-written as below.
Private Sub UnChkAllBox()
For i = 1 To 5
OLEObjects("CheckBox" & i).Object.Value = False
Next i
End Sub
And
Private Sub UnChkAllBox2()
Dim Obj As OLEObject
For Each Obj In Me.OLEObjects
If TypeOf Obj.Object Is MSForms.CheckBox Then Obj.Object.Value = False
Next Obj
End Sub

Access 2016 VBA - Create custom collection of controls, manually specifying control's name

Since you can't have a vertical Control Tab in Access, I'm creating a fake one.
I wrote this piece of code to highlight (change fore color to white) the current page:
Private Sub updateBtnColor()
Dim currentPageIndexSZero As Long
Dim ctl As Control
currentPageIndexSZero = Me.tab1
For Each ctl In Me.Controls
If ctl.Tag = "page" & CStr(currentPageIndexSZero) Then
ctl.ForeColor = white
ElseIf InStr(1, ctl.Tag, "page", vbBinaryCompare) Then
ctl.ForeColor = black
End If
Next ctl
End Sub
Every button has got the page it's referring to as a tag, in the form of:
page0
page1
...
pageN
So the loop basically checks the current page, finds the button with the appropriate tag (assuming I named them correctly) and highlights its text.
Now this can be slow since I've got a heavy loaded form or bad practise, so I thought of creating a custom collection instead of looping through the whole Controls Collection.
I wanted to create such structure so I can loop through it, something like:
Enum myButtons
button1 = Forms!myForm!button1
button2 = Forms!myForm!button2
button3 = Forms!myForm!button3
button4 = Forms!myForm!button4
End Enum
And then:
Public Sub updateBtnColor()
Dim curPageZ as Long
Dim button as Control
For Each button in myButtons
With button
If Right$(CStr(.Name, 1)) = curPageZ
.ForeColor = 16777215 ' White
Else ' No need for an additional ElseIf since I already know these are only the wanted buttons
.ForeColor = 0 ' Black
End If
End With
Next button
End Sub
What is the most elegant/fastest/best/proper way to create such structure or, if my idea is not that good, to create such logic?
Thanks!
Cycling thru controls works quite fast, but if you want to speedup this, use Collection for storing objects, not Enum. Collection can be cycled also using For Each....
On form module level create a collection for your buttons:
Dim mcolMyButtons As New Collection
Then fill this collection in Open or Load events. You can use tags:
Dim ctl As Control
For Each ctl In Me.Controls
If TypeOf ctl Is CommandButton Then
If ctl.Tag Like "page*" Then
mcolMyButtons.Add ctl
End If
End If
Next
Or just directly add buttons you want:
mcolMyButtons.Add Me.Button1
mcolMyButtons.Add Me.Button2
mcolMyButtons.Add Me.Button3
mcolMyButtons.Add Me.Button4
Then your sub:
Private Sub updateBtnColor()
Dim ctl As Control
For Each ctl In mcolMyButtons
If ctl.Tag = "page" & Me.tab1 Then
ctl.ForeColor = vbWhite
Else
ctl.ForeColor = vbBlack
End If
Next ctl
End Sub

VB hidden button on form

I am using VB to search some data from a text file and then populate in excel. It's working. The problem is xl.visible=true makes the excel sheet to be visible at once & then the values keep on populating. I want to hide the excel till data population is complete. then make a button appear on the form which when clicked, will display the excel file.
Please help. Here is the code I'm using:
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
' create an excel instance
Dim xl = Microsoft.VisualBasic.CreateObject("Excel.Application")
xl.Visible = False
Dim wb = xl.Workbooks.Add()
Dim sheet = wb.ActiveSheet
' find lines starting with any whitepace followed by MTV or MTB and capture
' the text after =
Dim pattern = "(?<=\s*(MTV).*=).*"
Dim i = 1
Dim arg = {Microsoft.VisualBasic.ControlChars.CrLf, Microsoft.VisualBasic.ControlChars.Lf}
If RichTextBox3.Text = "" Then
MsgBox("No input. What will I process??")
Else
Timer1.Start()
For Each line In File.ReadLines(RichTextBox3.Text)
Dim match = Regex.Match(line, pattern)
' check each line and fill sheet
If match.Success Then
sheet.Cells(i, 1).Value = match.Value
i += 1
End If
Next
End If
xl.Visible = True
End Sub
Use Button2 to do the Excel work but remove the line: xl.Visible = True
Put a button on your form called Button3 (or named whatever); set Button3 property Visible = False. Then at the bottom of Button2 click event put Button3.Visible = True
After your Button2 gets clicked you'll have Button3 visible. In Button3 click event put xl.Visible = True
To make this work, you'll need to declare "xl" as a module or class variable. Just put Dim xl as object above your sub and remove the Dim inside your sub; that will do it.
Simple type button1.Hide() when you click on the button or something else it will then hide the button. Hope this helps you out.
Remember, button1.Hide() refers to the first button - if it is the 2nd button it would be button2.hide(), etc.