What is wrong with this LINQ code? - vb.net

First, I have function to flatten all controls on a Control:
Protected Function GetAllControls(Optional ownerControl As Control = Nothing) As IEnumerable(Of Control)
Dim ret = New List(Of Control)()
For Each child As Control In If(ownerControl, Me).Controls
ret.AddRange(GetAllControls(child))
Next
ret.Add(ownerControl)
Return ret
End Function
Then, I want to hide certain buttons on a control using this code:
Dim buttons = GetAllControls().Where(Function(c) c.Name.StartsWith("subButton"))
For Each ctrl As Control In buttons
ctrl.Visible = False
Debug.WriteLine("Hid button " & ctrl.Name)
Next
Yet, after four buttons - the correct count - have been hidden, I get a NullReferenceException, with VS2012 highlighting the lambda expression.
What could possibly cause this?

The last line in your first function adds ownerControl, which is null the first time you call it, so its adding a Nothing to the list. In your lambda you're doing a c.Name which will throw an exception when c is Nothing.

Related

Unable to get the Event associated with a menu element to create another menu element

Hello everyone and thanks in advance for taking the time to read my question.
My scenario is as it follows: I have a fully developed software with a ToolStripMenu populated with all the DropDown menus with more elements inside.
I want to add a new feature, where I can create a new ToolStrip under the main menu with some of the "favourites" elements, something like a booksmark bar from some internet browser.
The list of favourite elements is stored in our DataBase, so I've managed to loop through every element in the main menu and its DropDownItems and locate which specific Meny Item corresponds to the favourite.
In this situation, I tried to get the EventHandlers associated with this Item, so I could pass it in the instance of the new Item that will go in the new ToolStripMenu. I've done this using an EventHandlerList.
However, eventhough this list is filled with te EventHandler for the MenuItem, I can't acces it so I could use it in the instance of the new ToolStripButton so it will fire the same method as it "twin" on the main menu. Apparently I need to use an Object named "Key", but I haven't been able to know how to get it.
I will leave the piece of code that I've come to so far: the object M_DtFavs is a DataTable with all the favourite items and "NomOpc" is the Name of the item in the main menu.
For Each L_Dr As DataRow In M_DtFavs.Rows
For Each L_Menu As ToolStripMenuItem In M_MainMenu.Items
L_Items = L_Menu.DropDownItems.Find(L_Dr("NomOpc"), True)
If L_Items.Length > 0 Then
L_EventList = getEventHandlers(L_Items(0))
Exit For
End If
Next
'L_Key -> I need this to acces the event, but I don't know what to use
If L_Items.Length > 0 Then
L_Cmd = New ToolStripButton(L_Dr("NomOpc"), My.Resources.Nuevo, L_EventList(L_key), L_Dr("NomOpc"))
L_Cmd.DisplayStyle = ToolStripItemDisplayStyle.Text
L_Cmd.ImageTransparentColor = System.Drawing.Color.Magenta
L_Cmd.Size = New System.Drawing.Size(23, 23)
L_Cmd.ToolTipText = L_Dr("NomOpc")
L_Cmd.Text = L_Items(0).Text
M_ToolBarResaltados.Items.Add(L_Cmd)
End If
Next
And here is the Function I use to get the list of handlers associated with the menu item:
Function getEventHandlers(cmpnt As System.ComponentModel.Component) As System.ComponentModel.EventHandlerList
Dim strMethodName = New System.Diagnostics.StackTrace().GetFrame(0).GetMethod().Name '...this procedure's name
Dim propInfo As System.Reflection.PropertyInfo
Dim value As System.ComponentModel.EventHandlerList = Nothing
Try
propInfo = GetType(System.ComponentModel.Component).GetProperty("Events",
Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Public Or
Reflection.BindingFlags.Static Or Reflection.BindingFlags.Instance Or Reflection.BindingFlags.DeclaredOnly)
If propInfo IsNot Nothing Then
value = CType(propInfo.GetValue(cmpnt, Nothing), System.ComponentModel.EventHandlerList)
End If
Catch ex As Exception
Finally
propInfo = Nothing
End Try
Return value
End Function
¡Thanks again!

Loop through all Checkboxes on a form [duplicate]

This question already has answers here:
loop over all textboxes in a form, including those inside a groupbox
(4 answers)
Closed 2 years ago.
I need to loop through all Checkboxes on a form and get the Tag property for each. The Checkboxes are in Groupboxes, and nested Groupboxes. Code I have works for the main form, it is not getting the values from the Checkboxes in the Groupboxes
...
i = 0
For Each ctrl As Control In Me.Controls
If (TypeOf ctrl Is CheckBox) Then
'Resize array
ReDim Preserve g_str_Array(i)
'Add Tag to array
g_str_Array(i) = CStr(ctrl.Tag)
i += 1
End If
Next
...
Here's a method that you can add to a form that provides access to every control on the form via the Tab order:
Public Iterator Function GetControls() As IEnumerable(Of Control)
Dim ctrl = GetNextControl(Me, True)
Do Until ctrl Is Nothing
Yield ctrl
ctrl = GetNextControl(ctrl, True)
Loop
End Function
Because that is an iterator, you can chain other LINQ methods to it. To get the Tag of each CheckBox into an array:
Dim checkBoxTags = GetControls().OfType(Of CheckBox)().
Select(Function(cb) CStr(cb.Tag)).
ToArray()
If you wanted to use this method for multiple forms then, rather than repeating the code in each of them, you can add a single extension method:
Imports System.Runtime.CompilerServices
Public Module FormExtensions
<Extension>
Public Iterator Function GetControls(source As Form) As IEnumerable(Of Control)
Dim ctrl = source.GetNextControl(source, True)
Do Until ctrl Is Nothing
Yield ctrl
ctrl = source.GetNextControl(ctrl, True)
Loop
End Function
End Module
and then call it in each form as though it were a member.

Change Label Text with Controls in VB

I'm trying to change a Label Text with Controls command, using this line
Controls("C_" & 0).Text = "Conta:"
But I get this error
"System.NullReferenceException"
If I delete this label and change it for a textbox (with same name "C_0"), it works! But I need to do this with a Label not a textbox...
This is because you do not have a control named C_0. I would suggest using ControlCollection.Find to get your control and then use a conditional If statement to check if the returned control exists:
Dim desiredControls() As Control = Me.Controls.Find("C_" & 0, True)
If desiredControls.Count = 0 Then
'No controls named C_0 found
ElseIf desiredControls.Count > 1 Then
'Multiple controls named C_0 found
Else
desiredControls(0).Text = "Conta:"
End If
Or if you simply wanted a one-liner then you would use:
Me.Controls.Find("C_" & 0, True).First().Text = "Conta:"
However, I would highly recommend that you use the conditional If statements so that you an exception isn't thrown if 0 controls are found.
Ok, I find the problem... This command wasn't working because it was inside a GroupBox.
Then the right code is
Me.Controls("GroupBox1").Controls("C_" & 0).Text = "123"
Thanks for everyone help!
Your problem is control.Controls only returns the controls directly inside the control. So you can use these extension methods. Put this in a Module:
<Extension>
Public Function ChildControls(parent As Control) As IEnumerable(Of Control)
Return ChildControls(Of Control)(parent)
End Function
<Extension>
Public Function ChildControls(Of TControl As Control)(parent As Control) As IEnumerable(Of TControl)
Dim result As New List(Of TControl)
For Each ctrl As Control In parent.Controls
If TypeOf ctrl Is TControl Then result.Add(CType(ctrl, TControl))
result.AddRange(ctrl.ChildControls(Of TControl)())
Next
Return result
End Function
Here is how you would use it:
' general option to return all controls, filter on name
Me.ChildControls().Single(Function(c) c.Name = "C_" & 0)).Text = "Conta:"
' generic option to return only Labels, filter on name
Me.ChildControls(Of Label)().Single(Function(c) c.Name = "C_" & 0)).Text = "Conta:"
This will work whether the Label is in the GroupBox or not, and if you move it to a different GroupBox, Panel, or back to the Form, without needing to change your code.

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).

WP use string as name of control

Please, can anyone help me with this problem:
I have a name(s) of control(s) in string format (str) and I want to set property (in code) of that controls using that string-name.
I try something like this but it doesn't work. Actually, I have a problem with expression. When I put exactly the name it works but when i use variable in string format it doesn't.
Dim str as String
str="k3"
Dim g As Image = CType(str, Image)
g.Source = New BitmapImage(New Uri("/APP;component/Icons/hero.png", UriKind.Relative))
This works:
Dim g As Image = CType(k3, Image)
While this does not:
Dim g As Image = CType(str, Image)
I think I understand what you are trying to do, to declare an object by a string...
Essentially for this to work you will need a custom function that returns the Object Type that you are seeking...
You will need to loop through each control and check the name of the control as a comparison, e.g. If oControl.Name.ToString = sString then Return oControl
Example
' A function to return a Control by the Control's name...
Public Function GetControlByName(ByVal oForm As Form, ByVal sName As String) As Control
Dim cReturn As New Control
Dim ctrl As Control
For Each ctrl In oForm.Controls
cReturn = ctrl
If ctrl.Name.ToString = sName Then
Return ctrl ' this is what we want!
End If
Next
Return cReturn
End Function
' Example Usage
Dim oButton As Button = GetControlByName(Me, "Button44")
If oButton.Name.ToString = "Button44" Then
MessageBox.Show("I have found your Button!")
Else
MessageBox.Show("Your button was NOT Found!")
End If
Obviously there is room for error with this function, because if sName is NOT found, then it will return the last ctrl found, therefore, you will need to ensure that the control you seek is indeed found, via the If statement as provided in the example above...
Furthermore, it may not loop through controls inside of containers, menus, etc, but I'm not sure on that, so you will need to check to ensure it's not having that problem...
(The Me in the statement will most likely be used more often than not, though Me could be the name of the form you are searching if you are running the code outside of the form you are searching the form with the function.)
FINALLY, to answer your question, you will need to change Control to Image, and Set CReturn as a New Image, and then use Return ctrl.BackgroundImage (etc) to return the image..