Why doesn't it remove both/all elements?
Code:
$AAD = New-Object system.Windows.Forms.CheckBox
$AAD.Name = 'AAD'
$AAD.text = 'AAD'
$AAD.Checked = $true
$AAD.AutoSize = $true
$AAD.width = 47
$AAD.height = 30
$AAD.enabled = $true
$AAD.location = New-Object System.Drawing.Point(12, 76)
$AAD.Font = New-Object System.Drawing.Font('Microsoft Sans Serif', 10)
$AD = New-Object system.Windows.Forms.CheckBox
$AD.Name = 'AD'
$AD.text = 'AD'
$AD.Checked = $true
$AD.AutoSize = $true
$AD.width = 45
$AD.height = 20
$AD.location = New-Object System.Drawing.Point(64, 76)
$AD.Font = New-Object System.Drawing.Font('Microsoft Sans Serif', 10)
$array = @('AAD', 'AD')
$Form.Controls | where { $array -contains $_.Name } | foreach { $Form.Controls.RemoveByKey($_.Name) }
If i log the name of which elements it finds it logs this (So it does find both, but doesn't remove them both):
AAD
AD
I can't explain why it's failing to remove one of the controls but I can provide you a more robust way of doing it that will not have such issue using .Remove
instead of .RemoveByKey
:
$form.Controls.AddRange(@($AD; $AAD))
'AAD', 'AD' | ForEach-Object {
foreach ($control in $Form.Controls.Find($_, $true)) {
$form.Controls.Remove($control)
}
}
$form.Controls.Count # 0
Following up on Jimi's helpful comment:
The reason why half of the Controls remain is that removing elements in a
foreach
loop modifies the collection you're iterating over. A backwards for loop is one of the common counter-measures -- In general, you should dispose of the Controls that are not need anymore instead of removing them.Controls.Remove()
doesn't dispose anything (this behavior is somewhat different in .NET 5+, though).
Modifying the collection while iterating it can explain OP's issue and the solution being a reverse for
loop also works to solve the problem. I would still personally stick to the previous method shown in this answer using .Find
and .Remove
.
$array = @('AAD', 'AD')
for ($i = $form.Controls.Count - 1; $i -ge 0; $i--) {
$control = $Form.Controls[$i]
if ($control.Name -in $array) {
$Form.Controls.RemoveByKey($control.Name) # -> Not needed
# Disposing the control also removes it from the control collection
$control.Dispose()
}
}
$form.Controls.Count # 0