me.control.remove is removing every second control in a loop for some reason - vb.net

I have some dynamically created checkboxes on my form and I want a function to delete them all.
I've got the following function:
Sub delete_checkboxes()
Dim radios = Controls.OfType(Of RadioButton).AsQueryable()
For Each r As RadioButton In radios
Me.Controls.Remove(r)
Next
End Sub
For some reason the above function only deletes every second radio button and leaves the rest.
Just as a test, I changed the function to delete radio buttons which are ticked:
Dim radios = Controls.OfType(Of RadioButton).AsQueryable()
For Each r As RadioButton In radios
If r.Checked Then
Me.Controls.Remove(r)
End If
Next
With the above I can tick each radio button and it will delete them invididually... so what it is in the first function which could be causing it to skip every second radio button?

Change AsQueryable() to ToList()
The reason it fails is that you are not supposed to modify a iterator while you are still looping over it. AsQueryable() is just using a state machine internally to know your current position in the Me.Controls collection. It doesn't actually keep it's own collection of controls, but just knows which controls you need from your original collection.
When you remove a control in the middle of loop, that position state is now wrong... in fact, it's off by one. You then remove the next control, which puts that internal position state off by one again, and so on. After a whole set of off-by-one adjustments and you end up with half of the controls still on your form.
ToList() will work, because it creates a separate collection for your controls, so that you don't have to mess with that state as your remove them from your Me.Controls collection.

this is usually how I accomplish it.
For Each cont As Control In Me.Controls
If cont.GetType().Name = "RadioButton" Then Me.Controls.Remove(cont)
Next

Related

Me.Controls does not find all controls on the form

I have a form that takes a list of search terms, and creates a combo box for each result set. The boxes get created at runtime, they are all individually named, and are all directly on the form, none are in panels or any sub controls.
I want to remove the previous searchs combo boxes every time a new search is done. I made the following sub to remove all combo boxes
For Each c As Control In Me.Controls
If c.GetType Is GetType(ComboBox) Then
Me.Controls.Remove(c)
c.Dispose()
End If
Next c
But for some mysterious reason, the Form doesn't see all the combo boxes the previous search created. If I create 3 boxes, it will see the first and third, but not the second. If I run the sub again, it will see the second one and catch it that time. So I need to run the sub twice to actually clear out all the comboboxes that were created. I checked the control collection and it seems to always be 1 short of the actual number of combo boxes that were created in the previous search
Any idea why it is always leaving a combo box behind and I need to clear them all again a second time to get rid of it?
Further to what Jimi said in the comments, I would recommend that you use this:
For Each cmbx In Controls.OfType(Of ComboBox)().ToArray()
cmbx.Dispose()
Next
The OfType does the filtering first and then the ToArray creates an array that you can loop over, so you're not enumerating and modifying a collection at the same time. There's no point removing and disposing the controls because disposing will implicitly remove them.

MS ACCESS VBA - best way to change all properties values of a form's textboxes at once

I have a form with 3 textboxs and i want to change their properties values according to some events.
My idea is to set these properties to a global form's variables in order to use them whenever i want with a function or module (this depends on your suggestion).
Assuming that i want to change all textboxs.enabled property by pressing a button
and i want to change another different property if needed in the future (like bordercolor),
my example is this:
in the form VBA,
Option Compare Database
Option Explicit
Private Const AllTxtboxes = *** all txtboxes ***
in the VBA button
Private Sub button1_Click()
AllTxtboxes.enabled = True
End sub
Thanks in advance.
You need to loop trough all controls and identify those you want to change. The problem is that each control type got their own properties, so you need to make sure you are in the right one.
And for that, there is a property common to all controls named Tag
There, you can specify a value and check it. It's really helpful to select only what you want.
I made a simple form with 6 textboxes:
Note that in design view, properties panel at right, tab others, last property is Tag (sometimes it comes as aditional info or something like it).
There, I typed 99 but only on textboxes 2, 4 and 6, because those are the textboxes I want to handle (that would be kind of your array of textboxes)
I added a command button to change forecolor of those textboxes to red when clicked. My code is:
Private Sub CMD_CHANGE_FONT_COLOR_Click()
Me.Painting = False
Dim MyControl As Control
Dim MyTxt As TextBox
For Each MyControl In Me.Controls
If MyControl.Tag = 99 Then
'we set MyTxt to MyControl so we can use Intellisense, not really needed, but it makes coding easier
Set MyTxt = MyControl
MyTxt.ForeColor = vbRed
Set MyTxt = Nothing
End If
Next MyControl
Me.Painting = True
End Sub
When the form loads, you will see:
But after click on command button, the forecolor of those 3 textboxes will be red:
There you go. So everytime you want to do something with that array of textboxes, you can use this code to loop and change the properties you want.

How do I properly use if/then for a bolean value in order to make an object visible?

I want to display an endless access form. For each data set there is a yes/no value (PA). If yes, then a hidden object should be displayed. It seems very straight forward, but it doesn't work.
I have tried by changing the value of PA to 1, 0, -1. Either nothing happens, or the object will be displayed for all data sets.
The object is defined as hidden in the form.
Private sub form_current()
If PA.value = true Then
me.object.visible = True
End if
End Sub
I would be very happy for some advice. /LP
Handle the control's Change event, and then you can assign to its value:
Private Sub PA_Change()
Me.object.Visible = PA.Value ' TODO: give 'object' an actual name
End Sub
Find PA in the top-left codepane dropdown, then select the Change event in the top-right code pane dropdown if it's not automatically selected - the VBE will generate the event handler procedure for you.
As the object is unbound, you can't do this. When unbound, it will either be visible or not - for all records.
One workaround is to move the control to a tiny subform having a master/child relation to the main form.

Getting reference to control from function

My application has a form which contains 4 tabs and each tab has a DataGridView (DGV) within it. There is also a panel of controls which sit outside of the tabs and are used for all the DGV controls.
I need to make sure that the code updates the correct DGV based on what the user is looking at. I currently do this for any events or functions where I need to do some work on a DGV:
Dim dgv As DataGridView = GetCurrentDGV(TabControl.SelectedTab)
For Each row As DataGridViewRow In dgv.SelectedRows
'Do work on row
Next
And this is the function GetCurrentDGV():
Private Function GetCurrentDGV(ByRef tab As TabPage) As DataGridView
For Each control As Control In tab.Controls
If control.GetType Is GetType(DataGridView) Then
Return CType(control, DataGridView)
End If
Next
End Function
There are a number of things I don't like about this.
Firstly, it is slow. I'm not exactly sure why as each tab only contains a DGV and nothing else (so the loop through controls is only a single item which should return on the first loop).
Secondly, there's a chance my function doesn't return anything which will give a null reference.
I am trying to avoid hard-coding the relationship between tabs and DGV but before I go down that route, are there better ways I could be doing this?

Re-Creating Dynamic Controls

I have a VB.Net WinForm Program.
I dynamically create panels with controls.
Each panel has:
2 Labels
1 DataGridView
1 Button
Everything works fine the first time I create the panels.
Everything gets created, and everything is functional.
If I have to re-create the form, I get rid of the existing panels (and their controls) with this code:
For P = 0 To Panels.Count - 1
For Each PControl In Panels(P).controls
Panels(P).controls.remove(PControl)
Next
Me.Controls.Remove(Panels(P))
Next
Panels.Clear()
DataGrids.Clear()
lblCounts.Clear()
Where:
Panels, DataGrids, & lblCounts are ArrayLists holding controls
When I re-create the panels, I get the panels and all of their controls except Buttons
When I step through the debugger, I see the buttons being removed, and I see them being created, but they don't appear in the panel
Any ideas?
Your question is regarding a button not appearing when you are adding the controls, but you are only showing the removal process, which is flawed.
Make a UserControl that holds your Labels, Grid and Button. Add that to your form. That's what UserControls are for.
Also, when you are done using it, just call:
MyControl.Dispose()
Otherwise, I suspect you are leaking memory. Remove does not destroy the object.
For Each PControl In Panels(P).controls
Panels(P).controls.remove(PControl)
Next
This part may kick you out of your code. The 'For Each' does not like it when its items change during execution. check it with Breakpoints. if is is really a problem , you could do..
lazy method, by just adding .ToList
For Each PControl In Panels(P).controls.ToList
Panels(P).controls.remove(PControl)
Next
similar to:
Dim AllControls as New List(Of control)
AllControls.AddRange(Panels(P).controls)
For Each PControl in AllControls
Panels(P).controls.remove(PControl)
Next
or:
For i as integer = Panels(P).controls.count -1 to 0 step -1
Dim PControl as control = Panels(P).controls(i)
PControl.parent.remove(PControl)
Next
Try this
WHILE Panels(P).controls.count > 0
Panels(P).controls.removeAt(1)