Reputation: 993
I have a UserControl which has several ContextMenuStrips add via VS Designer. They are not assigned at design time to any controls, because they are dynamically assigned to one "dropdown" button which is context sensative.I have a Class that iterates through all the controls within a control to theme those controls (i.e. dark mode). The code:
Private ControlList As List(Of Control) = New List(Of Control)()
Private Sub GetAllControls(ByVal container As Control)
For Each c As Control In container.Controls
GetAllControls(c)
ControlList.Add(c)
Next
End Sub
ContextMenuStrips are not detected. From reading, I understand that these are not a Control, but a Component. I tried one suggested solution via:
Private Sub GetAllComponents(container As Object)
For Each co As System.ComponentModel.Component In container.components.Components
If TypeOf co Is ContextMenuStrip Then
ControlList.Add(co)
End If
Next
End Sub
However I get an error at run-time:
System.MissingMemberException: 'Public member 'components' on type 'ObjectSelector' not found.'
ObjectSelector is the UserControl.
The theming Class is used by lots of other objects (forms, usercontrols) so it is difficult to have a specific property for this one UserControl. How can I get a list of all the ContextMenuStrips on a UserControl?
Upvotes: 1
Views: 143
Reputation: 125312
Note: A totally different approach, so I'll post it as another answer. It may be more interesting for some users; however this one is also a hacky way.
The other solution relies on components
private member of the controls to get all the components recursively, however if you are just focusing on getting ToolStrip and all its descendants, here is another option.
Getting all ToolStrip, ContextMenuStrip, MenuStrip and StatusStrip in the app
The ToolStripManager has an internal property, ToolStrips
, which keeps a reference to all ToolStrip instances (including ToolStrip, MenuStrip, ContextMenuStrip, StatusStrip) that you have in your application. In fact when you create an instance of those components, they add themselves to the toolstrips collection of the toolstrip manager. You can get all tool strips, context menu strips, menu strips and status strips using reflection using that property.
Here is the code:
public static IEnumerable<ToolStrip> ToolStrips()
{
var toolStrips = (typeof(ToolStripManager).GetProperty("ToolStrips",
System.Reflection.BindingFlags.Static |
System.Reflection.BindingFlags.NonPublic)
.GetValue(null, null) as System.Collections.IList);
for (int i = 0; i < toolStrips.Count; i++)
yield return toolStrips[i] as ToolStrip;
}
And to limit it to the context menu strips:
var ctxMenuStrips = ToolStrips().OfTypeOf<ContextMenuStrip>();
Upvotes: 0
Reputation: 125312
When you use designer and drop a component on the form, the designer creates a components
private member field form the form. To get that member you need to rely on reflection.
Before I post the code I'd like to highlight the following points:
ContextMenuStrip
, ToolStrip
, MenuStrip
and StatusStrip
is assigning a themed renderer to ToolStripManager.Renderer.Get All descendant components of a control (recursively)
Anyways, I'll post an answer for learning purpose to show you how you can get a list of all components on a control and it's children. To so, I'll create an extension method which you can use like this:
var ctxMenuStrips = this.FindForm().AllComponents().OfTypeOf<ContextMenuStrip>();
Here's the code of the extension method:
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
public static class ControlExtensions
{
public static IEnumerable<Component> AllComponents(this Control control)
{
var componentsFields = control.GetType().GetField("components",
System.Reflection.BindingFlags.NonPublic |
System.Reflection.BindingFlags.Instance);
var components = componentsFields?.GetValue(control) as IContainer;
if (components != null)
foreach (Component component in components.Components)
yield return component;
foreach (Control child in control.Controls)
foreach (Component c in child.AllComponents())
yield return c;
}
public static IEnumerable<Control> AllControls(this Control control)
{
foreach (Control c in control.Controls)
{
yield return c;
foreach (Control child in c.AllControls())
yield return child;
}
}
}
Upvotes: 1