M
M
Michael2019-03-22 09:59:06
C++ / C#
Michael, 2019-03-22 09:59:06

How to get DataGridView values ​​from all forms?

Hello. Help solve the problem, and it's next.
There is a parent form with a button, in the parent form there is a TreeView element in which the user adds an element (the tree is simple, there is no nesting, only the root and elements below), when you click on the added TreeView element, an MDI form opens with a DataGridView in which the user specifies the values, after that when you click on the button in the parent form, you need to go through the tree and collect the values ​​​​that the user entered from all forms for further calculations. Walking through the tree is not difficult, you can also walk through the forms, but I can get the name of the form, I don’t understand how I can collect the data .. Maybe there are ideas on how to do it more competently.

Answer the question

In order to leave comments, you need to log in

1 answer(s)
F
Foggy Finder, 2019-03-24
@mscrack

Suppose we need to process a set of some data represented by a table. Each table has a title, the user can add an arbitrary number of tables and edit the data in them. Let's take a simple sum of numbers as an example of processing.
We start by describing the "model" - everything that relates to data and its processing. There are only two classes - SomeClass , which represents the data from the table:

public class SomeClass
{
    public int M { get { return Data.GetLength(1); } }
    public int N { get { return Data.GetLength(0); } }

    public double[,] Data { get; }

    public SomeClass(double[,] data)
    {
        Data = new double[data.GetLength(0), data.GetLength(1)];
        Array.Copy(data, Data, data.Length);
    }

    public double Sum()
    {
        var s = 0.0;
        foreach (var x in Data)
            s += x;
        return s;
    }
}

and the Item class , which is responsible for the table. Everything is simple here - the table is the data itself and the header.
public class Item
{
    public string Title { get; }
    public SomeClass Data { get; }

    public Item(string title, SomeClass sc)
    {
        Title = title;
        if (sc != null)
            Data = new SomeClass(sc.Data);
    }

    public override string ToString() => Title;
}

Now the view for the cell (single table). In such cases, it is convenient to create a UserControl, let's call it ItemView.
Let's define two methods in it - Fill and Extract .
Fill - is responsible for displaying the specified element.
Extract - will collect information from the controls and return an instance of the Item class.
The table can be described in the form of the following controls - two TextBox x to specify the dimension (number of rows, columns) and DataGridView to fill in the values.
public void Fill(Item item)
{
    label1.Text = item.Title;
    if (item.Data != null)
    {
        var n = item.Data.N;
        var m = item.Data.M;
        textBox1.Text = n.ToString();
        textBox2.Text = m.ToString();

        dataGridView1.ColumnCount = m;
        dataGridView1.RowCount = n;

        for (int i = 0; i < n; i++)
            for (int j = 0; j < m; j++)
                dataGridView1[j, i].Value = item.Data.Data[i, j];
    }
    else
    {
        textBox1.Text = "";
        textBox2.Text = "";

        dataGridView1.ColumnCount = 0;
        dataGridView1.RowCount = 0;
    }
}

and the implementation of the Extract method:
public Item Extract()
{
    string title = label1.Text;
    if (string.IsNullOrWhiteSpace(title))
        return null;

    var n = dataGridView1.RowCount;
    var m = dataGridView1.ColumnCount;

    if (n == 0 || m == 0)
        return new Item(title, null);

    var data = new double[n, m];

    for (int i = 0; i < n; i++)
        for (int j = 0; j < m; j++)
            data[i, j] = Convert.ToDouble(dataGridView1[j, i].Value);

    return new Item(title, new SomeClass(data));
}

I omit the code for setting the dimension of the table and generating arbitrary values ​​so as not to unnecessarily inflate the answer.
Now almost everything is there, you can proceed to the creation of the main form.
Here you will need a ListBox for navigating between tables, a panel that will host the UserControl, a button for calculations, and a Label for displaying results.
To bind the model to the view, we use the BindingList classes:
ItemView iview;
BindingSource bs = new BindingSource()
{ DataSource = typeof(Item) };

public Form1()
{
    InitializeComponent();
    listBox1.DataSource = bs;
    bs.PositionChanged += PositionChanged;
}

PositionChanged - switch handler between tables. Here we need to store the information in the table at the old index and display the data for the new one.
private void SaveCurrent()
{
    var pr = iview.Extract();
    if (pr != null)
    {
        var old = bs.List.OfType<Item>().First(v => v.Title == pr.Title);
        var ind = bs.List.IndexOf(old);
        bs[ind] = pr;
    }
}

private void PositionChanged(object sender, EventArgs e)
{
    if (bs.Current is Item item && bs.Count > 1)
    {
        SaveCurrent();
        iview.Fill(item);
    }
}

Here, in the SaveCurrent() method, we sequentially perform the following steps:
1. Extract data
2. Search for a table by the specified header
3. Search for an index in the list
4. Update the table
and code for calculations:
SaveCurrent();
var items = bs.OfType<Item>().Select(item => item.Data?.Sum() ?? -1);
resLbl.Text = string.Join(";", items);

Here, the SaveCurrent() method is also called to force saving during the calculation.
In total, the result is something like this:
The code may not be optimal, since I mainly use F# and not C# and also WPF and not WinForms, but the main thing here is the essence of the approach ;-)

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question