Userform Listbox multiselect count to hide/unhide number of other controls - combobox

I have a listbox populated by a range, where multiselect (1) is enabled. The selected items (maximum of 10) then populate Label.Captions that have adjacent comboboxes and textboxes requiring further info from the user. I am looking for code that counts the number of selections made from the list box (between 1 and 10) and then hides my relative comboboxes and textboxes (again, 1 to 10). So if 4 selections are made in the listbox, only combo/Textboxes 1 to 4 are visible (combo/textboxes 5 to 10 are hidden).
I have some elements of the code I think I need but can't complete the full procedure.
For lstbxCount = 1 To 10
If lstbxCount = 1 Then
UserForm2.Controls("ComboBox" & lstbxCount).Visible = True
UserForm2.Controls("TextBox" & lstbxCount).Visible = True
Next intCount
But the above, if it worked, I think would only show one combobox with the highest count number and would leave all others hidden. I'm looking for a code that loops from combobox 1 up to the lstbxCount number and shows those, and hides anything higher than the lstbxCount integer.
Thanks.

Your code is on the right path however does require a few adjustments to get it working.
Currently as you suspected your code will only show your controls if ListBox item 1 exists.
Below is a code that will show/hide UserForm controls based on the selected ListBox items.
NOTE: In my test I created:
A UserForm named UserForm1
A ListBox named ListBox1
Three Label named Label1, Label2 & Label3
Three TextBox named TextBox1, TextBox2 & TextBox3
The code has been assigned to the ListBox Change event in the UserForm module, so every time a change occurs on the ListBox it will show/hide the relevant controls.
Private Sub ListBox1_Change()
Dim i As Long
For i = 0 To Me.ListBox1.ListCount - 1
If Me.ListBox1.Selected(i) = True Then
i = i + 1
Me.Controls("Label" & i).Visible = True
Me.Controls("TextBox" & i).Visible = True
i = i - 1
ElseIf Me.ListBox1.Selected(i) = False Then
i = i + 1
Me.Controls("Label" & i).Visible = False
Me.Controls("TextBox" & i).Visible = False
i = i - 1
End If
Next
End Sub
Explanation:
The variable i represents an integer which I'm using as a counter.
The For...Next statement is looping from 0 to the ListBox item count minus 1. This is because the ListBox items have a zero-based index meaning the first item in the listbox is index number 0, the second item is index number 1 etc. So, we loop from 0 (the first listbox item) until the ListCount value minus 1.
NOTE: To clarify, the ListBox Item Index numbers are Zero-Based (starts at 0) where the ListCount property is not (starts at 1). This is also explained a little further on.
If Me.ListBox1.Selected(i) = True Then uses our counter variable as the index number (starting at 0 and increasing by 1 each time the loop iterates) for each ListBox item to determine if it's selected or not.
If it is either selected or not, i = i + 1 is used to increase our counter by 1. Once our counter value has increased we can now reference the correct controls to show/hide.
Once we have done so, we decrease our counter by 1 (i = i - 1) as with each loop iteration the value of i increases. If we don't decrease i before the next loop iteration it we will skip list items which means some items to show/hide controls and some won't.
To elaborate, let's remove the variable i and use the first iteration of the loop as an example;
The first iteration would translate to:
Private Sub ListBox1_Change()
For 0 To Me.ListBox1.ListCount - 1
If Me.ListBox1.Selected(0) = True Then
Me.Controls("Label1").Visible = True
Me.Controls("TextBox1").Visible = True
ElseIf Me.ListBox1.Selected(i) = False Then
Me.Controls("Label1").Visible = False
Me.Controls("TextBox1").Visible = False
End If
Next
End Sub
I'll leave it to you to adjust the code to suit your variables and reference the correct names of objects/controls.

Related

How to hide column in multicolumn listbox using VBA

I am using a 2-dimensional array to load data into a multi-column List Box.
I would like to hide a specific column, but don't know how. I can't just exclude the data — because I want to reference it later as a hidden column — but I don't want the user to see it.
Here is what I have so far:
For x = 0 To UBound(ReturnArray, 2)
NISSLIST.ListBox1.Clear 'Make sure the Listbox is empty
NISSLIST.ListBox1.ColumnCount = UBound(ReturnArray, 1) 'Set the number of columns
'Fill the Listbox
NISSLIST.ListBox1.AddItem x 'Additem creates a new row
For y = 0 To UBound(ReturnArray, 1)
NISSLIST.ListBox1.LIST(x, y) = ReturnArray(y, x) 'List(x,y) X is the row number, Y the column number
If y = 3 Then 'Want to hide this column in listbox
NISSLIST.ListBox1.NOIDEA '<<< HELP HERE <<<, What do I put to hide this column of my multi-column listbox????
End If
Next y
Next x
The ColumnHidden property only applies to RowSource queries and will not include that column in the listbox values.
If you want the values to still be in the listbox and be hidden, the only way to do so in VBA is through the ColumnWidths property.
To hide the 4th column (index 3) you would put the following code:
NISSLIST.ListBox1.ColumnWidths = (";;;0cm")
Or if you want it in a loop like the one in the question:
Dim strWidths As String
For y = 0 To UBound(ReturnArray, 1)
If y = 3 Then
strWidths = strWidths + "0cm;"
Else
strWidths = strWidths + ";"
End If
Next y
NISSLIST.ListBox1.ColumnWidths = (strWidths)
Though I would not suggest doing this in a nested loop as it only needs to be done once.
The widths of the other columns will be equally divided unless you specify a width.
I know this is likely no longer useful to the original poster but it may help others (like me) who still run into this issue. It is quite useful for hiding the Key column for when you want users to be able to manipulate data in the listbox.
Via MSDN
NISSLIST.ListBox1.Column(x,3).ColumnHidden=-1

vb6 looping through an array

I have a simple loop where I go through a bunch of items, and if there are corresponding values (subitems), then mark them completed.
For x = 0 To UBound(strArray)
For Each itmx In lvwThings.ListItems
If strArray(x) = itmx.SubItems(1) Then
itmx.Selected = True
End If
Next itmx
Next x
My problem is, if I have 3 values, say 1,2,3 only the third one will get selected. How do I change it so that all values get selected?

PictureBoxes in an Array

Okay so I've got two problems with this code:
I have 8 picture boxes, I'm trying to run a check on a randomly generated number, to see if it matches the number at the end of the picture box (IE if the random number = 8, another constant gets put into picture box 8 and no other, but only if there isn't already something in it).
I have done it by simply running out every check as a single if statement but... 180 if statements later... that's too much to code.
I'm trying to (as you can see) run it through several while loops. The main part I'm having trouble with is assigning a particular picture box in the array with an image from the array. I can get an image using the imgName and imgPictures(i) using it in the format PictureBox1.Image = imgPictures(i), but not replacing PictureBox1.Image with picBoxes().Image.
Using the MsgBoxes that I've left in the code, the loops and if statements run "Debug1" and "Debug2" but none of the others... Why is this?
I'll leave the code and see what you guys can make of it.
Dim i As Integer = 1
Dim x As Integer = 1
Dim rndnumber As Integer = mathsclass.get_randomnumber()
Dim imgPictures(20) As Image
Dim picBoxes(8) As PictureBox
picBoxes = New PictureBox() {PictureBox1, PictureBox2, PictureBox3, PictureBox4, PictureBox5, PictureBox6, PictureBox7, PictureBox8}
Dim imgName As String = ("_" & i)
imgPictures(0) = My.Resources.ResourceManager.GetObject(imgName)
picBoxes(x).Image = imgPictures(i)
While (i <= 20)
MsgBox("Debug1")
rndnumber = mathsclass.get_randomnumber()
imgName = ("_" & i)
imgPictures(i) = My.Resources.ResourceManager.GetObject(imgName)
x = 0
While (x < 8)
MsgBox("Debug2")
If ((randomnumber = i) & (randomposition = x)) Then
MsgBox("Debug3")
picBoxes(x).Image = imgPictures(i)
Else
While (rndnumber = randomnumber)
MsgBox("Debug4")
rndnumber = mathsclass.get_randomnumber()
End While
MsgBox("Debug5")
If ((randomnumber <> rndnumber) & (randomposition <> x)) Then
MsgBox("Debug6")
imgName = ("_" & rndnumber)
imgPictures(i) = My.Resources.ResourceManager.GetObject(imgName)
picBoxes(x).Image = imgPictures(i)
End If
MsgBox("Debug7")
End If
MsgBox("Debug8")
x += 1
End While
MsgBox("Debug9")
i += 1
End While
Am not really sure which language you are trying to use here. I mean, whether it is VB.Net or class VB6!
I assume it is VisualBasic.Net (VB.Net).
Am not sure whether I understood you clearly. So, am giving you a small example code for you to try it out. The comments in it would pretty much explain I believe.
First create a new blank project. Add 5 textboxes in it(no need to change the names of it. Just drag and drop 5 nos. of textboxes). Add a Button and then double click on it and paste the code that I have shown below:
Dim rand As New Random '~~~ for creating the random numer
Dim intTotal As Integer = 5 '~~~ I have 5 textboxes with the names ending by number from 1 to 5
Dim intUpdated As Integer = 0 '~~~ to store the total number of updated textboxes
'~~~ first of all we are looping through the textboxes(am just showing how you can fetch them via it's name. There are other ways to iterate through the controls though!)
For i As Integer = 1 To intTotal
Dim tbox As TextBox = DirectCast(Me.Controls.Item("TextBox" & i.ToString()), TextBox) '~~~ here, we are fetching the TextBox via it's "Name" property, by appending the number at the end of "TextBox". Because in my form, I have "TextBox1", "TextBox2", "TextBox3", "TextBox4" and "TextBox5"
tbox.Tag = "untouched" '~~~ just setting it's "Tag" property to some text so that we could later use it evaluate whether we have touched this textbox via the random number
'-------------------------------------------------
'if you want to do something else like loading up default text or something, you can do it here too
'-------------------------------------------------
Next
'~~~ the randomly choosing part..
Dim j As Integer = 1
Do While intUpdated < intTotal '~~~ loop until we have taken into consideration all the textboxes via random number
Dim r As Integer = rand.Next(1, intTotal + 1) '~~~ create a random number between 1 and 5 (both inclusive)
'~~~ fetch that TextBox via it's name. Note that, am appending the random number to the name "TextBox". For eg, if the random number generated was "2", it would access "TextBox2"
Dim tbox As TextBox = DirectCast(Me.Controls.Item("TextBox" & r.ToString()), TextBox)
'~~~ since we have textbox now, we are checking whether we have accessed it earlier via random number
If tbox.Tag.ToString() = "untouched" Then '~~~ if not accessed before...
tbox.Tag = "touched" '~~~ mark the textbox as accessed via random number
intUpdated += 1 '~~~ increment the counter variable that we are using for storing the number of textboxes we accessed
'-------------------------------------------------
'~~~ do whatever you want on it here.. Right now, am just inserting some text on it to show the order in which the textboxes are accessed via our random number
tbox.Text = "updated" & j.ToString
j += 1
'-------------------------------------------------
End If
Loop
After running it, I hope you will be able to get the idea. To access a control name residing in your form, you could access it like this: Me.Controls.Item("control_name_goes_here"). So you can append numbers to the string when you mention the control's name. I have used this in code above. Look at it.
BTW, you are using & which is in VB, will act like concatenation operator! ie, Dim a As String = "Akhilesh" & "B Chandran". This will store Akhilesh B Chandran in that variable a. For AND operation, you need to use AND. In C, C++, C#, etc it is && (note that there is two ampersand characters)
Also, in VB.Net, you should use MessageBox.Show() instead of the MsgBox() function. That's the better way.
Hope this will help.

How to prevent dynamic array from including a blank first element

I'm really struggling with why my array keeps including an empty element as its first element.
I'm populating an array from the selected choices in a listbox and I keep getting an empty first element in the array. I'm not getting any empty elements afterwards, just for the first element.
I've looked at lots of code to remove an empty element, all of which is beyond my understanding of arrays at this point, but that is not ideally for what I'm looking: I'm looking to prevent that empty element from appearing in my array in the first place.
Would anyone please help me understand what I need to change? I've tried using Option Base 1 and iterating from 1 instead of 0, but I get an error when trying to do so. Additionally, I did Redim my array as (0 To 0) and (0 to UBound) as well but nothing changed from my code below.
I'm assuming it has to do with the way I'm iterating through my listbox elements but I'm not sure how to fix the issue.
ReDim Part_Number_Array(1 To 1) As Variant
For Selected = 0 To Part_Number_Select.ListCount - 1
If Part_Number_Select.Selected(Selected) = True Then
ReDim Preserve Part_Number_Array(1 To UBound(Part_Number_Array) + 1) As Variant
'Add Part Number to the Array
Part_Number_Array(UBound(Part_Number_Array)) = _
Part_Number_Select.List(Selected)
'UpperBoundCount = UBound(Part_Number_Array)
'MsgBox "The upper bound is " & UpperBoundCount
End If
Next Selected
Thanks in advance for your help.
That is a known vb limitation which causes us all some "Empty/not empty" and member-count issues.
I have dealt with this by:
Define a class with Add/Delete/Find/etc methods no showing the array and provide a Count property. Also, you can use a Collection (and then there are some interesting derivatives, like the Dictionaries)
OR
OPTION BASE 0 (to be more C like) and use a _Count variable, initialized to 0, to keep track exactly how many useful items does the array hold and do Redims 0 to NewCount+1.
I think it is because you are redimming 1 to 1 outside the loop, then inside the loop you are redimming again before adding another element to it, then when you do add the element you are using the ubound. So it seems what happening is, you're redimming 1 to 1, which gives the array 1 empty slot. then, inside the loop you are redimming it 1 to UBound(Part_Number_Array) + 1). the + 1 at the end adds another slot (ubound is currently 1, 1 + 1 = 2) and then, when you add the element you are using Part_Number_Array(UBound(Part_Number_Array)) = so that statement puts the element at the ubound of the array, which is 2, leaving the first spot blank.
The solution:
ReDim Part_Number_Array(1 To 1) As Variant
For Selected = 0 To Part_Number_Select.ListCount - 1
If Part_Number_Select.Selected(Selected) = True Then
'Add Part Number to the Array before redimming, thus putting the new element in the 1 empty slot
Part_Number_Array(UBound(Part_Number_Array)) = _
Part_Number_Select.List(Selected)
'UpperBoundCount = UBound(Part_Number_Array)
'MsgBox "The upper bound is " & UpperBoundCount
ReDim Preserve Part_Number_Array(1 To UBound(Part_Number_Array) + 1) As Variant
End If
Next Selected
then, after the loop you can do - ReDim Preserve Part_Number_Array(1 To UBound(Part_Number_Array) - 1) notice the minus 1, this will eliminate the last slot (which is empty because of having a redim statement without adding another element)

DataGrid Row Removal Resulting in Error

I have a datagrid that I programatically add a certain number of user control objects to in row definitions based on a user's selection. When the user selects a new option, I'm trying to clear each of the rows besides the header, but I keep finding that the last row definition ends up visually on the top of my header for some reason. Here's my code:
Here's where I add a row with the user control in it:
For Each qTask As QTask In QBD.Tasks
Dim rowDef As New RowDefinition
rowDef.Height = New GridLength(0, GridUnitType.Auto)
grdQBD.RowDefinitions.Add(rowDef)
Dim newTask As New QBDControls
newTask.DataContext = qTask
...
Grid.SetRow(newTask, grdQBD.RowDefinitions.Count - 1)
Grid.SetColumn(newTask, 0)
Grid.SetColumnSpan(newTask, 7)
grdQBD.Children.Add(newTask)
Here's where I remove the rows (all but the first, my header):
Dim rows As Integer = grdQBD.RowDefinitions.Count
If rows > 1 Then
For child As Integer = rows - 1 To 1 Step -1
grdQBD.RowDefinitions.RemoveAt(child)
'I'VE ALSO TRIED .REMOVERANGE
Next
End If
Notice that the last row covers the header.
Well, your problem is that you are just removing the RowDefinitions and not the actual UserControls.
Since there is just one RowDefinition left, all UserControls will be in the remaining one, all on top of each other, with the last one added as the topmost one.
To get the behavior you want, I guess you have to remove the UserControls as well.
You can do that by looking through the children of the Grid, and removing the one with the current row index in the loop.
Dim rows = g1.RowDefinitions.Count
If rows > 1 Then
For i As Integer = rows - 1 To 1 Step -1
g1.RowDefinitions.RemoveAt(i)
' Find the control in the row and remove it
Dim controlToRemove = g1.Children.Cast(Of UIElement)()
.Single(Function(c) Grid.GetRow(c) = i)
g1.Children.Remove(controlToRemove)
Next
End If

Resources