VB.NET•Build Menus at Run Time

Listing 1. Building menus at run time allows you to combine standard menu options with ones that derive directly from available assemblies. You can identify appropriate types by their base class or by attributes, and you can use other attributes to specify captions and the location of new options.

Protected Overridable Sub BuildMenuFromAssembly()
	Dim assemblies As Reflection.Assembly()
	Dim types As System.Type()
	Dim baseMenu As Windows.Forms.Menu
	Dim deleg As System.Delegate
	Dim mnu As Windows.Forms.MenuItem
	If mnuMain Is Nothing Then
		Throw New _
			System.ApplicationException( _
			"Internal error - mnuMain is nothing")
	Else
		assemblies = Me.GetAssemblies
		For Each assem As Reflection.Assembly In _
			assemblies
			If Array.IndexOf(ignoreAssemblies, _
				assem.GetName.Name) < 0 AndAlso _
				Not assem.GetName.Name.StartsWith( _
				"System") Then
				types = assem.GetTypes
				For Each type As System.Type In _
					types
					If IsValidChildForm(type) Then
						baseMenu = _
							Me.GetBaseMenu(type)
						mnu = New MenuItemWithType( _
							Me.GetCaption(type), _
							AddressOf _
							mnuDynamicItem_Click, _
							type)
							baseMenu.MenuItems.Add _
							(mnu)
					End If
				Next
			End If
		Next
	End If
End Sub

Protected Overridable Function GetAssemblies() _
	As Reflection.Assembly()
	Return Threading.Thread.CurrentThread. _
		GetDomain.GetAssemblies
End Function

Private Function IsValidChildForm(ByVal type As _
	System.Type) As Boolean
	Dim ret As Boolean
	If Utility.GetCustomTypeAttribute( _
		type, "DynamicSupport.ExcludeFromMenus" &_
		"Attribute") Is Nothing Then
		If type.IsSubclassOf(GetType( _
			DynamicSupport.BaseEditForm)) Then
			Return True
		ElseIf Not type.GetInterface( _
			"DynamicSupport.IProcess") Is Nothing _
			Then
			Return True
		ElseIf Not _
			Utility.GetCustomTypeAttribute( _
			type, "DynamicSupport." &_
			"IncludeInMenuAttribute") Is Nothing _
			Then
			Return True
		End If
	End If
End Function

Protected Function GetBaseMenu(ByVal type As _
	System.Type) As Windows.Forms.Menu
	Dim obj As Object
	Dim caption As String
	Dim rescaption As String
	obj = Utility.GetCustomTypeAttribute(type, _
		"DynamicSupport.BaseMenuAttribute")
	If TypeOf obj Is BaseMenuAttribute Then
		caption = CType(obj, _
			BaseMenuAttribute).BaseMenu
		rescaption = _
			GetResManager(type).GetString(caption)
		If (Not rescaption Is Nothing) AndAlso _
			(rescaption.Trim.Length > 0) Then
			caption = rescaption
		End If
		Return GetBaseMenu(caption)
	Else
		Return GetDefaultMenu()
	End If
End Function

Protected Overridable Function GetDefaultMenu() _
	As Windows.Forms.MenuItem
	Return GetBaseMenu("Action")
End Function

Private Function GetBaseMenu(ByVal caption As _
	String) As Windows.Forms.MenuItem
	For Each MenuItem As Windows.Forms.MenuItem _
		In Me.mnuMain.MenuItems
		If MenuItem.Text = caption Then
			Return MenuItem
		End If
	Next
	' If we get here, it doesn't exist yet
	Return Me.mnuMain.MenuItems.Add(caption)
End Function