Reputation: 452
I have WinForm, which have many controls (tabcontrol, tabpage, datagridview,...etc). I want to get data in datagridview. The structure of design is like this (TabControl will have many TabPages, each TabPage will have one TabControl, each TabControl will have many TabPages, each TabPage will have one DataGridView).
TabControlX
+TabPage
++TabControl
+++TabPage
++++DataGridView
+++TabPage
++++DataGridView
+TabPage
++TabControl
+++TabPage
++++DataGridView
+++TabPage
++++DataGridView
So, I want to get data each DataGridView. I'm not sure if the stuff is correct or bad.
foreach (TabPage tbp in tabControl_X.TabPages)
{
foreach (Control item in tbp.Controls)
{
foreach (TabPage sub_tab in item.Controls)
{
foreach (Control dgv in sub_tab.Controls)
{
//how to get data dgv?
}
}
}
}
Upvotes: 0
Views: 85
Reputation: 32298
Alternative method, using reflection.
This method takes a generic T, constrained to the Control
class.
Set T
to the Type of Control you want to find and pass a Control that is the common ancestor (could be your tabControl_X
).
You could turn it into an extension method.
For example:
var childControls = GetAllChildControlsOfType<DataGridView>(tabControl_X);
If you pass the Form instance (this
), you get all child Controls of Type DataGridView
that have the Form as ancestor, and so on.
Built for .NET 6+, nullable enabled.
If you're targeting .NET Framework, remove all ?
and !
using System.Reflection;
private ReadOnlyCollection<T> GetAllChildControlsOfType<T>(Control parent) where T : Control {
var flags = BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField;
var ctlList = new List<T>();
var form = parent is Form ? parent : parent.FindForm();
var childControls = form!.GetType().GetFields(flags).Select(fi => fi.GetValue(form))
.Where(obj => obj?.GetType() == typeof(T))
.Select(obj => obj as T);
foreach (T? ctl in childControls) {
if (HasAncestor(ctl, parent)) {
ctlList.Add(ctl!);
}
}
bool HasAncestor(Control? ctl, Control? parent) {
if (ctl is null || parent is null) return false;
while (ctl.Parent != null) {
if (ctl.Parent == parent) return true;
ctl = ctl.Parent;
}
return false;
}
return new ReadOnlyCollection<T>(ctlList);
}
Upvotes: 1
Reputation: 9511
To iterate the control hierarchy of the Form
, make an iterator method similar to this:
public partial class MainForm : Form
{
IEnumerable<Control> IterateControls(Control.ControlCollection controls)
{
foreach (Control control in controls)
{
yield return control;
foreach (Control child in IterateControls(control.Controls))
{
yield return child;
}
}
}
}
First, demonstrate the iterator by listing ALL the controls:
// Iterate all names
Debug.WriteLine("ALL CONTROLS");
foreach (var control in IterateControls(Controls))
{
Debug.WriteLine(
string.IsNullOrWhiteSpace(control.Name) ?
$"Unnamed {control.GetType().Name}" :
control.Name);
}
An additional extension indents the output by depth see Repo.
Your question is how about getting just the DataGridView
controls, so specify OfType()
// Iterate DataGridView only
Debug.WriteLine("\nDATA GRID VIEW CONTROLS");
foreach (var control in IterateControls(Controls).OfType<DataGridView>())
{
Debug.WriteLine(control.Name);
}
Upvotes: 1
Reputation: 6021
If you want to get the dgv control, it must be:
So, if you added dgv's through designer of the TabControlX control, then you should be able to access them through thier name.
If you want to access them from outside the TabControlX control, then the dgv's memmbers must be marked as internal
or public
.
In addition to the Properties tab in the designer, try to look at the .Designer.cs
code file; there you should be able to find your control's variables.
Upvotes: 1