Headshot
Headshot

Reputation: 452

How to get content of sub-controls in WinForm?

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

Answers (3)

Jimi
Jimi

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

IV.
IV.

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);
}

list all controls

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);
}

data grid view controls

Upvotes: 1

Andrea Colleoni
Andrea Colleoni

Reputation: 6021

If you want to get the dgv control, it must be:

  • accessible in the point you need it, in your code
  • named as you can find it

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

Related Questions