Задать вопрос
mscrack
@mscrack

Как получить значения DataGridView со всех форм?

Здравствуйте. Помогите решить проблему, а она в следующем.
Есть родительская форма с кнопкой, в родительской форме есть элемент TreeView, в которое пользователь добавляет элемент (дерево простое нет вложенности, только корень и элементы ниже), при клике на добавленный элемент TreeView открывается MDI форма с DataGridView в которой пользователь указывает значения, после этого при нажатии на кнопку в родительской форме, нужно пройтись по дереву, и со всех форм собрать значения которые ввел пользователь для дальнейших расчетов. Пройтись по дереву не сложно, по формам тоже можно пройтись но я могу получить название формы, не пойму как мне данные собрать.. Может есть идеи как это сделать более грамотно.
  • Вопрос задан
  • 400 просмотров
Подписаться 2 Средний 17 комментариев
Решения вопроса 1
FoggyFinder
@FoggyFinder
Предположим, что нужно обрабатывать набор некоторых данных представляемых таблицей. У каждой таблицы есть заголовок, пользователь может добавлять произвольное число таблиц и редактировать данные в них. В качестве примера обработки возьмем простую сумму чисел.

Начинаем с того, что описываем "модель" - все то, что относится к данным и их обработке. Тут всего два класса - SomeClass, который представляет собой данные из таблицы:

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


и класс Item, который отвечает за таблицу. Тут все просто - таблица это сами данные и заголовок.

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


Теперь представление для ячейки (одной таблицы). В таких случаях удобно создавать UserControl, назовем его ItemView

Определим в нем два метода - Fill и Extract.

Fill - отвечает за то чтобы отобразить указанный элемент.
Extract - соберет информацию из элементов управления и вернет экземпляр класса Item.

Таблицу можно описать в виде следующих элементов управления - двух TextBox для указания размерности (количество строк, столбцов) и DataGridView для заполнения значений.

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


и реализация метода Extract:

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


Код для задания размерности таблицы и генерации произвольных значений опускаю, чтобы излишне не раздувать ответ.

Теперь практически все есть, можно переходить к созданию главной формы.
Здесь понадобится ListBox для навигации между таблицами, панель, в которой будет размещаться UserControl, кнопка для расчетов и Label для вывода результатов.

Чтобы связать модель с представлением воспользуемся классов BindingList:

ItemView iview;
BindingSource bs = new BindingSource()
{ DataSource = typeof(Item) };

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


PositionChanged - обработчик переключений между таблицами. Здесь нам нужно сохранять информацию в таблицу по старом индексу и отображать данные для новой.

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


Здесь в методе SaveCurrent() мы последовательно выполняем следующие шаги:

1. Извлекаем данные
2. Ищем таблицу по указанному заголовку
3. Ищем индекс в списке
4. Обновляем таблицу

и код для расчетов:

SaveCurrent();
var items = bs.OfType<Item>().Select(item => item.Data?.Sum() ?? -1);
resLbl.Text = string.Join(";", items);


Здесь тоже вызывается метод SaveCurrent() для принудительного сохранения при расчете.

Итого, результат примерно следующий:

5c975f2a21c91976114645.gif

Код может быть не оптимальным, так как я в основном использую F# а не C# а также WPF а не WinForms, но главное тут суть подхода ;-)
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы