Связь двух DataGridView в C# пример кода

Как связать 2 datagridview c

Как связать 2 datagridview c

Синхронизация данных между двумя DataGridView в Windows Forms – задача, требующая точного контроля над источниками данных и событиями. Чаще всего связь реализуется через общий BindingSource или привязку к одной коллекции с фильтрацией. Например, если первый DataGridView отображает список заказов, а второй – связанные с выбранным заказом товары, ключевым моментом становится обработка события SelectionChanged первого контрола.

Для корректной работы необходимо учитывать особенности привязки данных. Используйте DataTable или List<T> с реализацией INotifyPropertyChanged для автоматического обновления интерфейса. Пример: если данные загружаются из SQL-запроса, создайте две таблицы с отношением «один ко многим» и привяжите их к BindingSource с указанием DataMember для дочерней таблицы.

Обработка ошибок – обязательный этап. Проверяйте CurrentRow на null перед доступом к выбранной строке, чтобы избежать исключений. Для динамического обновления второго DataGridView используйте метод ResetBindings(false) после изменения фильтра. Пример кода с фильтрацией по ID:

dataGridView2.DataSource = ordersTable.Select($»OrderID = {selectedOrderId}»);

Избегайте прямой манипуляции с Rows – работайте через источник данных. Это гарантирует согласованность состояния и упрощает отладку. Для сложных сценариев рассмотрите использование DataView с динамическим изменением RowFilter.

Подготовка данных для синхронизации таблиц

Синхронизация двух DataGridView требует приведения данных к единой структуре. Начните с определения ключевых полей, по которым будет выполняться связь. Например, если таблицы содержат заказы и клиентов, используйте поле CustomerID как первичный ключ. Убедитесь, что типы данных совпадают: int в одной таблице не должен соответствовать string в другой. Для числовых полей проверьте диапазоны значений, чтобы избежать ошибок при фильтрации.

Создайте промежуточный класс или структуру для хранения связанных данных. Ниже пример структуры для синхронизации таблиц «Заказы» и «Клиенты»:

Поле Тип данных Назначение
OrderID int Идентификатор заказа
CustomerID int Ключ связи с таблицей клиентов
OrderDate DateTime Дата создания заказа
CustomerName string Дубликат имени клиента (для отображения)

При загрузке данных в DataGridView используйте LINQ для объединения таблиц: var result = orders.Join(customers, o => o.CustomerID, c => c.ID, (o, c) => new { o.OrderID, c.Name }). Это сократит время обработки и исключит дублирование кода.

Реализация односторонней связи между DataGridView

Реализация односторонней связи между DataGridView

Односторонняя связь между двумя DataGridView в WinForms реализуется через привязку данных с использованием события SelectionChanged. Основной DataGridView (источник) содержит полный набор данных, а зависимый – фильтрованные записи, соответствующие выбранной строке в источнике. Для этого создайте два экземпляра DataGridView, привяжите их к разным BindingSource, а затем настройте фильтрацию зависимого источника при изменении выбора в основном. Пример кода:

private void dataGridViewSource_SelectionChanged(object sender, EventArgs e) {
if (dataGridViewSource.CurrentRow != null) {
var selectedId = dataGridViewSource.CurrentRow.Cells["Id"].Value;
bindingSourceDependent.Filter = $"ParentId = {selectedId}";
}
}

Ключевой момент – правильная структура данных. Источник должен содержать уникальный идентификатор (Id), а зависимый – поле для связи (ParentId). Если данные загружаются из базы, используйте SQL-запрос с JOIN или отдельные таблицы с внешним ключом. Для динамического обновления зависимого DataGridView без перезагрузки данных применяйте метод ResetBindings(false) после изменения фильтра.

Оптимизируйте производительность, отключая автоматическое обновление зависимого DataGridView перед массовыми операциями: dataGridViewDependent.SuspendLayout();. Восстанавливайте его после завершения фильтрации. Для сложных сценариев используйте DataView вместо BindingSource.Filter, чтобы избежать ограничений синтаксиса фильтрации.

Настройка двусторонней синхронизации с фильтрацией строк

Настройка двусторонней синхронизации с фильтрацией строк

Двусторонняя синхронизация между двумя DataGridView требует привязки к общему источнику данных с поддержкой фильтрации. Используйте BindingSource для каждого контрола, связав их с одной коллекцией, например, List<T> или DataTable. При изменении фильтра в одном DataGridView обновляйте Filter свойство BindingSource второго, чтобы синхронизировать отображаемые строки без дублирования логики.

Для фильтрации строк применяйте выражения в формате SQL-подобных условий: bindingSource.Filter = "Status = 'Active' AND Price > 100". Избегайте прямой модификации DataSource – это нарушит синхронизацию. Вместо этого обновляйте фильтр через BindingSource, который автоматически пересчитает отображаемые данные в обоих контролах.

Обработка событий CellValueChanged и RowValidated критична для поддержания актуальности. При изменении значения в одной таблице вызывайте метод bindingSource.ResetBindings(false) для принудительного обновления связанных данных. Это гарантирует, что фильтры применятся корректно, даже если изменение затронуло поле, участвующее в условии фильтрации.

Для сложных сценариев фильтрации используйте DataView как промежуточный слой. Создайте два экземпляра DataView с разными фильтрами, но привязанных к одной DataTable. Привяжите каждый DataGridView к своему DataView, а изменения в данных обрабатывайте через DataTable.RowChanged, чтобы синхронизировать оба представления.

При динамическом изменении фильтров учитывайте производительность. Если данные содержат более 10 000 строк, избегайте частых пересчетов фильтров. Вместо этого кешируйте результаты фильтрации или используйте BindingList<T> с реализацией INotifyPropertyChanged для более эффективного обновления.

Для синхронизации выделения строк между таблицами подпишитесь на событие SelectionChanged. В обработчике получайте текущую выделенную строку через dataGridView.CurrentRow и программно выделяйте соответствующую строку во втором контроле с помощью dataGridView2.Rows[index].Selected = true. Убедитесь, что индексы строк совпадают в обоих источниках данных.

Тестируйте синхронизацию на данных с дубликатами и пустыми значениями. Фильтры типа "Name IS NOT NULL" или "ID IN (1, 2, 3)" помогут избежать ошибок при обработке неполных данных. Для отладки используйте bindingSource.SupportsFiltering и проверяйте корректность выражений фильтрации до их применения.

Обработка событий выбора строки в первом DataGridView

Обработка событий выбора строки в первом DataGridView

Для синхронизации данных между двумя DataGridView используйте событие SelectionChanged первого элемента. Подпишитесь на него в конструкторе формы или методе инициализации:

  • Добавьте обработчик: dataGridView1.SelectionChanged += DataGridView1_SelectionChanged;
  • В методе-обработчике проверяйте dataGridView1.SelectedRows.Count > 0, чтобы избежать исключений при пустом выделении.
  • Получайте ключевое значение из выбранной строки (например, ID) через dataGridView1.SelectedRows[0].Cells["IdColumn"].Value.

Для фильтрации данных во втором DataGridView используйте полученный идентификатор. Если данные загружены в DataTable, примените фильтр: dataTable2.DefaultView.RowFilter = $"ParentId = {selectedId}";. При работе с BindingSource установите фильтр через его свойство Filter. Учитывайте, что частые обновления фильтра могут снижать производительность – оптимизируйте код, отключая временно привязку данных или используя BeginUpdate()/EndUpdate().

Обновление второго DataGridView при изменении источника данных

Синхронизация двух DataGridView в Windows Forms требует явного обновления зависимого контрола при изменении источника данных основного. Используйте событие DataSourceChanged или CellValueChanged для отслеживания изменений. Пример базовой реализации:

  • Подпишитесь на событие DataSourceChanged первого DataGridView.
  • В обработчике события вызовите метод Refresh() или перепривяжите DataSource второго контрола.
  • Для фильтрации данных используйте BindingSource с параметром Filter.

Если второй DataGridView зависит от выбранной строки первого, применяйте событие SelectionChanged. Пример кода для динамического обновления:

private void dataGridView1_SelectionChanged(object sender, EventArgs e) {
if (dataGridView1.CurrentRow != null) {
var selectedId = dataGridView1.CurrentRow.Cells["Id"].Value;
var filteredData = originalData.Where(x => x.ParentId == selectedId).ToList();
dataGridView2.DataSource = filteredData;
}
}

Для оптимизации производительности избегайте полной перезагрузки данных. Вместо этого обновляйте только изменённые строки через BindingList<T> или DataView. Пример с BindingList:

var bindingList = new BindingList<YourModel>(originalData);
dataGridView1.DataSource = bindingList;
bindingList.ListChanged += (s, e) => {
if (e.ListChangedType == ListChangedType.ItemChanged) {
dataGridView2.Refresh();
}
};

При работе с DataTable используйте метод GetChanges() для получения только изменённых строк. Это сокращает нагрузку на интерфейс:

var changes = dataTable1.GetChanges();
if (changes != null) {
dataGridView2.DataSource = changes;
dataTable1.AcceptChanges();
}

Для сложных сценариев с несколькими уровнями зависимости применяйте паттерн «Наблюдатель». Создайте класс-посредник, который будет уведомлять все зависимые контролы об изменениях. Пример структуры:

  • Класс DataNotifier с событием OnDataChanged.
  • Метод Notify() для вызова события при изменении данных.
  • Подписка всех DataGridView на событие через лямбда-выражения.

При использовании Entity Framework Core обновляйте второй DataGridView через ObservableCollection. Подпишитесь на событие CollectionChanged и вызывайте Refresh() при добавлении, удалении или изменении элементов. Пример:

var observableCollection = new ObservableCollection<YourEntity>();
dataGridView1.DataSource = observableCollection;
observableCollection.CollectionChanged += (s, e) => {
dataGridView2.Refresh();
};

Для асинхронных операций используйте async/await с блокировкой интерфейса. Пример обновления после загрузки данных:

private async void LoadDataAsync() {
var data = await Task.Run(() => GetDataFromDb());
dataGridView1.DataSource = data;
dataGridView2.DataSource = data.Where(x => x.IsActive);
}

Использование BindingSource для управления связью

BindingSource – промежуточный слой между источником данных и элементами управления, например DataGridView. Он автоматически синхронизирует изменения в данных с отображением, избавляя от необходимости вручную обновлять интерфейс. Для связки двух DataGridView через BindingSource достаточно привязать оба элемента к одному источнику, но с разными фильтрами или представлениями. Например, если основной DataGridView отображает список заказов, а второй – связанные с выбранным заказом товары, BindingSource позволяет динамически обновлять второй список при изменении строки в первом.

Создайте два экземпляра BindingSource: один для основного набора данных, другой – для зависимого. Привяжите первый DataGridView к основному BindingSource, а второй – к зависимому. В обработчике события SelectionChanged основного DataGridView обновите DataSource зависимого BindingSource, используя LINQ-запрос или метод Filter. Например: dependentBindingSource.DataSource = mainBindingSource.List.Cast<Order>().First(o => o.Id == selectedOrderId).Items;. Это гарантирует мгновенное обновление второго DataGridView без перезагрузки данных.

BindingSource поддерживает сортировку и фильтрацию без обращения к базе данных. Для фильтрации используйте свойство Filter: dependentBindingSource.Filter = "OrderId = " + selectedId;. Сортировка задается через Sort: dependentBindingSource.Sort = "Price DESC";. Эти операции выполняются на клиентской стороне, что снижает нагрузку на сервер и ускоряет работу интерфейса. Учтите, что Filter работает только с DataTable или DataView; для коллекций используйте LINQ.

При работе с Entity Framework или другими ORM BindingSource упрощает привязку к результатам запросов. Создайте контекст данных, загрузите сущности в BindingSource, и оба DataGridView будут автоматически реагировать на изменения. Например: mainBindingSource.DataSource = dbContext.Orders.ToList();. Для зависимого списка используйте навигационные свойства: dependentBindingSource.DataSource = selectedOrder.OrderItems;. Это исключает необходимость в ручной синхронизации данных между слоями.

BindingSource отслеживает изменения в данных и поддерживает отмену редактирования через метод CancelEdit. Если пользователь отменил ввод в ячейке DataGridView, вызовите bindingSource.CancelEdit(), чтобы вернуть исходное значение. Для сохранения изменений в базе данных используйте метод EndEdit перед вызовом SaveChanges у контекста Entity Framework. Это предотвращает потерю несохраненных данных при переключении между строками.

Обработка событий BindingSource позволяет расширить функциональность. Событие CurrentChanged срабатывает при смене текущей записи, что полезно для обновления зависимых элементов управления. Событие ListChanged сигнализирует об изменении состава данных (добавление, удаление, перемещение). Подпишитесь на него, чтобы динамически обновлять связанные элементы: dependentBindingSource.ListChanged += (s, e) => UpdateSummary();. Это избавляет от необходимости вручную отслеживать изменения в источниках данных.

Для сложных сценариев используйте BindingSource в сочетании с BindingList<T>. BindingList поддерживает уведомления об изменениях и позволяет динамически добавлять или удалять элементы без перепривязки. Пример: var orders = new BindingList<Order>(); mainBindingSource.DataSource = orders;. Теперь добавление элемента в orders автоматически отобразится в DataGridView. Это особенно эффективно при работе с данными, загружаемыми асинхронно или через API.

Обработка ошибок при несовпадении структуры данных

Обработка ошибок при несовпадении структуры данных

При синхронизации двух DataGridView через общий источник данных (DataTable, BindingSource) критически важно проверять соответствие структуры столбцов. Например, если первый DataGridView содержит столбец Id типа int, а второй ожидает string, привязка завершится исключением InvalidCastException. Решение – использовать метод DataTable.Clone() для создания копии структуры с последующей валидацией типов через DataColumn.DataType перед заполнением. Для динамических данных добавляйте проверку в обработчик DataBindingComplete:

  • Сравнивайте DataGridView.Columns.Count и имена столбцов через Columns[i].Name.
  • Используйте try-catch с логированием конкретных ошибок (например, ArgumentException при несовпадении типов).
  • Для сложных сценариев реализуйте адаптер, преобразующий данные между структурами (например, intstring через ToString()).

В случаях, когда данные загружаются из внешних источников (CSV, JSON, SQL), применяйте схемы валидации. Например, при десериализации JSON с помощью Newtonsoft.Json используйте атрибут [JsonProperty(Required = Required.Always)] для обязательных полей. Для SQL-запросов проверяйте метаданные через SqlDataReader.GetSchemaTable(), сравнивая их с ожидаемой структурой DataGridView. При обнаружении расхождений генерируйте пользовательское исключение с деталями: имя отсутствующего столбца, ожидаемый и фактический тип. Это ускорит отладку и позволит реализовать fallback-стратегии, например, подстановку значений по умолчанию или отображение предупреждения пользователю.

Пример кода с комментариями для быстрого внедрения

Ниже приведён рабочий пример синхронизации двух DataGridView через привязку к общему источнику данных и обработку событий. Код использует класс BindingSource для управления данными и событие SelectionChanged для обновления связанной таблицы при выборе строки. В примере предполагается, что первый DataGridView отображает список заказов, а второй – товары в выбранном заказе.

// Создаём источник данных для заказов и товаров
DataTable ordersTable = new DataTable("Orders");
ordersTable.Columns.Add("OrderID", typeof(int));
ordersTable.Columns.Add("CustomerName", typeof(string));
ordersTable.Rows.Add(1, "Иванов");
ordersTable.Rows.Add(2, "Петров");
DataTable orderItemsTable = new DataTable("OrderItems");
orderItemsTable.Columns.Add("ItemID", typeof(int));
orderItemsTable.Columns.Add("OrderID", typeof(int)); // Внешний ключ
orderItemsTable.Columns.Add("ProductName", typeof(string));
orderItemsTable.Rows.Add(1, 1, "Ноутбук");
orderItemsTable.Rows.Add(2, 1, "Мышь");
orderItemsTable.Rows.Add(3, 2, "Клавиатура");
// Привязываем DataGridView к источникам
BindingSource ordersBindingSource = new BindingSource();
ordersBindingSource.DataSource = ordersTable;
dataGridViewOrders.DataSource = ordersBindingSource;
BindingSource orderItemsBindingSource = new BindingSource();
orderItemsBindingSource.DataSource = orderItemsTable;
// Фильтруем товары по выбранному заказу
dataGridViewOrders.SelectionChanged += (sender, e) => {
if (dataGridViewOrders.CurrentRow != null) {
int selectedOrderId = (int)dataGridViewOrders.CurrentRow.Cells["OrderID"].Value;
orderItemsBindingSource.Filter = $"OrderID = {selectedOrderId}";
}
};
// Настраиваем второй DataGridView
dataGridViewOrderItems.DataSource = orderItemsBindingSource;
dataGridViewOrderItems.Columns["OrderID"].Visible = false; // Скрываем внешний ключ

Для корректной работы убедитесь, что свойство SelectionMode первого DataGridView установлено в FullRowSelect. При динамическом изменении данных вызывайте ordersBindingSource.ResetBindings(false) и orderItemsBindingSource.ResetBindings(false) для принудительного обновления отображения.

Вопрос-ответ:

Ссылка на основную публикацию