Unable to get the Event associated with a menu element to create another menu element - vb.net

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!

Related

The class list is giving an error when used with the 'tag' property in main form

So I have created a list called 'menu' in which I have stored all the items' names and prices using a class called 'MenuItem', inside the main form. Then I created a second list called 'items' to store these items in every time the user creates an order with all the items they chose (and then I can use that list to calculate the total etc).
Private Sub ButtonClickHandler(sender As Object, e As EventArgs) Handles ckburger.checkedchanged, ckwrap.CheckedChanged, ckparmesan.checkedchanged
Dim menu As New List(Of MenuItem) From
{
New MenuItem With {.Name = "Burger", .Cost = 2.0 * txtburger.Text},
New MenuItem With {.Name = "wrap", .Cost = 2.0 * txtwrap.Text},
New MenuItem With {.Name = "Parmesan", .Cost = 2.5 * txtparmesan.Text},
I then instantiated an object from the 'order' class, where the item from the menu is passed as parameter and stored in a list in one of the methods, and another method is used to calculate the total (which also has the textbox value passed in as parameter so the total can show up on the form). I am using the 'tag' property to read the name of each item and comparing it to the 'menu' list earlier on, to find the item.
So up in the sub I will handle multiple check boxes (for each item) so every time each item gets checked it can be added to the list.
Dim button As Button = CType(sender, Button)
Dim menuItem As MenuItem
menuItem = menu.Find(Function(item As MenuItem) item.Name = button.Tag)
Dim order1 As New order
order1.AddItem(menuItem)
order1.OrderTotal(txttotal.Text)
But when I run the program and check one of the item checkboxes on the form, I get an odd error on the 'menu' list stating 'Conversion from string "" to type 'Double' is not valid.' I then tried declaring the list outside the sub, just in the form, but when I run that, I get an error saying 'Object reference not set to an instance of an object.'
(When I tried running the program without the use of the 'tag' property and only using ONE event handler button, I never got this error but instead after the item was stored in the list, when I called the 'orderTotal' method, the total value wouldn't show up in the textbox on the form)
Public Sub AddItem(ByVal item As MenuItem)
Items.Add(item)
End Sub
Public Sub OrderTotal(total)
Dim fullamount As Decimal = 0
For Each item As MenuItem In Item
fullamount = fullamount + item.Cost
total = fullamount
Next
I'm not sure what I'm doing wrong and how to sort this, If anyone can help?

How do I save textboxes with tabs depending which tab is open?

I am trying to create a document writer in vb.net
So I decided to add a tab control, so I can create a new tab when I press a button. I would like to save the text in the tab that is open. I created a function the returns the text depending on what tab is open, but that only works for the two default tabs. I don't know how I would save if I've created a new tab.
The function to get the text is this:
Public Function getText() As String
If tabPage1.Visible = True Then
Return mainText.Text
ElseIf tabPage2.Visible = True Then
Return textBox1.Text
End If
End Function
I've done some research online, I've even looked at SharpDevelop's source code and I couldn't find anything.
Thanks in advance!
EDIT :
Public Sub setText(Byval value As String)
If tabPage1.Visible = True Then
mainText.Text = value
ElseIf tabPage2.Visible = True Then
textBox1.Text = value
End If
End Sub
Does anyone know how I would do an open feature determined on what tab is open (as before.)
If I understand you correctly, you are trying to have a textbox in each of your tabPages generated dynamically. If this is the case you could generalize your GetText function with this code
Function GetText() As String
If tabControl1.SelectedTab IsNot Nothing Then
Return tabControl1.SelectedTab.Controls.OfType(Of TextBox)().First().Text
End If
End Function
This requires that you have at least one textbox in each page (and your TabControl is named tabControl1). The SelectedTab property (if not nothing) is the current tabPage displayed by your tabControl

What is wrong with this LINQ code?

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.

should changing the parent of a listbox change the selected index?

You wouldn’t think so, but it does when the listbox is bound to a datasource (as far as I can see).
I’ve reduced the behaviour to the code below. The "if" line toggles between loading a list via data binding and loading a list “manually” (both use the same data table). In each case I set the selected index afterwards, and then change the parent form. With manual loading, the selected index is retained, with binding it is lost. I cannot see how this makes any sense – I don't see why changing the host form should alter any property of the list. Is this a bug?
Public Class Form1
Sub main() Handles Me.Load
Dim ListControl1 As ListBox = New ListBox
ListControl1.Parent = Me
Dim dt = New DataTable
dt.Columns.Add("intColourID")
dt.Columns.Add("strName")
dt.Rows.Add({1, "Red"})
dt.Rows.Add({2, "Green"})
dt.Rows.Add({3, "Blue"})
ListControl1.ValueMember = dt.Columns(0).ColumnName
ListControl1.DisplayMember = dt.Columns(1).ColumnName
If False Then
ListControl1.DataSource = dt
Else
For i = 0 To dt.Rows.Count - 1
ListControl1.Items.Add(dt.Rows(i)("strName").ToString)
Next
End If
ListControl1.SelectedIndex = 2
Dim z As Form = New Form
ListControl1.Parent = z
z.Show()
End Sub
End Class
The correct way of adding a control to a form is not to set its parent, but to add it to the Controls collection of the form. If I do it like this I do not get an exception (the three last lines commented out as you write in your comment).
Me.Controls.Add(ListControl1) ' Instead of ListControl1.Parent = Me

Need help adding an custom object to a custom collection

Lets say I have a custom collection and a custom object that have a Parent-Child relationship
I have a userform where the user names the collection and provides input for other properties of the collection. When they click "Add Parent" the click event is handled and calls the following function:
Public Function AddParent()
Dim newParent As clsParent
Set newParent = New clsParent
'Add Parent Properties'
With frmAddParent
newParent.Name = .cboParentName.Value
newParent.Width = .txtParentWidth.Value
newParent.Xslope = .txtParentCrossSlope.Value
End With
'show the form for creating the Child Object'
frmAddLaneMaterial.Show
End Function
The user then sees a new form for creating a Child object. When the user clicks "Add Child" the event is handled and calls the following function:
Public Function AddChild()
Dim newChild As clsChild
Set newChild = New clsChild
'Add child Properties'
With frmAddChild
newChild.Name = .cboParentName.Value
newChild.LayerNumber = .cboLayerNum.Value
newChild.xSlope = newParent.Xslope
End With
'Add the new child to the parent collection'
newParent.Add newChild
End Function
And then the user needs to be able to return to the userform and add another child.
The lines that won't work are:
newChild.xSlope = newParent.Xslope
and:
newParent.Add newChild
I get an 'object required' error.
How do I/where do I add the child to the Parent Collection?
First of all, newParent is local to the AddParent function so AddChild has no visibility of it. To fix that, you would need to move Dim newParent As clsParent out of the AddParent function and make it a module-level variable. This assumes that AddParent and AddChild are in the same module.
Secondly, newParent.Add newChild will only work if you have written an Add method in clsParent. If you have not then you'll need to write one. I have no idea how you plan on using it, but the following code is pretty generic and should get you pointed in the right direction. This code would go in the clsParent module:
Private m_oChildren As Collection
Sub Add(Child As clsChild)
If m_oChildren Is Nothing Then Set m_oChildren = New Collection
m_oChildren.Add Child
End Sub
This would build a private collection of clsChild objects that you would manipulate using additional methods or expose via Property Get.
UPDATE: To address your comment, if you want to keep multiple parents you'll need to add a collection for that. For example:
Dim Parents As Collection
Public Function AddParent()
Dim newParent As clsParent
Set newParent = New clsParent
'Add Parent Properties'
With frmAddParent
newParent.Name = .cboParentName.Value
newParent.Width = .txtParentWidth.Value
newParent.Xslope = .txtParentCrossSlope.Value
End With
'Initialize the collection of Parents if it has not already been done'
If Parents Is Nothing Then Set Parents = New Collection
'Add the new parent object to the Parents collection'
Parents.Add newParent
'show the form for creating the Child Object'
frmAddLaneMaterial.Show
End Function
Public Function AddChild()
Dim newChild As clsChild
Set newChild = New clsChild
'Add child Properties'
With frmAddChild
newChild.Name = .cboParentName.Value
newChild.LayerNumber = .cboLayerNum.Value
newChild.xSlope = newParent.Xslope
End With
'Add the new child to the most recently added parent in the parent collection'
Parents.Item(Parents.Count).Add newChild
End Function
Of course, now you have another collection to keep track of. It gets really tricky keeping track of multiple instances of forms (which I assume is what you are doing) and it is easy to suddenly find yourself with an unmanageable mess.