Question How to reference ContextMenuStrip from Controls collection

siddhartha

Member
Joined
Jan 6, 2010
Messages
6
Programming Experience
10+
I am writing a simple control translation function that reads an XML file containing control names and their localized text. Within the application, each time a form is loaded, it iterates through every control and tests to see if that control is defined in the XML file. If it is, it then sets the control text to whatever localized text is defined in the XML file.

There are some tests and additional requirements (i.e. nested loops) for TabControl and MenuStrip etc., but the control that is giving me the most trouble is the ContextMenuStrip.

frmForm.Contains(cmuContextualMenu) returns False. It seems that contextual menus are not part of the Form.Controls collection.

From the form.Designer.vb, I can see that it creates a contextual menu as follows:

VB.NET:
Private components As System.ComponentModel.IContainer
Me.components = New System.ComponentModel.Container

Me.cmuContextualMenu = New System.Windows.Forms.ContextMenuStrip(Me.components)

So, I could reference the contextual menu by first changing "Private components..." to "Public components..." then:

VB.NET:
For Each ctl_control As Control In frmForm.components.Components
  MessageBox.Show(ctl_control.Name)
Next ctl_control

But two problems arise:

1) It would be nice to be able to do something like:

VB.NET:
MessageBox.Show(CType(frmForm.components.Components("cmuContextualMenu"), Control).Name)

2) I would like to have the frmForm.components a standard Form property so I could access the property from any generic Form object and not a specific, named form. If this can't be done, it would be nice to be able to test the existence of the .components property on any Form object.

The best possible solution is to change:

VB.NET:
Me.cmuContextualMenu = New System.Windows.Forms.ContextMenuStrip(Me.components)

To:

VB.NET:
Me.cmuContextualMenu = New System.Windows.Forms.ContextMenuStrip

So that the ContextualMenu is now part of the controls collection of the Form object and not in some mysterious private components IContainer. Alas, this does not work.

Any help is appreciated.
 
The form objects are divided in controls and components. Control class inherits Component, but not all components are controls, they may only be inheriting Component or implementing IComponent interface. Class vs. Component vs. Control

There is no way to override how this works, and you can't just cast any component to type Control, you'd have to check the type first (f.ex If TypeOf comp Is Control Then...) or TryCast. Also notice that components.Components(name) is not the name of the control (if the component is a control), but rather the name associated with the ISite, Component/IComponent has no Name property.
 
Yes, I do test for TypeOf Is, typecasting cmuContextualMenu to a Control was a slight error in my pseudo code. Sorry about that.

Regardless, is the ContextualMenu really only a Component and not a Control? That seems like an over site on MS's part...
 
It seems as though ContextMenuStrip is not part of Form.Controls. When I add it to the Controls collection using Form.Controls.Add(), an ArgumentException is thrown "Top-level control cannot be added to a control."

When I change the ContextMenuStrip's TopLevel property to False, I can add it to the Controls collection, but then I get weird behaviour from the contextual menu in that the Z-Order messes up and its popup location is wrong.
 
Well, I figured out a kludgey workaround.

When I loop through all the controls on the form, I simply test to see if that control has a ContextualMenu.

VB.NET:
For Each ctr_control As Control In frmForm.Controls
  set_control_translation(ctr_control)

  If Not ctr_control.ContextMenuStrip Is Nothing Then
    set_control_translation(ctr_control.ContextMenuStrip)
  End If
Next ctr_control

Note that in set_control_translation() I also test if ctr_control.HasChildren to recall the function for things like TabControls. If HasChildren returns True then a ctr_child_control.ContextMenuStrip Is Nothing test must be run as well.
 
It seems as though ContextMenuStrip is not part of Form.Controls. When I add it to the Controls collection using Form.Controls.Add(), an ArgumentException is thrown "Top-level control cannot be added to a control."

When I change the ContextMenuStrip's TopLevel property to False, I can add it to the Controls collection, but then I get weird behaviour from the contextual menu in that the Z-Order messes up and its popup location is wrong.
I simply stated that CMS does inherit Control class, not that you should attempt to add it to the forms Controls collection, the CMS belongs where it's at in the Components container.

You can also loop the components if you need to:
VB.NET:
For Each comp As IComponent In Me.components.Components
Then check what type the component object is and do your thing accordingly.
 
Thanks for the suggestions. Unfortunately, the Components loop requires setting .components to Public instead of Private. I figure the .ContextMenuStrip test two posts up is the better approach since it doesn't require changes to Designer files.
 
Back
Top