Data Grid View. User Deleting Row Подія
Деякі відомості відносяться до попередньої версії продукту, до якої можуть бути внесені істотні зміни до випуску. Майкрософт не надає жодних явних або певних гарантій щодо наведених тут відомостей.
Відбувається, коли користувач видаляє рядок з керування DataGridView.
public: event System::Windows::Forms::DataGridViewRowCancelEventHandler ^ UserDeletingRow;
public event System.Windows.Forms.DataGridViewRowCancelEventHandler UserDeletingRow;
public event System.Windows.Forms.DataGridViewRowCancelEventHandler? UserDeletingRow;
member this.UserDeletingRow : System.Windows.Forms.DataGridViewRowCancelEventHandler
Public Custom Event UserDeletingRow As DataGridViewRowCancelEventHandler
Тип події
Приклади
У наступному прикладі коду показано, як використовувати UserDeletingRow подію для скасування видалення рядків з DataGridView, якщо початковий рядок балансу включений у виділений фрагмент. Цей приклад є частиною більшого прикладу, доступного у події SelectionChanged .
private void DataGridView1_UserDeletingRow(object sender, DataGridViewRowCancelEventArgs e) < DataGridViewRow startingBalanceRow = DataGridView1.Rows[0]; // Check if the Starting Balance row included in the selected rows if (DataGridView1.SelectedRows.Contains(startingBalanceRow)) < // Do not allow the user to delete the Starting Balance row. MessageBox.Show("Cannot delete Starting Balance row!"); // Усунення неспроможності, якщо загальний рівень балансу включає. e.Cancel = true; >>
Private Sub UserDeletingRow(ByVal sender As Object, _ ByVal e As DataGridViewRowCancelEventArgs) _ Handles DataGridView1.UserDeletingRow Dim startingBalanceRow As DataGridViewRow = DataGridView1.Rows(0) ' Якщо DataGridView1.SelectedRows.Contains(startingBalanceRow) Then ' не може дозволити користувачеві скористатися рівнем запуску балансу. MessageBox.Show("Cannot delete Starting Balance row!") ' Cancel the deletion if the Starting Balance row is included. e.Cancel = True End If End Sub
Коментарі
Цю подію можна скасувати, щоб запобігти видаленню рядків.
Додаткові відомості про обробку подій див. у розділі Обробка та створення подій.
Обробка подій таблиць даних
Об'єкт DataTable надає низку подій, які може обробляти програму. У наступній таблиці наводиться опис подій DataTable.
| Захід |
Description |
| Initialized |
Відбувається після виклику методу EndInit об'єкта DataTable. Головним чином, ця подія призначена для підтримки сценаріїв під час розробки. |
| ColumnChanged |
Відбувається після успішної зміни значення в об'єкті DataColumn. |
| ColumnChanging |
Відбувається при подачі значення об'єкта DataColumn . |
| RowChanged |
Відбувається після успішної зміни значення DataColumn або стану RowState об'єкта DataRow в об'єкті DataTable. |
| RowChanging |
Відбувається при подачі зміни значення DataColumn або стану RowState об'єкта DataRow в об'єкті DataTable . |
| RowDeleted |
Відбувається після того, як об'єкт DataRow в об'єкті DataTable був позначений, як Deleted . |
| RowDeleting |
Відбувається перед тим, як об'єкт DataRow в об'єкті DataTable буде позначений як Deleted . |
| TableCleared |
Відбувається після того, як виклик методу Clear об'єкту DataTable успішно очистив кожен об'єкт DataRow . |
| TableClearing |
Відбувається після виклику методу Clear, але на початок операції Clear. |
| TableNewRow |
Відбувається після створення нового об'єкта DataRow шляхом виклику методу NewRow об'єкта DataTable. |
| Disposed |
Відбувається, коли об'єкт DataTable видаляється Disposed . Успадковується від MarshalByValueComponent. |
Більшість операцій, які додають чи видаляють рядки, не викликають подій ColumnChanged та ColumnChanging . Однак метод ReadXml викликає події ColumnChanged і ColumnChanging , якщо тільки властивості XmlReadMode не задано значення DiffGram або Auto , коли читаний XML-документ є DiffGram .
Зміна даних в об'єкті DataSet , з якого була викликана подія RowChanged , може призвести до пошкодження даних. При такому пошкодженні даних виняток не буде викликано.
Додаткові пов'язані події
Властивість Constraints містить екземпляр ConstraintCollection. Клас ConstraintCollection представляє подію CollectionChanged. Ця подія виникає при додаванні, зміні або видаленні обмежень із колекції ConstraintCollection .
Властивість Columns містить екземпляр DataColumnCollection. Клас DataColumnCollection представляє подію CollectionChanged. Ця подія виникає при додаванні, зміні або видаленні з DataColumn колекції об'єкта DataColumnCollection . Серед змін, які можуть спричинити виникнення цієї події, – зміни імені, типу, виразу або порядкового номера стовпця.
Властивість Tables об'єкта DataSet містить екземпляр DataTableCollection. Клас DataTableCollection викликає події CollectionChanged і CollectionChanging. Ці події виникають при додаванні або видаленні з колекції DataTable об'єкта DataSet.
Внесення змін до об'єктів DataRows також можуть викликати події для асоційованого об'єкта DataView. Клас DataView викликає подію ListChanged, яка виникає при зміні значення DataColumn або зміні композиції або порядку сортування подання.Клас DataRowView викликає подію PropertyChanged, що виникає за зміни значення асоційованого об'єкта DataColumn .
Послідовність операцій
При додаванні, зміні або видаленні об'єкта DataRow слід виконати наступну послідовність операцій.
- Створіть запропонований запис та внесіть зміни.
- Перевірте обмеження для стовпців, які не входять до виразів.
- Зателефонуйте RowChanging або RowDeleting , залежно від ситуації.
- Зробіть запропонований запис поточного.
- Оновіть усі асоційовані індекси.
- Викличте події ListChanged для асоційованих об'єктів DataView, а також події PropertyChanged для асоційованих об'єктів DataRowView.
- Оцініть усі стовпці виразу, але відкладіть перевірку будь-яких обмежень цих стовпців.
- Викличте події ListChanged для асоційованих об'єктів DataView , а також події PropertyChanged для асоційованих об'єктів DataRowView , які стосуються оцінок стовпців виразу.
- Зателефонуйте RowChanged або RowDeleted , залежно від ситуації.
- Перевірте обмеження на стовпці виразу.
Внесення змін до стовпців виразів ніколи не викликає події DataTable. Внесення змін у стовпці виразів викликає лише події DataView та DataRowView. Стовпці виразу можуть мати залежність від кількох інших стовпців, їх оцінка під час однієї операції DataRow може здійснюватися кілька разів. Події викликаються при обчисленні кожного виразу, а при роботі зі стовпцями виразів одна операція DataRow може викликати кілька подій ListChanged і PropertyChanged, можливо, з кількома подіями для одного стовпця виразів.
Не викликайте виключення NullReferenceException в обробнику події RowChanged. Якщо виняток NullReferenceException викликається в межах події RowChanged об'єкта DataTable , об'єкт DataTable буде пошкоджено.
приклад
У наступному прикладі демонструється, як створювати обробники подій для подій RowChanged, RowChanging, RowDeleted, RowDeleting, ColumnChanged, ColumnChanging, TableNewRow, TableCleared та TableClearing. Кожен обробник події при події відображає дані в консольному вікні.
static void DataTableEvents() < DataTable table = new("Customers"); // Add two columns, id and name. table.Columns.Add("id", typeof(int)); table.Columns.Add("name", typeof(string)); // Set the primary key. table.Columns["id"].Unique = true; table.PrimaryKey = New DataColumn[] < table.Columns["id"] >; // Add a RowChanged event handler. table.RowChanged += Row_Changed; // Add a RowChanging event handler. table.RowChanging + = Row_Changing; // Add a RowDeleted event handler. table.RowDeleted += Row_Deleted; // Add a RowDeleting event handler. table.RowDeleting += Row_Deleting; // Add a ColumnChanged event handler. table.ColumnChanged += Column_Changed; // Add a ColumnChanging event handler. table.ColumnChanging + = Column_Changing; // Add a TableNewRow event handler. table.TableNewRow += Table_NewRow; // Add a TableCleared event handler. table.TableCleared += Table_Cleared; // Add a TableClearing event handler. table.TableClearing += Table_Clearing; // Add a customer. DataRow row = table.NewRow(); row["id"] = 1; row["name"] = "Customer1"; table.Rows.Add(row); table.AcceptChanges(); // Change the customer name. table.Rows[0]["name"] = "ChangedCustomer1"; // Delete the row. table.Rows[0].Delete(); // Clear the table.table.Clear(); > static void Row_Changed(object sender, DataRowChangeEventArgs e) => Console.WriteLine("Row_Changed Event: name=; action=", e.Row["name"], e.Action); static void Row_Changing(object sender, DataRowChangeEventArgs e) => Console.WriteLine("Row_Changing Event: name=; action=", e.Row["name"], e.Action); static void Row_Deleted(object sender, DataRowChangeEventArgs e) => Console.WriteLine("Row_Deleted Event: name=; action=", e.Row["name", DataRowVersion.Original], e.Action); static void Row_Deleting(object sender, DataRowChangeEventArgs e) => Console.WriteLine("Row_Deleting Event: name=; action=", e.Row["name"], e.Action); static void Column_Changed(object sender, DataColumnChangeEventArgs e) => Console.WriteLine("Column_Changed Event: ColumnName=; RowState=", e.Column.ColumnName, e.Row.RowState); static void Column_Changing(object sender, DataColumnChangeEventArgs e) => Console.WriteLine("Column_Changing Event: ColumnName=; RowState=", e.Column.ColumnName, e.Row.RowState); static void Table_NewRow(object sender, DataTableNewRowEventArgs e) => Console.WriteLine("Table_NewRow Event: RowState=", e.Row.RowState.ToString()); static void Table_Cleared(object sender, DataTableClearEventArgs e) => Console.WriteLine("Table_Cleared Event: TableName=; Rows=", e.TableName, e.Table.Rows.Count.ToString()); static void Table_Clearing(object sender, DataTableClearEventArgs e) => Console.WriteLine("Table_Clearing Event: TableName=; Rows=", e.TableName, e.Table.Rows.Count.ToString());
Private Sub DataTableEvents() Dim table As New DataTable("Customers") ' Add 2 columns, id and name. table.Columns.Add("id", Type.GetType("System.Int32")) table.Columns.Add("name", Type.GetType("System.String")) ' Set the primary key. table.Columns("id").Unique = True table.PrimaryKey = New DataColumn() ' Add a RowChanged event handler. AddHandler table.RowChanged, _ New DataRowChangeEventHandler(AddressOf Row_Changed) ' Add a RowChanging Event Handler.AddHandler table.RowChanging, _ New DataRowChangeEventHandler(AddressOf Row_Changing) ' Add a RowDeleted event handler. AddHandler table.RowDeleted, New _ DataRowChangeEventHandler(AddressOf Row_Deleted) ' Add a RowDeleting event handler. AddHandler table.RowDeleting, New _ DataRowChangeEventHandler(AddressOf Row_Deleting) ' Add a ColumnChanged event handler. AddHandler table.ColumnChanged, _ New DataColumnChangeEventHandler(AddressOf Column_Changed) ' Add a ColumnChanging Event handler for table. AddHandler table.ColumnChanging, New _ DataColumnChangeEventHandler(AddressOf Column_Changing) ' Add a TableNewRow event handler. AddHandler table.TableNewRow, New _ DataTableNewRowEventHandler(AddressOf Table_NewRow) ' Add a TableCleared event handler. AddHandler table.TableCleared, New _ DataTableClearEventHandler(AddressOf Table_Cleared) ' Add a TableClearing event handler. AddHandler table.TableClearing, New _ DataTableClearEventHandler(AddressOf Table_Clearing) ' Add a customer. Dim row As DataRow = table.NewRow() row("id") = 1 row("name") = "Customer1" table.Rows.Add(row) table.AcceptChanges() ' Change the customer name. table.Rows(0).Item("name") = "ChangedCustomer1" ' Delete the row. table.Rows(0).Delete() ' Clear the table.table.Clear() End Sub Private Sub Row_Changed(ByVal sender As Object, _ ByVal e As DataRowChangeEventArgs) Console.WriteLine("Row_Changed Event: name=; action=", _ e.Row("name"), e.Action ) End Sub Private Sub Row_Changing(ByVal sender As Object, _ ByVal e As DataRowChangeEventArgs) Console.WriteLine("Row_Changing Event: name=; action=", _ e.Row("name"), e.Action) End Sub ByVal e As DataRowChangeEventArgs) Console.WriteLine("Row_Deleted Event: name=; action=", _ e.Row("name", DataRowVersion.Original), e.Action) End Sub Private Sub Row_Deleting(ByVal sender As Object, _ ByVal e As DataRowChange Console.WriteLine("Row_Deleting Event: name=; ; RowState=", _ e.Column.ColumnName, e.Row.RowState) Назад Sub Private Sub Column_Changing(ByVal sender As Object, _ ByVal e As DataColumnChangeEventArgs) Console.WriteLine("Column_Changing Event: ColumnName=; RowState=", _ e.Column.ColumnName, e.Row.RowState) End Sub Private Sub Table_NewRow(ByVal sender As Object, _ ByVal e As DataTableNewRowEventArgs) Console.WriteLine("Table_NewRow Event: RowState=". .RowState.ToString()) End Sub Private Sub Table_Cleared(ByVal sender As Object, _ ByVal e As DataTableClearEventArgs) Console.WriteLine("Table_Cleared Event: TableName=; Rows=", _ e.TableName, e.Table.Rows.Count.ToString()) End Sub Private Sub Table_Clearing(ByVal sender As Object, _ ByVal e As DataTableClearEventArgs) Console.WriteLine("Table_Clearing Event: TableName=; Rows=", _ e.TableName, e.Table.Rows.Count.ToString()) End Sub
також
DataGridView управління базою даних
Під керуванням базою даних у додатку розуміється редагування, вставка та видалення рядків.Додаток дозволяє редагувати вміст бази даних в режимі "Що бачу, те і отримую" (технологія WYSIWYG).
Виконуючі методи-події уніфіковані для візуальних елементів, що редагуються. DataGridView. Процедура вставки нового рядка та редагування існуючого включає практично однакові дії. Процес видалення рядків має логіку відміни дії. Джерела даних для елементів DataGridView - це об'єкти типу BindingSource, які взаємодіють з базою даних за допомогою одного екземпляра DataSet.
Програмний код програми управління базою даних містить трохи додаткових рядків, основна логіка побудована на вже закладеній в DataGridView функціональність. У додатку програмно реалізовані відносини між таблицями та обмеження Foreign Key, Unique.
Формування інтерфейсу DataGridView
Інтерфейс програми побудований на трьох елементах DataGridView. Як джерело даних для елементів відображення таблиць у додатку застосовані об'єкти типу BindingSource. Використання класу BindingSource забезпечує додатком взаємопов'язані маніпуляції з таблицями даних.
Перший елемент DataGridView Відображає таблицю типів типів продуктів. Другий елемент використовує джерело даних першого елемента та показує тимчасову таблицю вибірки продуктів певного типу. Третій елемент виводить усю таблицю продуктів Products.
У свою чергу об'єкти BindingSource, отримують дані з бази за допомогою одного екземпляра DataSet. Заповнення DataSet можливо двома способами.
Метод формування інтерфейсу елементів відображення таблиць. У таблиці додаються стовпці типу DataGridViewComboBoxColumn для відображення імені типу замість числового ідентифікатора.
void BindDataSource(DataSet ds, DataGridView dgvParent, DataGridView dgvDetail, DataGridView dgvChild) < // === Створення функціонального інтерфейсу елемента DataGridView === // Джерела даних для DataGridView.BindingSource parent = new(); BindingSource detail = new(); BindingSource child = new(); // Таблиця типів, що редагується, батьківська для таблиці Product. parent.DataSource = ds; parent.DataMember = Constants.ParentTableName; // Заголовок з назвою поточної таблиці. groupBoxParent.Text = "Таблиця """ + Constants.ParentTableName + """"; // Таблиця перегляду продуктів вибраного типу. detail.DataSource = parent; // Відображаємо таблицю відповідно до обраного зовнішнього ключа. detail.DataMember = "FK_Product_Type"; groupBoxDetail.Text = "Продукти вибраного типу"; // таблиця, що редагується Product child.DataSource = ds; child.DataMember = Constants.ChildTableName; // Зв'язування джерел даних з елементами управління // відображення відповідних таблиць. dgvParent.DataSource = parent; dgvDetail.DataSource = detail; dgvChild.DataSource = child; // Видимість стовпців у залежності від зміни рішення. // Невидимість стовпця ідентифікаторів. // Для зручності створено порядкові номери у заголовках рядків. // Ідентифікатори зовнішніх ключів ховаються // залишаються тільки назви типів. #if DEBUG == false dgvParent.Columns["Id"].Visible = false; dgvDetail.Columns["Id"].Visible = false; dgvChild.Columns["Id"].Visible = false; dgvDetail.Columns["TypeId"].Visible = false; dgvChild.Columns["TypeId"].Visible = false; #endif //===Таблиця вибраного типу === // Вставка стовпця НазваТипу в таблицю вибраного типу DataGridViewComboBoxColumn colbox2 = new(); colbox2.Name = "TypeName"; // Таким кодом активізується зв'язок між батьківською таблицею та // дочірньою для відображення в елементах DataGridView. colbox2.DataSource = parent; // Так зв'язку нічого очікувати. // colbox.DataSource = ds.Tables["Type"]; colbox2.DisplayMember = "Name"; colbox2.ValueMember = "Id"; colbox2.DataPropertyName = "TypeId"; colbox2.DisplayIndex = 1; colbox2.FlatStyle = FlatStyle.Flat; // Якщо закоментувати рядок нижче біля осередків DataGridViewComboBoxColumn// з'явиться кнопка виклику списку.colbox2.DisplayStyle = DataGridViewComboBoxDisplayStyle.Nothing; dgvDetail.Columns.Add(colbox2); // ==== // === Відображення назви таблиці. .ChildTableName + """; // Додавання способу установки типу продукту на ім'я. DataGridViewComboBoxColumn = new(); colbox.Name = "TypeName"; "Id"; colbox.DataPropertyName = "TypeId"; 1;colbox.FlatStyle = FlatStyle.Flat; Colbox.SortMode = DataGridViewColumnSortMode.Automatic; dgvChild.Columns.Add(colbox);
Наповнення DataSet даними
У додатку є два варіанти отримання даних із бази в об'єкт DataSet.Обидва способи працюють однаково, але другий дозволяє трохи спростити програмний код.
Перший спосіб, класичний, DataSet заповнюється методом SqlDataAdapter.Fill(. ). DataSet не отримує з бази якості IDENTITY (AUTO_INCREMENT) первинних ключів, назви таблиць, значення NOT NULL та інше. Для відповідності імен таблиць вихідним у базі даних додатково потрібна карта зіставлення назв таблиць.
Другий спосіб заснований на отримання даних через SqlDataReader у вигляді списку таблиць. даних видають будь-які схемо-відомості.
public static DataSet FillDataAdapter(string querystring, List? parameters = null) < using SqlConnection connection = new(ConnectionString); SqlDataAdapter adapter = GetAdapter(connection, querystring, parameters); DataSet dataSet = new("Catalog"); // Карта зіставлення назв таблиць набору DataSet та бази даних. adapter.TableMappings.Add("Table", Constants.ParentTableName); adapter.TableMappings.Add("Table1", Constants.ChildTableName); adapter.Fill(dataSet); return dataSet; >public static DataSet FillDataReader(string querytext, List? parameters = null) < using SqlConnection connection = new(ConnectionString); // C# 9.0 using SqlCommand command = new (querytext, connection); // C# 9.0 if (parameters != null) < foreach (SqlParameter p in parameters) < command.Parameters.Add(p); >> connection.Open(); List tables = new(); // Отримуємо багато даних таблиць, зокрема. та назва таблиць автоматично. // Отримуємо: властивості автоінкремента первинного ключа, властивості стовпців // NOT NULL та ін. SqlDataReader reader = command.ExecuteReader(CommandBehavior.KeyInfo); while (reader.IsClosed == false) < DataTable table = new(); table.Load(reader); tables.Add(table); > DataSet dataSet = New DataSet ("Catalog"); // Додавання таблиць до колекції набору. foreach (DataTable dt in tables) < dataSet.Tables.Add(dt); >return dataSet; > public static void SaveData(DataTable dt, string querystring, List? parameters = null) < using SqlConnection connection = new(ConnectionString); SqlDataAdapter adapter = GetAdapter(connection, querystring, parameters); // Для оновлення передається таблиця. // adapter.Update оновлює базу даних таблично. // Щоб не передавати в даний метод два параметри: // DataSet та назва таблиці, передається один параметр DataTable // з назвою таблиці всередині. int i = adapter.Update(dt); // Звук успішного оновлення – для режиму тестування.#if DEBUG if (i> 0) Console.Beep(3000, 100); #endif> // Метод формує різний код залежно від // способу отримання даних. void SetRelationConstraint(DataSet ds, bool isdataadapter) < DataTable parentTable = ds.Tables[Constants.ParentTableName]; DataTable childTable = ds.Tables[Constants.ChildTableName]; // Визначення відносин між таблицями. / / У базі даних ці відносини не визначені. DataColumn parentColumn = parentTable.Columns["Id"]; DataColumn childColumn = childTable.Columns["TypeId"]; . . . // Код необхідний лише при заповненні DataSet за допомогою SqlDataAdapter. // Якщо заповнення DataSet побудувати на DataTable цей код не потрібно. if (isdataadapter == true) < // Встановлення якості автонумерації ідентифікаторів. DataColumn columnId = parentColumn; columnId.AutoIncrement = true; columnId.ReadOnly = true; DataColumn columnId2 = childTable.Columns["Id"]; columnId2.AutoIncrement = true; columnId2.ReadOnly = true; // Вручну встановлюємо заборону NULL. // Якщо заповнення DataSet побудувати на DataTable цей рядок не потрібно. parentTable.Columns["Name"].AllowDBNull = false; childTable.Columns["SKU"].AllowDBNull = false; >>
Вставка нового рядка
Метод події DataGridView.RowEnter() у додатку починає процедуру створення нового рядка. Ця подія генерується в момент отримання фокусу будь-яким рядком елемента керування DataGridView. Рядки у додатку можуть додаватися лише по одній штуці.
При отриманні фокусу найнижчим рядком, призначеним для формування нового рядка, в комірку ідентифікатора записується значення, яке гарантовано не збігається з будь-яким значенням первинного ключа. Далі у необхідні осередки вводяться значення.
Після закінчення редагування нового рядка переклад фокусу на інший рядок викликає подію DataGridView.RowValidated() , новий рядок позначається як AddedRow і далі записується в базу даних.
Одночасно із записом нового рядка з бази даних запитується її ідентифікатор, яким перезаписується тимчасове значення первинного ключа. Це дуже важливий захід: без синхронізації первинних ключів таблиць елементів DataGridView та таблиці бази даних можливе небажане видалення або перезаписування важливої інформації.
private void RowEnter(object sender, DataGridViewCellEventArgs e) < // === Програмний код участі у створенні нового рядка. DataGridView dgv = (DataGridView)sender; // Поки новий рядок не додано таблицю джерела даних, // її невідомий ідентифікатор. if (dgv.Rows[e.RowIndex].IsNewRow == true) < dgv.Rows[e.RowIndex].Cells["Id"].Value = "-1"; >> private void RowValidated(object sender, DataGridViewCellEventArgs e) < // Запис змін до бази даних. Запити UPDATE, INSERT, // виключаючи DELETE. DataGridView dgv = (DataGridView)sender; BindingSource bs = (BindingSource)dgv.DataSource; // Отримання посилання на редаговану частину джерела даних. DataSet ds = (DataSet)bs.DataSource; string tablename = bs.DataMember; // Збереження до бази даних змін та вставок рядків. SaveModifications(ds, tablename); // Погашення тексту та іконки помилок пов'язаних з осередками. foreach (DataGridViewCell cell in dgv.Rows[e.RowIndex].Cells) < cell.ErrorText = null; >> // Логіка запису UPDATE, INSERT до бази даних. void SaveModifications(DataSet ds, string tablename) < DataTable? dt = ds.Tables[tablename]; if (dt != null && ds.HasChanges() == true) < if (ds.HasChanges(DataRowState.Added) == true) InsertRow(dt); if (ds.HasChanges(DataRowState.Modified) == true) UpdateRow(dt); >> // Метод запису SQL запиту INSERT до бази даних. void InsertRow(DataTable dt) <string tablename = dt.TableName; // Отримуємо посилання на новий рядок.DataRow? row = dt.AsEnumerable().FirstOrDefault(r =>r.RowState == DataRowState.Added); if (row == null) return; List parameters = new(); List listnames = new(); List listvalues = new(); // Формування списків імен та значень стовпців. int count = 0; foreach (DataColumn col in row.Table.Columns) < // У стовпець із властивістю ідентифікатора IDENTITY // значення записувати не можна. if (col.AutoIncrement == false) < listnames.Add("[" + col.ColumnName + "]"); SqlParameter parUri = new() < // Конвертує в необхідний тип автоматично, // без явної вказівки типу. //DbType = , Direction = ParameterDirection.Input, ParameterName = "Param" + count, Value = row [col.ColumnName] >; parameters.Add(parUri); count++; listvalues.Add("@" + parUri.ParameterName); > > string names = string.Join(',', listnames); string values = string.Join(',', listvalues); // Очікуваний ідентифікатор вставленого рядка. SqlParameter lastId = new() < Direction = ParameterDirection.Output, ParameterName = "Identity", Size = 8 >; parameters.Add(lastId); // Одночасно зі вставкою рядка отримуємо її ідентифікатор // для синхронізації ключів елемента DataGridView і бази даних. string queryString = "INSERT INTO" + tablename + "(" + names + ") VALUES(" + values + ");"; // Функція IDENT_CURRENT( 'table_or_view' ) повертає // останнє значення ідентифікатора для зазначеної таблиці. queryString += "SET @Identity = IDENT_CURRENT('" + tablename + "');"; //queryString += "SELECT @Identity=IDENT_CURRENT ('Product');"; ConnectDatabase.SaveData(dt, queryString, parameters); // Ідентифікатор нового рядка набуває актуального значення. dt.Columns["Id"]!.ReadOnly = false; row["Id"] = lastId.Value; dt.Columns["Id"]!.ReadOnly = true; // Ухвалення змін після присвоєння цього ідентифікатора рядка. // інакше логіка SqlDataAdapter.Update() думатиме, що це модифікація рядка і // без необхідності перезапише цей рядок у базі даних. row.AcceptChanges(); >
Редагування рядка
Логічна процедура схожа на послідовність вставки рядка: одержання фокуса рядка - подія RowEnter() , редагування осередків, переклад фокусу - подія RowValidated() .
Редагування рядків полягає у зміні значень заповнених осередків або додаванні значень до порожніх осередків таблиць елемента управління DataGridView.
Після закінчення редагування рядка перекладом фокусу викликається подія DataGridView.RowValidated(), відредагований рядок позначається як ModifiedRow і зберігається у базі даних.
Метод запису змінених та нових значень рядка до бази даних, виконання SQL запиту UPDATE . Програмний код методів подій дивіться вище.
void UpdateRow(DataTable dt) <string tablename = dt.TableName; // Отримання посилання змінений рядок. DataRow? row = dt.AsEnumerable().FirstOrDefault(r =>r.RowState == DataRowState.Modified); if (row == null) return; List parameters = new(); List listnamesvalues = new(); object int count = 0; foreach (DataColumn col in row.Table.Columns) < string columnname = col.ColumnName; object value = row[col.ColumnName]; // Ідентифікатор не можна перезаписувати. if (col.AutoIncrement == false) < SqlParameter parUri = new() < // Конвертує в необхідний тип автоматично, // без явної вказівки типу. //DbType = , Direction = ParameterDirection.Input, ParameterName = "Param" + count, Value = row [col.ColumnName] >; parameters.Add(parUri); count++; // Напівфабрикат [ім'я] = значення. для рядка запиту. listnamesvalues.Add("[" + col.ColumnName + "]=@" + parUri.ParameterName); > > // Ідентифікатор модифікованого рядка // передається у вигляді параметра. SqlParameter < Direction = ParameterDirection.Input, ParameterName = "Identity", Value = id >; parameters.Add(Id); string namesvalues = string.Join(',', listnamesvalues); string queryString = "UPDATE" + tablename + "SET" + namesvalues + "WHERE ConnectDatabase.SaveData(dt, queryString, parameters); >
Видалення рядків
Щоб видалити рядки з бази, необхідно виділити їх і натиснути клавішу DELETE. Програмна послідовність видалення рядків включає три події:
DataGridView.KeyDown() – забезпечує діалоговий інтерфейс підтвердження чи скасування процедури видалення рядків.
DataGridView.UserDeletingRow() – програмна логіка скасування процедури видалення рядків.
DataGridView.RowsRemoved() – рядки, що видаляються, вже помічені як DeletedRow і передаються в метод видалення з бази даних.
private void DGVKeyDown(object sender, KeyEventArgs e) < // === Інтерфейс діалогу видалення та скасування видалення рядків === DataGridView dgv = (DataGridView)sender; BindingSource bs = (BindingSource)dgv.DataSource; DataSet ds = (DataSet)bs.DataSource; string tablename = bs.DataMember; if (e.KeyCode == Keys.Delete) < if (ds != null) < int numberrowsdelete = dgv.SelectedRows.Count; DataTable? dt = ds.Tables[tablename]; if (dt! = null) < // Зовнішній прапор видалення рядків для перенесення рішення в інший метод. // За замовчуванням забороняємо видалення рядків. _deleteRows = false; if (MessageBox.Show( "Видалити рядки? Кількість - " + numberrowsdelete, "Видалення рядків!", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation) == DialogResult.Yes) < // Видалення дозволено. _deleteRows = true; >>>>. . . > private void UserDeletingRow(object sender, DataGridViewRowCancelEventArgs e) < // === Логіка скасування видалення рядків === // true - скасування дії, false - продовження дії. e.Cancel = !_deleteRows; >private void RowsRemoved(object sender, DataGridViewRowsRemovedEventArgs e) < // === Безпосереднє видалення рядків === DataGridView dgv = (DataGridView)sender; BindingSource bs = (BindingSource)dgv.DataSource; DataSet ds = (DataSet)bs.DataSource; string tablename = bs.DataMember; DataTable? dt = ds.Tables[tablename]; if (dt! = null) < DeleteRows(dt); >> void DeleteRows(DataTable dt) <string tablename = dt.TableName; // Отримання рядків позначених видалення.DataRow? row = dt.AsEnumerable().FirstOrDefault(r =>r.RowState == DataRowState.Deleted); if (row! = null) < // Отримання ідентифікатора віддаленого рядка. string? DataRowVersion.Original].ToString(); if (id == null) return; // Видаляємо рядок з бази за його ідентифікатором. string queryString = "DELETE FROM" + tablename + "WHERE;"; ConnectDatabase.SaveData(dt, queryString); >>
Порядкові номери рядків
Первинні ключі таблиць бази даних мають властивість IDENTITY (для MySQL AUTO_INCREMENT) і збільшуються рівними кроками після вставки нового рядка. Але рівномірність порушується при видаленні рядків і користувачів не дуже логічно виглядають такі значення.
Для комфорту користувачів у рядкових заголовних осередках вставляються порядкові номери рядків, а стовпці з ідентифікаторами ховаються. Оновлення порядкових номерів відбувається після підключення джерела даних та змін його вмісту.
У таблиці елемента DataGridView, пов'язаним із джерелом даних, змінити значення в заголовках рядків можна лише після завершення прив'язки. Тому обчислення та вставка номерів відбувається у події DataGridView.DataBindingComplete() .
private void DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e) < // Ця подія виникає при підключенні джерела даних та // при зміні його вмісту. // При елементі DataGridView пов'язаних із джерелом даних, // змінити значення в заголовках рядків можна // тільки після завершення прив'язки. DataGridView dgv = (DataGridView)sender; foreach (DataGridViewRow row in dgv.Rows) < // Порядковий номер не ставиться в новому рядку, який ще не доданий в джерело даних. if (row.IsNewRow == false) row.HeaderCell.Value = (row.Index + 1).ToString(); >>
Оповіщення про незбережені дані
При змінах у комірках редагованого рядка таблиць виникає подія DataGridView.CellValueChanged(). У тілі методу цієї події відредаговані, але не збережені осередки позначаються іконкою з підказкою, що випливає.Такий стан може виникати у випадках редагування користувачем кількох осередків одного рядка без втрати фокусу рядком. Як тільки редагований рядок втратить фокус, незбережені значення запишуться до бази даних (див. вище), а іконки сповіщення зникнуть.
Програмна логіка оповіщення про незбережені зміни:
private void CellValueChanged(object sender, DataGridViewCellEventArgs e) < // Заголовки рядків не редагуються. if (e.ColumnIndex >= 0) < DataGridView dgv = (DataGridView)sender; // Не призначено нових рядків (які внизу) елемента DataGridView. if (dgv.Rows[e.RowIndex].IsNewRow == false) < // Оповіщення користувача про незбережені значення в осередках. dgv.Rows[e.RowIndex]. Cells[e.ColumnIndex].ErrorText = "Є незбережені зміни!"; >> > private void RowValidated(object sender, DataGridViewCellEventArgs e) < . . . // Погашення тексту та іконки помилок пов'язаних з осередками. foreach (DataGridViewCell cell in dgv.Rows[e.RowIndex].Cells) < cell.ErrorText = null; >>
Оповіщення про помилки
Помилки, що виникають у процесі редагування таблиць DataGridView попереджаються подією DataGridView.DataError(). У методі розміщується найпростіший код діалогового оповіщення для вибору подальших дій користувачем. Подія підключена до всіх редагованих елементів DataGridViewОбчислення власника відбувається за параметром sender.
private void DataError(object sender, DataGridViewDataErrorEventArgs e) < DataGridView dgv = (DataGridView)sender; BindingSource bs = (BindingSource)dgv.DataSource; string tablename = bs.DataMember; // Якщо натиснути кнопку Ок помилкове значення можна змінити, // натискання на кнопку Скасувати (Cancel) повертає попереднє // коректне значення. if (MessageBox.Show("Таблиця" + tablename + "Помилка: комірка" + e.RowIndex + "x" + e.ColumnIndex + " " + e.Exception.Message, "Увага", MessageBoxButtons.OKCancel, MessageBoxIcon. ) == DialogResult.OK) < e.Cancel = true; >>
Вихідник програми управління базою даних
Вихідник написаний мовою C#, середовище програмування MS Visual Studio 2022, .NET 6. У рішенні програми знаходиться скрипт для створення бази даних. Скрипт також можна завантажити на сторінці Relations Tables в DataSet.
Завантажити вихідний
SQL Server
- Технології: PHP, Python, Perl
- APC, Zend OPcache (PHP 5.6, 7+), memcached
- VDS: Ubuntu, Debian, CentOS, Windows
- Тестовий період для всіх послуг – 10 днів
Створення сайтів, написання програм, скриптів C++, C#, F#, PHP, JavaScript