Invalid Conversion error - vb.net

I recently upgraded a VB 6 project to .net. I'm having a problem with this block of code:
Dim CtrlName As System.Windows.Forms.MenuItem
For Each CtrlName In Form1.Controls
'Some code here
Next CtrlName
Now this code compiles but throws the following runtime error:
Unable to cast object of type 'System.Windows.Forms.Panel' to type 'System.Windows.Forms.MenuItem.
I have a panel control on the subject form. How do I resolve this?
Thanks.

You are iterating over all controls that are directly inside the form, not just the MenuItems. However, your variable is of type MenuItem. This is causing the problem.
For normal controls (e.g. Buttons), you’d want to use the following, easy fix; test inside the loop whether the control type is correct:
For Each control As Control In Form1.Controls
Dim btt As Button = TryCast(control, Button)
If btt IsNot Nothing Then
' Perform action
End If
Next
However, this does not work for MenuItems since these aren’t controls at all in WinForms, and they aren’t stored in the form’s Controls collection.
You need to iterate over the form’s Menu.MenuItems property instead.

The items in the Controls property of a form, which may or may not be MenuItem. Assuming that you just want to iterate over MenuItem objects you can change your code to:
For Each menuControl As MenuItem In Me.Controls.OfType(Of MenuItem)
' Some code
Next
Note that the menuControl variable is declared in the For so is only accessible within the block and is disposed automatically.

for each ctrl as control in me.controls
if typeof ctrl is menuitem then
' do stuff here
end if
next
typeof keyword allows you to test the type of control being examined in the control collection.
Found the answer after a bit of research, you need to search for the menu strip first and then loop through the items collection.
For Each ctrl As Control In Me.Controls
If TypeOf ctrl Is MenuStrip Then
Dim mnu As MenuStrip = DirectCast(ctrl, MenuStrip)
For Each x As ToolStripMenuItem In mnu.Items
Debug.Print(x.Name)
Next
End If
Next

Related

How does Tab Index work if the control's enabled property is false

If I'm viewing a form and I set the enabled property of the control with tab index = 0, does the cursor then move to the next tab index? Do I need to, and is there a way, to force the tab to set to the first control with Enabled = True?
So in order to achieve this (assuming there are no panels on your form), this is how you could iterate through the controls in tab order. The first control which you encounter and which is enabled, you set the focus on it and leave the Sub. The myFirstControl variable is initialized by you with the first control in the tab order list of the form.
Private Sub IterateControls()
Dim ctrl As Control = myFirstControl
While ctrl IsNot Nothing
If ctrl.Enabled = True Then
Me.ActiveControl = ctrl
Exit Sub
End If
ctrl = Me.GetNextControl(ctrl, True)
End While
End Sub
If you have panels also, you should build a dictionary of panels (with the panel as key, its first control as value) and take them one by one using a For loop. The For loop should be placed to include the whole method's code, but this time you initiate the ctrl variable with the first control from the panel (i.e. the value of the current dictionary entry), instead of the first control of the Form, and also you would call myPanel.GetNextControl(...) instead of Me.GetNextControl(...). The other code lines should remain the same. If this is not helpful enough, add a comment and I will edit my answer.

Loop - set Textboxes to ReadOnly in all Groupboxes

I have groupboxes that contain textboxes and I want to set all of them to ReadOnly=True. Here is what I tried (doesn't work):
EDIT (I forgot about Split container):
For Each SplitCon As Control In Me.Controls
If TypeOf SplitCon Is SplitContainer Then
For Each GBox As Control In SplitCon.Controls
If TypeOf GBox Is GroupBox Then
For Each ctrl As Control In GBox.Controls
If TypeOf (ctrl) Is TextBox Then
CType(ctrl, TextBox).ReadOnly = True
End If
Next
End If
Next
End If
Next
You can simplify things a great deal. Rather than looking thru all sorts of Control collections, create an array to act as a ToDo list. Then you can get rid of all the TypeOf and CType in the loop used.
' the ToDo list
Dim GrpBoxes = New GroupBox() {Groupbox1, Groupbox2,
Groupbox3, Groupbox4}
For Each grp In GrpBoxes
For Each tb As TextBox In grp.Controls.OfType(Of TextBox)()
tb.ReadOnly = True
Next
Next
Your code no longer depends on the form layout of the moment. The only thing you have to remember is to add any new GroupBox items to your list. You can also declare the array once ever for the whole form if you prefer (or even in the For Each statement)
Rather than working with Control objects, Controls.OfType(Of T) filters the collection and returns an object variable of that type, so there is no need to cast it or skip over controls you are not interested in. You can also tack on a Where method to further refine the list to include only do those with a certain name or Tag.

How do I iterate through Components on a Windows Form?

I'm using VisualStudio 2008 on a system using .NET Compact Framework 3.5 in VB.NET and have a form that contains a couple of Components, namely Timers and Messageboxes. Part of my application is a sub that iterates through all the Controls in the form and adjusting properties like the Front or BackColor so that we can reskin the entire application on demand.
While I'm iterating through these Controls I'd also like to iterate through the Components and set some properties of the Messageboxes on the form. I've tried using a For Each to access Me.components.Components but that collection appears to be private.
For Each comp As Object in Me.components
<do something>
Next comp
Is there a way to iterate through the components?
EDIT:
I was wrong thinking that me.components.Component is private. Using Me.components.Components I get the following error:
'Components' is not a member of 'System.ComponentModel.IContainer".
You can loop throught timers with this: ....
For Each c As Object In Me.components.Components
If TypeOf c Is Timer Then
Dim tim As Timer = CType(c, System.Windows.Forms.Timer)
tim.Interval = 12345
End If
Next
Change the .Interval = 12345 with anithing you want to do to the timers.
.... And through other controls with:
For Each ctrl As Control In Me.Controls
If (ctrl.GetType() Is GetType(TextBox)) Then
Dim txt As TextBox = CType(ctrl, TextBox)
txt.BackColor = Color.LightYellow
End If
Next
Change the TextBox with the type of control
And .BackColor = Color.LightYellow with anything you want to do with the control.
Unfortunatly i don't know how to do is with MesseageBoxes :(

Catch exception in iteration of ContextMenuStrip items

I'm trying to iterate over contextmenustrip items like this:
Public Sub TranslateContextMenuStrip(ByRef u As ContextMenuStrip)
For Each t As ToolStripMenuItem In u.Items 'here the error occurs
pProcessMenuItem(t) 'not here
Next
End Sub
But I have toolstrip separators in the contextmenustrip, and I'm getting the error
"System.InvalidCastException: The object of type System.Windows.Forms.ToolStripSeparator can't be converted to type System.Windows.Forms.ToolStripMenuItem"
as soon as it stumbles over a separator.
I wonder why this separator is included in items (I am requesting "For Each t As ToolStripMenuItem" so why does it return non-ToolStripMenuItems???) and how to catch this error or avoid it.
I found a solution, but not the problem:
For Each it As Object In u.Items
If TypeOf it Is ToolStripMenuItem Then
pProcessMenuItem(it)
End If
Next
I dont think For Each t As ToolStripMenuItem does what you might think it does.
It simply declares that the t iterator will be of Type ToolStripMenuItem. It does nothing to the item collection itself. When you come to seperator, you get the cast exception because a separator cannot be converted to a menu item.
Items is a collection of ToolStripItems. This is a base class used for all the Types a context menu can hold (menu item, combo, text box or separator). Since these all inherit from ToolStripItem the collection can hold any of them (specifically, a ToolStripSeparator item is also a ToolStripItem)
There are several ways to iterate or work with just the menu entries:
Filter the Items collection
For Each tsi As ToolStripMenuItem In u.Items.OfType(Of ToolStripMenuItem)()
' do something fun with tsi
Next
The OfType() extension filters the items collection to just menu items.
This is by far the simplest because your iterator tsi is the proper type. This is particularly true if your method is writen to expect menu items:
Sub pProcessMenuItem(item As ToolStripMenuItem)
The tsi iterator is same Type expected by the method, so no further steps are needed. Anything else will require casting either in the method or to call the method (or Option Strict Off).
Test the Type:
' iterate all the items
For Each tsi As ToolStripItem In u.Items
' test the type of each
If TypeOf tsi Is ToolStripMenuItem Then
' do something fun with tsi
End If
Next
Under Option Strict passing tsi to a method declared as shown above wont compile. You would have to cast before invoking your method:
pProcessMenuItem(CType(tsi, ToolStripMenuItem))
If the method is declared to accept a ToolStripItem or Object, the cast would have to take place in the method if you need to access any menu related properties.
The same is true using As Object to iterate:
For Each it As Object In u.Items
If TypeOf it Is ToolStripMenuItem Then
pProcessMenuItem(it)
End If
Next
The only way this will compile under Option Strict if the method argument is declared As Object. As above, Object may have to be cast to ToolStripMenuItem. The first method prevents any need for this.

Handle containers inside containers in for each control vb 2008

I have created a function to translate my forms. I can loop through every control in a form to call this function, but I have made a situation, I cannot handle.
In one of my forms, I have groupbox in a groupbox.
This source works if I only have one groupbox.
Public Function translate_form(ByVal form As Form)
Dim control As Object
Dim controlname As String
form.Text = Get_Control_Name(form.Name, "Form")
Try
For i = 0 To form.Controls.Count - 1
control = form.Controls(i)
If TypeOf (control) Is MenuStrip Then
For j = 0 To control.items.count - 1
control.items(j).text = Get_Control_Name(form.Name, "MenuItem" & j)
Next
Else
controlname = Get_Control_Name(form.Name, control.Name)
control.Text = IIf(controlname Is Nothing, control.Text, controlname)
If TypeOf (control) Is GroupBox Then
For j = 0 To control.Controls.Count - 1
controlname = Get_Control_Name(form.Name, control.Controls(j).Name)
control.Controls(j).Text = IIf(controlname Is Nothing, control.Controls(j).Text, controlname)
If TypeOf (control.Controls(j)) Is Button Then
control.Controls(j).AutoSize = True
End If
Next
End If
If TypeOf (control) Is Button And UCase(control.Text) <> "X" Then
control.AutoSize = True
End If
End If
Next
Catch ex As Exception
MsgBox(ex.Message, MsgBoxStyle.Critical)
End Try
End Function
But in some cases I want to sperate controls inside a container. I could have one more loop if the
control.Controls(j)
is a groupbox but I want to make this function to handle any kind of "container pyramid", if you know what I mean. Maybe I will have a container which has one also and that one too etc... Or is there any control I can use as a groupbox but it doesn't count as a contaier, so I can see it with:
form.Controls
Any suggestions?
Thanks in advance.
The reason why your code does not deliver what you want is that you are not performing a recursive search of controls. Bear in mind that Form.Controls contains only the parent controls (not the children controls eventually contained by the parents; like the situation you refer of controls contained by a GroupBox). Additionally, I see various not-too-right issues (you should writeOption Strict On on the top of your file) and that's why this answer intends to provide you with a somehow better framework to work with (you have just to fill in the blanks with your code):
Public Sub translate_form2(ByVal form As Form)
Try
For Each ctrl As Control In form.Controls
actionsCurrentControl(ctrl)
recursiveControls(ctrl)
Next
Catch ex As Exception
End Try
End Sub
'Accounting for all the child controls (if any)
Public Sub recursiveControls(parentControl As Control)
If (parentControl.HasChildren) Then
For Each ctrl As Control In parentControl.Controls
actionsCurrentControl(ctrl)
recursiveControls(ctrl)
Next
End If
End Sub
Public Sub actionsCurrentControl(curControl As Control)
If TypeOf curControl Is MenuStrip Then
Else
If TypeOf (curControl) Is GroupBox Then
End If
If TypeOf (curControl) Is Button And UCase(curControl.Text) <> "X" Then
End If
End If
End Sub
translate_form2 iterates through all the parent controls as in your code (but by relying on a set of Subs (you are wrongly using a Function without returning any value, what is wrong), making the structure more adaptable); it also calls recursiveControls (which also calls itself for each control it analyses) to take care of any child control which might be present. I am also including actionsCurrentControl which contains all the actions to perform for each control (you have to populate it with your code).