C windows forms datagrid

В этой статье мы рассмотрим основные принципы работы с элементом DataGridView в приложении Windows Forms и напишем тестовую программу на языке C#, которая будет выводить в элемент DataGridView список названий книг, их авторов и год публикации книг. Подобный список мог бы использоваться в какой-то специализированной программе, например, для автоматизации работы книжной библиотеки.

Элемент DataGridView применяется для отображения данных в табличной форме с возможностью управления этими данными. Например, с его помощью можно регулировать способ выбора строк или ячеек таблицы, производить правку данных непосредственно в самом элементе DataGridView, сортировать данные в таблице и так далее.

В нашей программе мы предусмотрим и реализуем следующие возможности:

  • коллекция книг будет храниться в памяти и устанавливаться в качестве источника данных для элемента DataGridView
  • мы сделаем быстрый поиск по списку книг, т.е. поддержим механизм фильтрации данных в элементе DataGridView
  • удаление строк из таблицы через контекстное меню, отображаемое для элемента DataGridView; будем также показывать диалог с подтверждением удаления записи
  • редактирование данных о книгах и их авторах прямо в элементе управления DataGridView, а также через отдельную форму редактирования записи в таблице
  • добавление новых книг в коллекцию и их отображение в элементе DataGridView
  • текстовое поле с отображением внутреннего содержимого списка книг — это позволит отслеживать состояние списка книг в памяти при работе программы.

Вот так будет выглядеть наше приложение:

Приложение будет позволять редактировать элементы прямо в таблице — мы сможем изменять название книги и автора и видеть эти изменения сразу в памяти программы.

Мы также поддержим контекстное меню для элемента DataGridView, которое будет содержать два пункта меню — «Удалить выбранную книгу» и «Редактировать выбранную книгу». При нажатии на пункт меню «Удалить выбранную книгу» запись с данными о книге будет удаляться из таблицы, а также из списка книг в памяти приложения.

При нажатии на второй пункт меню «Редактировать выбранную книгу» будет отображаться отдельная форма с редактированием данных о книге:

 

Перед удалением книги мы будем также отображать всплывающий диалог с подтверждением этого действия:

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

Итак, давайте приступим к созданию проекта в среде разработки Microsoft Visual Studio для нашего приложения.

Создание нового проекта в среде Microsoft Visual Studio

Откройте среду разработки Microsoft Visual Studio и создайте новый проект с типом Приложение Windows Forms (.NET Framework), как показано ниже:

В качестве имени нового проекта задайте DataGridViewSample и выберите местоположение, куда будут сохранены файлы нового проекта (здесь я обычно оставляю все настройки по умолчанию и рекомендую сделать то же самое, просто запомните каталог, указанный в поле «Расположение», чтобы затем открывать его в Проводнике).

Переименование главной формы приложения

После создания проекта будет создана главная форма по умолчанию с именем Form1 и соответствующий ей класс Form1.cs.

Выберем в окне «Обозреватель решений» эту форму и в окне «Свойства» изменим название класса для формы на FrmDataGridViewSampleMain.cs.

В диалоговом окне, запрашивающем подтверждение переименования класса формы и связанных с ней файлов, соглашаемся.

В итоге должно получиться следующее — в окне «Обозреватель решений» форма и зависящие от неё файлы будут переименованы:

А так должно выглядеть окно «Свойства»:

Настройка главной формы приложения и её элементов

Далее нам нужно изменить размеры и свойства главной формы и расположить на ней все нужные нам элементы управления.

Выберите форму, чтобы она появилась в представлении конструктора и установите для формы следующие свойства и их значения:

  • FormBorderStyle — FixedSingle
  • Text — [Allineed.Ru] Пример работы с элементом DataGridView
  • Size — 1026; 568
  • StartPosition — CenterScreen
  • Name — FrmDataGridViewSampleMain
  • MaximizeBox — False
  • MinimizeBox — False

Теперь расположим на главной форме нужные элементы. Для этого в левой части Microsoft Visual Studio находим «Панель элементов» и последовательно перетаскиваем с неё на нашу главную форму следующие элементы, а также устанавливаем им описанные ниже свойства:

1) Один элемент ContextMenuStrip для контекстного меню, которое будет отображаться на элементе DataGridView

Перетаскиваем на форму элемент ContextMenuStrip и устанавливаем ему следующие свойства:

  • Name — ContextMenuStripForGrid

Далее необходимо выбрать этот элемент ContextMenuStrip в нижней части представления конструктора формы, как показано ниже:

После этого в представлении конструктора формы появится выпадающее меню с подсказкой «Вводить здесь». Необходимо ввести с клавиатуры названия для двух новых пунктов меню:

  • &Удалить выбранную книгу
  • &Редактировать выбранную книгу

В результате будут созданы два соответствующих элемента ToolStripMenuItem для контекстного меню (знак & перед текстом пунктов меню позволит при работе программы использовать комбинацию клавиш Alt+<подчёркнутая_буква> для выбора пункта меню с клавиатуры без использования мыши):

Теперь снова последовательно выбираем каждый из этих элементов и устанавливаем им свойства:

Для пункта меню «&Удалить выбранную книгу»:

  • Name — MenuItemRemoveBook

Для пункта меню «&Редактировать выбранную книгу»:

  • Name — MenuItemEditBook

2) Один элемент DataGridView для списка книг

Перетаскиваем на форму элемент DataGridView и устанавливаем ему следующие свойства:

  • CellBorderStyle — RaisedHorizontal
  • ColumnHeadersDefaultCellStyle — открыть окно «Построитель CellStyle» и установить в нём для свойства Font значение: Microsoft Sans Serif; 8,25pt; style=Bold
  • RowsDefaultCellStyle — открыть окно «Построитель CellStyle» и установить в нём для свойства BackColor значение PapayaWhip, а для свойства ForeColor — значение SaddleBrown
  • Location — 12; 41
  • Size — 587; 388
  • ColumnHeadersHeightSizeMode — AutoSize
  • ContextMenuStrip — ContextMenuStripForGrid
  • Name — DataGridViewBooks

3) Один элемент TextBox для текстового представления списка книг в памяти программы

Перетаскиваем на форму элемент TextBox и устанавливаем ему следующие свойства:

  • ScrollBars — Vertical
  • Location — 605; 12
  • Size — 391; 417
  • Multiline — True
  • ReadOnly — True
  • Name — TextBoxBooks

4) Один элемент Label с текстом «Поиск:»

Перетаскиваем на форму элемент Label и устанавливаем ему следующие свойства:

  • Text — Поиск:
  • Location — 12; 15
  • Size — 42; 13
  • Name — LabelSearch

5) Один элемент TextBox для быстрого поиска книги или автора и фильтрации данных в элементе DataGridView

Перетаскиваем на форму элемент TextBox и устанавливаем ему следующие свойства:

  • Text — <оставить поле пустым>
  • Location — 53; 12
  • Size — 546; 20
  • Name — TextBoxSearch

6) Один элемент GroupBox для группировки элементов в нижней части формы

Перетаскиваем на форму элемент GroupBox и устанавливаем ему следующие свойства:

  • Text — Добавить новую книгу в коллекцию:
  • Location — 12; 435
  • Size — 984; 59
  • Name — GroupBoxAddNewBook

Теперь внутрь этого элемента GroupBox перетаскиваем дополнительные элементы и устанавливаем им свойства:

6.1) один элемент Label для названия книги

  • Text — Название:
  • Location — 16; 25
  • Size — 60; 13
  • Name — LabelTitle

6.2) один элемент TextBox для ввода названия книги

  • Text — <оставить поле пустым>
  • Location — 82; 22
  • Size — 294; 20
  • Name — TextBoxTitle

6.3) один элемент Label для автора книги

  • Text — Автор:
  • Location — 393; 25
  • Size — 40; 13
  • Name — LabelAuthor

6.4) один элемент TextBox для ввода автора книги

  • Text — <оставить поле пустым>
  • Location — 439; 22
  • Size — 175; 20
  • Name — TextBoxAuthor

6.5) один элемент Label для года издания книги

  • Text — Год издания:
  • Location — 630; 25
  • Size — 73; 13
  • Name — LabelDatePublished

6.6) один элемент DateTimePicker для ввода года издания книги

  • Format — Custom
  • Location — 717; 22
  • Size — 66; 20
  • CustomFormat — yyyy
  • Name — DateTimePickerDatePublished

6.7) один элемент Button для добавления книги в таблицу

  • Text — &Добавить книгу
  • Location — 823; 20
  • Size — 147; 23
  • Name — ButtonAddBook

Дизайн для главной формы приложения готов, все необходимые элементы на ней присутствуют.

Добавление новой формы для редактирования книги

Добавьте к проекту новую форму и назовите её FrmEditBook. Для добавления новой формы в окне «Обозреватель решений» кликните на проекте DataGridViewSample правой кнопкой мыши (маркер 1 на рисунке ниже), затем выберите пункт меню «Добавить» (маркер 2 на рисунке ниже), а затем выберите пункт меню «Форма (Windows Forms)…» (маркер 3 на рисунке ниже):

В открывшемся диалоговом окне вместо имени класса по умолчанию Form1.cs введите имя нового класса для формы FrmEditBook.cs и нажмите кнопку «Добавить».

После добавления формы к проекту установите ей следующие свойства:

  • FormBorderStyle — FixedSingle
  • Text — Редактировать книгу
  • Size — 548; 342
  • StartPosition — CenterScreen
  • Name — FrmEditBook
  • MaximizeBox — False
  • MinimizeBox — False 

Далее расположите на форме следующие элементы управления и задайте им соответствующие свойства:

1) Один элемент Label (для текстового поля с названием книги):

  • Text — Название книги:
  • Location — 23; 20
  • Size — 92; 13
  • Name — LabelTitle

2) Один элемент TextBox (для отображения редактируемого названия книги):

  • Text — <оставить поле пустым>
  • Location — 26; 36
  • Size — 476; 20
  • Name — TextBoxTitle

3) Один элемент Label (для текстового поля с автором книги):

  • Text — Автор книги:
  • Location — 23; 81
  • Size — 72; 13
  • Name — LabelAuthor

4) Один элемент TextBox (для отображения редактируемого автора книги):

  • Text — <оставить поле пустым>
  • Location — 26; 97
  • Size — 476; 20
  • Name — TextBoxAuthor

5) Один элемент Label (для поля с выбором года издания книги):

  • Text — Год издания:
  • Location — 23; 144
  • Size — 73; 13
  • Name — LabelDatePublished

6) Один элемент DateTimePicker (поле с выбором года издания книги):

  • Format — Custom
  • Location — 26; 160
  • Size — 89; 20
  • CustomFormat — yyyy
  • Name — DateTimePickerDatePublished

7) Один элемент Button (кнопка «OK»):

  • Text — &OK
  • Location — 346; 236
  • Size — 75; 23
  • DialogResult — OK
  • Name — ButtonOK

8) Один элемент Button (кнопка «Отмена»):

  • Text — &Отмена
  • Location — 427; 236
  • Size — 75; 23
  • DialogResult — Cancel
  • Name — ButtonCancel

Теперь все основные элементы расположены на нашей второй форме «Редактировать книгу». Для завершения дизайна формы нам осталось выставить ещё два дополнительных свойства на форме.

Выберите форму в представлении конструктора и установите свойства из категории «Прочее»:

  • AcceptButton — ButtonOK
  • CancelButton — ButtonCancel

Должно было получиться следующее:

Теперь дизайн второй формы полностью готов. Нажмите комбинацию Ctrl+Shift+S, чтобы сохранить все изменения в проекте.

Чуть позже мы привяжем текстовые поля формы и элемент DateTimePicker, который используется для редактирования года издания книги к источнику данных, которым будет являться экземпляр класса Book.

Создаём класс Book для хранения данных о книге

Добавим в приложение новый класс с именем Book. Он будет описывать объекты книг, которые мы будем хранить в списке в памяти нашей программы и отображать этот список в элементе DataGridView, используя его в качестве источника данных для элемента DataGridView.

Для добавления нового класса в окне «Обозреватель решений» кликните на проекте DataGridViewSample правой кнопкой мыши (маркер 1 на рисунке ниже), затем выберите пункт меню «Добавить» (маркер 2 на рисунке ниже), а затем выберите пункт меню «Класс…» (маркер 3 на рисунке ниже):

В открывшемся диалоговом окне вместо Class1.cs введите имя нового класса: Book.cs и нажмите кнопку «Добавить». В результате будет открыт редактор кода со сгенерированным начальным кодом класса Book. Всё содержимое нужно будет заменить представленным ниже кодом.

Ниже полный код класса Book, который имеет свойства для хранения названия книги (Title), автора книги (Author), а также даты публикации книги (DatePublished). Мы также отдельно будем возвращать год публикации книги (YearPublished), без возможности его редактирования:

using System;

namespace DataGridViewSample {
    public class Book {
        public string Title { get; set; }
        public string Author { get; set; }
        public DateTime DatePublished { get; set; }

        public string YearPublished { get { return DatePublished.ToString("yyyy"); } }

        public Book(string title, string author, DateTime datePublished) {
            Title = title;
            Author = author;
            DatePublished = datePublished;
        }

        public override string ToString() {
            return "\tBook@" + GetHashCode() + "{\r\n" +
                "\t\tTitle: " + Title + "\r\n" +
                "\t\tAuthor: " + Author + "\r\n" +
                "\t\tDatePublished: " + DatePublished + "\r\n" +
                "\t\tYearPublished: " + YearPublished + "\r\n" +
                "\t}";
        }
    }
}

Вставьте весь представленный выше код в редактор и сохраните изменения, нажав Ctrl+S.

Привязка полей формы FrmEditBook к источнику данных

Теперь, когда класс книги Book у нас готов, мы привяжем текстовые поля формы редактирования книги (FrmEditBook) и её элемент DateTimePicker к источнику данных, которым будет являться экземпляр книги.

Чтобы сделать это, выделите в представлении конструктора формы первое текстовое поле TextBoxTitle и в окне «Свойства» для него найдите категорию «Данные», а под ним свойство DataBindings:

 

Раскройте напротив свойства Text выпадающий список и в открывшемся окне раскройте узел «Другие источники данных» и вложенный в него «Источники данных проекта». Вы увидите созданный ранее класс Book и его поля, как показано ниже на рисунке. Выберите поле Title для его привязки к свойству Text для текстового поля:

В результате вы должны увидеть следующее — напротив свойства Text появился индикатор привязки к источнику данных — bookBindingSource — Title:

Также обратите внимание на нижнюю часть под формой в представлении конструктора — можно увидеть добавленный к форме источник данных с именем bookBindingSource:

Проделайте аналогичную привязку к полям класса Book для остальных элементов формы:

Для элемента TextBoxAuthor необходимо выбрать привязку данных для свойства Text к полю Author в классе Book. Но в этот раз нужно уже выбирать поле из созданного источника bookBindingSource, поскольку теперь он есть на форме:

 

Для элемента DateTimePickerDatePublished необходимо выбрать привязку данных для свойства Value к полю DatePublished в классе Book:

Теперь оба текстовых поля формы и элемент для редактирования года издания книги привязаны к источнику данных, и мы можем приступить к программированию наших форм.

Программируем главную форму приложения (FrmDataGridViewSampleMain)

Добавление полей формы

В начало класса формы мы добавим список книг с именем books, а также флаг cancelContextMenu, который будет отвечать за то, чтобы не показывать контекстное меню для элемента DataGridView при определённых условиях (какие именно это условия мы узнаем чуть позже):

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;

namespace DataGridViewSample {
    public partial class FrmDataGridViewSampleMain : Form {
        /// <summary>
        /// список книг
        /// </summary>
        private List<Book> books = new List<Book>();

        /// <summary>
        /// флаг отмены отображения контекстного меню для элемента DataGridView
        /// </summary>
        private bool cancelContextMenu = false;

        // ... остальной код формы
    }
}

Вставьте код для этих двух полей в начало класса формы FrmDataGridViewSampleMain, как показано в листинге и сохраните изменения, нажав Ctrl+S.

Инициализация списка книг тестовыми данными

Далее нам нужно инициализировать некоторую тестовые данные с книгами, добавив их в список books. Эти данные будут затем загружаться в элемент DataGridView сразу при запуске приложения. Добавим в класс формы новый метод InitTestBooksData(), который будет инициализировать список книг и добавлять туда некоторые известные произведения классиков литературы с их авторами и годами публикации книг. Для даты публикации книги в нашем приложении будет важен только год издания, поэтому число и месяц публикации произведения мы будем принимать везде по умолчанию за 1 января того самого года, в котором была издана книга:

        private void InitTestBooksData() {
            books.Add(new Book("Война и мир", "Лев Николаевич Толстой", DateTime.Parse("1867-01-01")));
            books.Add(new Book("Анна Каренина", "Лев Николаевич Толстой", DateTime.Parse("1877-01-01")));
            books.Add(new Book("После бала", "Лев Николаевич Толстой", DateTime.Parse("1911-01-01")));
            books.Add(new Book("Юность", "Лев Николаевич Толстой", DateTime.Parse("1857-01-01")));
            books.Add(new Book("Кавказский пленник", "Лев Николаевич Толстой", DateTime.Parse("1872-01-01")));
            books.Add(new Book("Преступление и наказание", "Фёдор Михайлович Достоевский", DateTime.Parse("1866-01-01")));
            books.Add(new Book("Евгений Онегин", "Александр Сергеевич Пушкин", DateTime.Parse("1833-01-01")));
            books.Add(new Book("Капитанская дочка", "Александр Сергеевич Пушкин", DateTime.Parse("1836-01-01")));
            books.Add(new Book("Руслан и Людмила", "Александр Сергеевич Пушкин", DateTime.Parse("1820-01-01")));
            books.Add(new Book("Метель", "Александр Сергеевич Пушкин", DateTime.Parse("1831-01-01")));
            books.Add(new Book("Пиковая дама", "Александр Сергеевич Пушкин", DateTime.Parse("1834-01-01")));
            books.Add(new Book("Мать", "Максим Горький", DateTime.Parse("1906-01-01")));
            books.Add(new Book("Мёртвые души", "Николай Васильевич Гоголь", DateTime.Parse("1842-01-01")));
            books.Add(new Book("Шинель", "Николай Васильевич Гоголь", DateTime.Parse("1842-01-01")));
            books.Add(new Book("Тарас Бульба", "Николай Васильевич Гоголь", DateTime.Parse("1835-01-01")));
            books.Add(new Book("Нос", "Николай Васильевич Гоголь", DateTime.Parse("1836-01-01")));
            books.Add(new Book("Вечера на хуторе близ Диканьки", "Николай Васильевич Гоголь", DateTime.Parse("1830-01-01")));
            books.Add(new Book("Ночь перед рождеством", "Николай Васильевич Гоголь", DateTime.Parse("1832-01-01")));
            books.Add(new Book("Вий", "Николай Васильевич Гоголь", DateTime.Parse("1835-01-01")));
            books.Add(new Book("Повесть о капитане Копейкине", "Николай Васильевич Гоголь", DateTime.Parse("1842-01-01")));
            books.Add(new Book("Заколдованное место", "Николай Васильевич Гоголь", DateTime.Parse("1832-01-01")));
            books.Add(new Book("Портрет", "Николай Васильевич Гоголь", DateTime.Parse("1835-01-01")));
            books.Add(new Book("Тихий Дон", "Михаил Александрович Шолохов", DateTime.Parse("1928-01-01")));
        }
Метод для фильтрации списка книг

Теперь создадим метод GetFilteredBooks(). Он будет возвращать отфильтрованный список книг, который мы будем формировать из нашего исходного списка books, в зависимости от содержимого текстового поля с фильтром TextBoxSearch. Как только мы будем что-то вводить в поле-фильтр TextBoxSearch, то этот введённый текст будет приводиться к нижнему регистру, и мы будем просматривать все книги из списка books, пытаясь найти среди их названий, авторов и годов публикации введённый текст в поле-фильтре. Если текст будет найден, то книга будет добавлена в результирующий список result, в противном же случае она будет проигнорирована, что в дальнейшем придаст элементу DataGridView эффект динамической фильтрации при каждом вводе текста в поисковый фильтр. Код этого метода представлен ниже, добавим его в класс формы:

        private List<Book> GetFilteredBooks() {
            List<Book> result = new List<Book>();
                       
            string searchString = TextBoxSearch.Text;

            if (searchString.Length == 0) {
                return books;
            }

            searchString = searchString.ToLower();

            foreach (Book book in books) {                
                if ((book.Title.Length > 0 && book.Title.ToLower().Contains(searchString)) || 
                    (book.Author.Length > 0 && book.Author.ToLower().Contains(searchString)) ||
                    (book.YearPublished.Length > 0 && book.YearPublished.Contains(searchString))) {
                    result.Add(book);
                }
            }
            return result;
        }
Метод для обновления содержимого элемента DataGridView

Далее мы добавим в код формы метод RefreshDataGridView(), который будет обновлять данные в элементе DataGridView главной формы. В качестве источника данных (свойство DataSource элемента DataGridView) мы будем устанавливать результат вызова метода GetFilteredBooks(). При обновлении элемента DataGridView мы также установим названия его столбцов и их ширину. Ниже код этого метода:

        private void RefreshDataGridView() {
            DataGridViewBooks.DataSource = null;
            DataGridViewBooks.DataSource = GetFilteredBooks();

            DataGridViewBooks.Columns["Title"].HeaderText = "Название";
            DataGridViewBooks.Columns["Title"].Width = 200;

            DataGridViewBooks.Columns["Author"].HeaderText = "Автор";
            DataGridViewBooks.Columns["Author"].Width = 200;

            DataGridViewBooks.Columns["DatePublished"].HeaderText = "Дата издания";
            DataGridViewBooks.Columns["DatePublished"].Width = 200;
            DataGridViewBooks.Columns["DatePublished"].Visible = false;

            DataGridViewBooks.Columns["YearPublished"].HeaderText = "Год публикации";
            DataGridViewBooks.Columns["YearPublished"].Width = 126;
        }
Метод для отображения внутреннего содержимого списка книг

Теперь нам будет нужен отдельный метод для обновления текстового поля TextBoxBooks, которое используется для отображения внутреннего содержимого основного списка books. Назовём новый метод UpdateBooksListDetails() и добавим к коду главной формы:

        private void UpdateBooksListDetails() {
            StringBuilder sb = new StringBuilder();
            sb.Append("Books: [\r\n");
            foreach (Book b in books) {
                sb.Append(b.ToString());
                sb.Append("\r\n");
            }            
            sb.Append("]");

            TextBoxBooks.Text = sb.ToString();
        }

Как видим, он просто пробегает циклом foreach по списку книг и добавляет текстовое представление очередной книги к строковому буферу (переменная sb). В конце метода содержимое строкового буфера sb мы записываем в свойство Text текстового поля TextBoxBooks

Метод для обновления всех данных (список книг в элементе DataGridView и внутреннее содержимое списка книг)

Создадим ещё один небольшой метод RefreshGridAndBookDetails(), который будет вызывать два уже ранее добавленных метода. Этот метод будет сразу обновлять и сам элемент DataGridView, и текстовое поле с содержимым списка books:

        private void RefreshGridAndBookDetails() {
            RefreshDataGridView();
            UpdateBooksListDetails();
        }
Метод для управления доступностью кнопки «Добавить книгу»

Добавим метод UpdateButtonAddBookState(), который будет регулировать доступность кнопки «Добавить книгу» . Кнопка должна быть доступна только тогда, когда оба текстовых поля с названием и автором книги содержат текст:

        private void UpdateButtonAddBookState() {
            ButtonAddBook.Enabled = TextBoxTitle.Text.Length > 0 && TextBoxAuthor.Text.Length > 0;
        }
Пишем логику при загрузке главной формы 

Нам нужно добавить код, который будет выполняться сразу при загрузке формы. Для этого нужно сгенерировать в коде формы обработчик события Load. Это можно сделать перейдя к представлению конструктора формы и дважды кликнув по форме — будет добавлен пустой метод FrmDataGridViewSampleMain_Load. Теперь мы добавим в него следующий код с вызовом трёх методов InitTestBooksData(), RefreshGridAndBookDetails() и UpdateButtonAddBookState():

        private void FrmDataGridViewSampleMain_Load(object sender, EventArgs e) {
            InitTestBooksData();
            RefreshGridAndBookDetails();
            UpdateButtonAddBookState();
        }

Как видим, при загрузке программы и главной формы будут производиться следующие действия:

  1. список с книгами books будет наполнен тестовыми данными — это достигается вызовом метода InitTestBooksData()
  2. будет обновлено содержимое элемента DataGridView и текстового поля TextBoxBooks — это достигается вызовом метода RefreshGridAndBookDetails()
  3. будет установлена доступность кнопки «Добавить книгу» — это достигается вызовом метода UpdateButtonAddBookState()

Теперь вернёмся в представление конструктора формы и дважды кликнем по кнопке «Добавить книгу», чтобы сгенерировался метод-обработчик для события Click (т.е. события клика по кнопке). Добавим в него следующий код:

        private void ButtonAddBook_Click(object sender, EventArgs e) {
            DateTime selectedDate = DateTimePickerDatePublished.Value;
            DateTime truncatedDate = DateTime.Parse(selectedDate.ToString("dd.MM.yyyy"));

            Book newBook = new Book(TextBoxTitle.Text, TextBoxAuthor.Text, truncatedDate);
            books.Add(newBook);
            RefreshGridAndBookDetails();

            TextBoxTitle.Text = "";
            TextBoxAuthor.Text = "";
            DateTimePickerDatePublished.Value = DateTime.Now;

            TextBoxTitle.Focus();
        }

В переменную selectedDate мы получаем значение даты и времени, выбранное в элементе DateTimePickerDatePublished. Это значение содержит выбранную дату вместе с текущим временем. Время нам не нужно, поэтому мы записываем в переменную truncatedDate значение, которое содержит только дату. Далее остаётся только создать новый экземпляр книги newBook со значениями названия, автора и даты публикации и добавить его в основной список books, после чего обновить данные в элементе DataGridView и текстовом поле с содержимым всего списка книг. Напоследок мы также очищаем все поля в нижней части формы и устанавливаем дату в текущую, а также явно устанавливаем фокус на текстовое поле с названием книги — это необходимо для удобства последовательного ввода нескольких книг.

Вернёмся к представлению конструктора главной формы. Нам нужно сгенерировать в коде главной формы обработчики следующих событий для элемента DataGridView:

  • CellEndEdit — это событие возникает при завершении редактирования содержимого ячейки
  • DataError — это событие возникает при появлении ошибки при редактировании ячейки. В нашем случае оно будет вызываться при попытке установить некорректное значение года публикации книги (к примеру, если попытаемся ввести некорректную дату)
  • MouseDown — событие возникает при клике мышью на элементе DataGridView. Его мы будем обрабатывать в случае нажатия правкой кнопки мыши в целях полного выделения строки, над которой произошёл клик, а также установки специального флага скрытия контекстного меню

Для этого выбираем на форме элемент DataGridView, а затем в окне «Свойства» кликаем на иконку «молнии». В списке событий находим сначала CellEndEdit и дважды кликаем напротив имени этого события, в результате чего в коде формы будет сгенерирован пустой метод-обработчик DataGridViewBooks_CellEndEdit. Аналогичное действие проделываем, дважды кликая напротив событий DataError и MouseDown.

Теперь пора написать код для этих сгенерированных методов-обработчиков.

Начнём с первого — DataGridViewBooks_CellEndEdit. Внутри него мы напишем код, который из входного параметра e с типом DataGridViewCellEventArgs получит индексы строки и столбца в переменные row и col, соответственно. После этого в переменную cell мы получим ссылку на ячейку элемента DataGridView, которая и была отредактирована. Далее нам нужно получить содержимое ячейки с помощью свойства Value — его мы устанавливаем в переменную cellValue с типом object (на этом этапе мы не знаем информацию о точном типе данных для значения отредактированной ячейки). Теперь мы можем получить ссылку на редактируемую книгу в переменную book — индекс отредактированной книги в списке books совпадает с индексом строки в элементе DataGridView. А чтобы обновить конкретное свойство экземпляра книги мы смотрим на индекс столбца col и приводим cellValue уже к конкретному типу данных и устанавливаем в конкретное свойство для экземпляра книги. После этого мы можем вызывать метод UpdateBooksListDetails() для обновления содержимого текстового поля TextBoxBooks:

        private void DataGridViewBooks_CellEndEdit(object sender, DataGridViewCellEventArgs e) {
            int row = e.RowIndex;
            int col = e.ColumnIndex;

            DataGridViewCell cell = DataGridViewBooks[col, row];

            object cellValue = cell.Value;

            Book book = books[row];
            switch (col) {
                case 0:
                    book.Title = (string)cellValue;
                    break;
                case 1:
                    book.Author = (string)cellValue;
                    break;
                case 2:                    
                    book.DatePublished = (DateTime)cellValue;                    
                    break;
            }

            UpdateBooksListDetails();
        }

Теперь перейдем к методу-обработчику DataGridViewBooks_DataError и напишем код для него.

Аналогичным образом из параметра e можно извлечь индексы столбца и строки в переменные col и row. Далее нас будет интересовать свойство EditedFormattedValue для ячейки по этим индексам — его мы получим в переменную editedValue. В конце метода мы проверим, что если тип исключения — это FormatException и при этом индекс столбца равен 2 (т.е. мы редактируем какую-то ячейку в столбце «Год публикации»), то мы выводим сообщение о необходимости ввода корректной даты, а также отменяем событие, устанавливая свойство Cancel в true. Ниже код этого метода:

        private void DataGridViewBooks_DataError(object sender, DataGridViewDataErrorEventArgs e) {
            int col = e.ColumnIndex;
            int row = e.RowIndex;
            
            object editedValue = DataGridViewBooks[col, row].EditedFormattedValue;

            if (e.Exception is FormatException fe && col == 2) {
                MessageBox.Show("Введите корректную дату, т.к. значение '" + editedValue.ToString() + "' не является датой", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
                e.Cancel = true;
            }
        }

Перейдем к последнему методу-обработчику для элемента DataGridView — DataGridViewBooks_MouseDown и напишем код для него. Прежде всего мы должны проверить, что клик по таблице с книгами произведён именно правой кнопкой мыши (MouseButtons.Right). Далее мы используем специальный метод HitTest, доступный для элемента DataGridView — он поможет вернуть специальную информацию о координатах клика в переменную hitTestInfo, в частности, мы сможем получить доступ к индексу строки и столбца, по которым произошёл клик мышью. Мы проверим, что если эти индексы — неотрицательные, то это значит, что текущее выделение для элемента DataGridView необходимо очистить и выделить только ту строку, над которой находился курсор мыши в момент клика. В этом случае мы также сбросим флаг cancelContextMenu в false — это означает, что контекстное меню показать надо. В противном же случае — если мы кликнули где-то по заголовкам или служебным областям элемента DataGridView — индексы будут отрицательными и в развилке else мы выставим флаг cancelContextMenu в true:

        private void DataGridViewBooks_MouseDown(object sender, MouseEventArgs e) {
            if (e.Button == MouseButtons.Right) {
                var hitTestInfo = DataGridViewBooks.HitTest(e.X, e.Y);
                if (hitTestInfo.RowIndex >= 0 && hitTestInfo.ColumnIndex >= 0) {
                    DataGridViewBooks.ClearSelection();
                    DataGridViewBooks.Rows[hitTestInfo.RowIndex].Selected = true;
                    cancelContextMenu = false;
                } else {                    
                    cancelContextMenu = true;
                }
            }
        }

Раз уж мы заговорили про специальный флаг cancelContextMenu — пора им воспользоваться и написать логику, которая будет отменять показ контекстного меню. Для этого нужно вернуться на главную форму в представление конструктора, выбрать контекстное меню ContextMenuStripForGrid и сгенерировать для него обработчик события Opening. Это событие возникает в момент открытия контекстного меню, при этом его можно отменить, тогда контекстное меню не будет отображено. В коде мы как раз проверим наш специальный флаг необходимости отмены показа контекстного меню и отменим само событие, если флаг установлен:

        private void ContextMenuStripForGrid_Opening(object sender, System.ComponentModel.CancelEventArgs e) {
            if (cancelContextMenu) {
                e.Cancel = true;
            }
        }
Обработка событий контекстного меню

Теперь мы напишем код, который будет обрабатывать события клика на элементах контекстного меню ContextMenuStripForGrid. Для этого нужно перейти к представлению конструктора формы, выбрать в нижней части элемент ContextMenuStripForGrid и дважды кликнуть по пункту меню «&Удалить выбранную книгу». В результате будет сгенерирован обработчик клика на этом пункте меню с названием MenuItemRemoveBook_Click. Затем нужно повторить то же самое для пункта меню «&Редактировать выбранную книгу» и сгенерировать обработчик MenuItemEditBook_Click.

Теперь напишем для них код:

        private void MenuItemRemoveBook_Click(object sender, EventArgs e) {
            DataGridViewSelectedRowCollection selectedRows = DataGridViewBooks.SelectedRows;
            foreach (DataGridViewRow selectedRow in selectedRows) {
                int rowIndex = selectedRow.Index;

                if (rowIndex < 0) {
                    continue;
                }
                
                Book book = books[rowIndex];
                
                DialogResult dlgConfirm = MessageBox.Show("Удалить эту книгу?\r\n\r\nАвтор: " + book.Author + "\r\nНазвание: " + book.Title + "\r\nГод публикации: " + book.YearPublished, "Подтвердите", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
                if (dlgConfirm == DialogResult.Yes) {
                    books.RemoveAt(rowIndex);
                }
            }

            RefreshGridAndBookDetails();
        }

        private void MenuItemEditBook_Click(object sender, EventArgs e) {
            DataGridViewSelectedRowCollection selectedRows = DataGridViewBooks.SelectedRows;

            foreach (DataGridViewRow selectedRow in selectedRows) {
                int rowIndex = selectedRow.Index;

                if (rowIndex < 0) {
                    continue;
                }

                Book book = books[rowIndex];

                FrmEditBook frmEdit = new FrmEditBook();
                frmEdit.EditedBook = book;
                frmEdit.BookUpdatedEvent += FrmEdit_BookUpdatedEvent;
                frmEdit.Show();
            }
        }

Как видим, в обработчике MenuItemRemoveBook_Click происходит получение выбранных строк таблицы в коллекцию selectedRows, а затем цикл по этой коллекции. На самом деле выбранная строка всегда будет одна, но для надёжности мы делаем цикл и в цикле проверяем, что индекс выбранной строки неотрицательный. В этом случае мы получаем выбранную книгу из списка books (её индекс в списке равен индексу выбранной строки таблицы) и выдаём диалоговое окно с подтверждением необходимости удаления книги. Если в окне диалога удаление подтверждено, то книга удаляется из списка.

Во втором обработчике MenuItemEditBook_Click у нас немного другая задача. Мы должны отобразить вторую форму для редактирования выбранной в таблице записи. Легко заметить, что логика с получением выбранных строк таблицы и проверки в цикле индекса аналогична первому обработчику, только в этот раз мы вместо удаления книги создаём экземпляр формы FrmEditBook, устанавливаем её свойство EditedBook текущей выбранной книге, а также подписываемся на событие BookUpdatedEvent, после чего отображаем форму.

Ниже код нового метода FrmEdit_BookUpdatedEvent, который надо добавить в код главной формы. Всё, что он делает, — это вызывает метод обновления данных на форме:

        private void FrmEdit_BookUpdatedEvent(Book updatedBook) {
            RefreshGridAndBookDetails();
        }

Последнее, что нам осталось добавить в код главной формы — это реакция на изменения текстовых полей. У нас их на форме три:

  • TextBoxTitle — для ввода названия книги (при добавлении новой)
  • TextBoxAuthor — для ввода автора книги (при добавлении новой)
  • TextBoxSearch — поисковое поле-фильтр для быстрого поиска по списку книг

Кликните дважды по каждому из них из представления конструктора формы — должны сгенерироваться пустые методы-обработчики для события TextChanged, которое возникает при изменении текста в текстовом поле.

Наполним эти пустые методы следующим образом:

        private void TextBoxTitle_TextChanged(object sender, EventArgs e) {
            UpdateButtonAddBookState();
        }

        private void TextBoxAuthor_TextChanged(object sender, EventArgs e) {
            UpdateButtonAddBookState();
        }

        private void TextBoxSearch_TextChanged(object sender, EventArgs e) {
            RefreshDataGridView();
        }

При изменении названия или автора книги будет вызываться UpdateButtonAddBookState() — это необходимо, чтобы делать кнопку «Добавить книгу» доступной при наличии всех данных по книге и наоборот — делать её недоступной, если хотя бы одно из полей не заполнено. А вот при изменении поля-фильтра мы будем вызывать RefreshDataGridView(), который «перерисует» элемент DataGridView — в зависимости от текста в поле-фильтре.

На этом код главной формы полностью готов.

Программируем форму редактирования книги (FrmEditBook)

Теперь, когда код для главной формы полностью готов, перейдем к написанию кода для второй формы, которая будет отображаться при редактировании выбранной в списке книги.

Сначала дважды кликнем по форме из представления конструктора — это сгенерирует пустой метод-обработчик FrmEditBook_Load для события Load формы.

Затем возвращаемся снова к конструктору формы и дважды кликаем по кнопке ButtonCancel — в результате будет сгенерирован пустой метод-обработчик ButtonCancel_Click, который будет отвечать за обработку события клика по кнопке «&Отмена».

Аналогичное действие делаем для второй кнопки ButtonOK — из представления конструктора формы дважды кликаем по ней, чтобы сгенерировать пустой метод-обработчик ButtonOK_Click.

Далее необходимо наполнить пустые сгенерированные методы представленным ниже кодом или просто выделить весь фрагмент и заменить им код в редакторе:

using System;
using System.Windows.Forms;

namespace DataGridViewSample {
    public partial class FrmEditBook : Form {
        /// <summary>
        /// делегат, который описывает контракт для события обновления данных для книги
        /// </summary>
        /// <param name="updatedBook">отредактированная книга</param>
        public delegate void DelegateUpdateBook(Book updatedBook);

        /// <summary>
        /// событие обновления данных для книги
        /// </summary>
        public event DelegateUpdateBook BookUpdatedEvent;

        /// <summary>
        /// свойство содержит ссылку на редактируемую книгу
        /// </summary>
        public Book EditedBook { get; set; }

        public FrmEditBook() {
            InitializeComponent();
        }

        private void FrmEditBook_Load(object sender, EventArgs e) {
            // Помещаем в источник данных экземпляр для редактируемой книги
            bookBindingSource.Add(EditedBook);
        }

        private void ButtonCancel_Click(object sender, EventArgs e) {
            Close();
        }

        private void ButtonOK_Click(object sender, EventArgs e) {
            // Получаем текущую обновлённую книгу из свойства Current
            // для источника данных bookBindingSource
            Book bookUpdated = (Book)bookBindingSource.Current;

            // Если событие BookUpdatedEvent не равно null, это значит, что
            // на него есть подписчики. Следовательно, вызовем это событие, передавая 
            // в него экземпляр обновлённой книги
            if (BookUpdatedEvent != null) {
                BookUpdatedEvent(bookUpdated);
            }
            Close();
        }
    }
}

Код формы для редактирования книги снабжён комментариями, к тому же он совсем несложный, поэтому разбирать его отдельно не будем.

Наше приложение полностью готово, можем запустить его из среды разработки при помощи клавиши F5 или нажав на кнопку «Пуск».

Заключение

Попробуйте поработать со списком книг — отфильтровать его содержимое, вводя различный текст в поисковое поле-фильтр. Также можно редактировать отдельные записи прямо в таблице — либо при помощи двойного клика по ячейкам, либо нажимая клавишу F2 для входа в режим редактирования ячейки. Обратите внимание, что ячейки таблицы с годом издания книги невозможно отредактировать. Год издания можно поменять только через вторую форму «Редактировать книгу», которая открывается из контекстного меню при клике правой кнопкой мыши по строкам таблицы.

Также попробуйте удалить пару книг из таблицы и добавить пару своих собственных тестовых книг, используя нижнюю часть формы. В этой тестовой программе мы не сохраняем данные о книгах и не загружаем их из внешнего файла или базы данных, поэтому при выходе из нашей программы состояние книг будет каждый раз сбрасываться. При необходимости можно доработать программу, чтобы она сохраняла книги куда-то в файл, а также загружала их из файла при следующем запуске, однако это выходит за рамки статьи, т.к. основной задачей было показать работу с элементом DataGridView.

Друзья, на этом всё, надеюсь, что урок вам понравился, при наличии вопросов/пожеланий — используйте форму комментариев внизу статьи.

Ссылка на готовый проект из этой статьи:

https://allineed.ru/our-products/download/4-allineed-ru-examples/15-csharp-demo-working-with-datagridview

Удачи и успехов в написании хороших программ!

C#: Табличные компоненты Windows Forms и работа с ними

Табличное представление данных повсеместно используется в приложениях. В этой лекции рассмотрены основные приёмы работы со следующими компонентами:

  • DataGridView – табличный редактор для отображения данных из файла XML или из БД (доступен в группе «Данные» Панели Элементов);
  • DataGrid – базовая табличная компонента для отображения связанных таблиц (щёлкнуть правой кнопкой мыши в Панели Элементов, команда «Выбрать элементы», дождаться загрузки списка, на вкладке «Компоненты .NET Framework» включить DataGrid из пространства имён System.Windows.Forms. После этого DataGrid можно добавить на форму).

Проект Lab4_1. Напишем простейший проект для редактирования таблицы и сохранения её в формате XML.

На форму добавим dataGridView1, установив ему свойство Dock = Fill, а объекты DataTable и DataSet создадим программно.

Для этого опишем глобально в классе формы следующие величины:

  private String BaseName;
  private DataTable Table;
  private DataSet Set;

На загрузку формы реализуем такой код (обработчик события Load):

   BaseName = "table.xml";
   Table = new DataTable ();
   Set = new DataSet ();
   if (System.IO.File.Exists (BaseName) == false) {
    //Если файл не существует - создать таблицу и DataSet
    dataGridView1.DataSource = Table;
    Table.Columns.Add ("Имена");
    Table.Columns.Add ("Номера телефонов");
    Set.Tables.Add (Table);
   }
   else {
    //Если файл существует - загрузить и показать данные
    Set.ReadXml (BaseName);
    String StringXML = Set.GetXml ();
    dataGridView1.DataMember = "Название таблицы";
    dataGridView1.DataSource = Set;
   }

Перед закрытием формы выполним следующий код (обработчик события FormClosing):

   Table.TableName = "Название таблицы";
   Set.WriteXml (BaseName);

Данные сохраняются в формате XML, после выполнения приложения найдите файл данных в папке с исполняемым файлом проекта.

Объект DataSet представляет собой кэш данных, расположенный в оперативной памяти. DataSet состоит из коллекции объектов класса DataTable.

Доступ к ячейкам таблицы можно получить, используя свойства класса DataTable (Rows, Cols, Item) — но «прямая» запись поля таблицы в файл может быть некорректной из-за того, что технология ADO.NET предусматривает кэширование данных (особенно если данные сохраняются посредством SQL-транзакций).
Пример такого кода:

   System.Data.DataRow newRow = Table.NewRow ();
   Table.Rows.Add (newRow);

Поэтому следует пользоваться методами объекта DataSet.

 Скачать пример Lab4_1 в архиве .zip с проектом C# Visual Studio 2019 (11 Кб)

Проект Lab4_2. Напишем простой проект для редактирования связанных отношением «один ко многим» таблиц.

Компонента DataGrid — решение для показа связанных таблиц в одной компоненте, в DataGridView такой возможности нет. Разместим компонент на форме, можно установить свойство Dock = Fill. Также предусмотрим пункты или кнопки меню «Переключить вид», «Сохранить», «Загрузить».

Эти данные описаны глобально в классе формы:

  private Boolean ShowClients; //Флажок-переключатель таблиц
  private System.Data.DataSet dataSet1; //Кэш данных
  private System.Data.DataTable Table, Table2; //Родительская и дочерняя таблицы

На загрузку формы (в обработчике её события Load) будем выполнять следующий код:

   ShowClients = true;
   if (System.IO.File.Exists ("data.xml") == true) {
    //Если файл существует - загрузить и показать данные
    загрузитьToolStripMenuItem_Click (this, e); //Обработчик команды "Загрузить"!
   }
   else { //Иначе создать предустановленные данные
    //Создадим таблицу:
    Table = new DataTable ("Клиенты");
    //Создадим и настроим столбец программно:
    DataColumn Column = new DataColumn ("Название организации");
    Column.ReadOnly = true;
    Column.Unique = true;
    Table.Columns.Add (Column);
    //Добавим столбцы с настройками по умолчанию, указав только названия:
    Table.Columns.Add ("Контактное лицо");
    Table.Columns.Add ("Телефон");
    //Создадим DataSet и добавим туда таблицу:
    dataSet1 = new DataSet ();
    dataSet1.Tables.Add (Table);
    //Добавим в таблицу предустановленные записи об организациях-заказчиках
    Table.Rows.Add ("НГАСУ", "Иванов Максим", "3234566");
    Table.Rows.Add ("НГТУ", "Сидорова Ксения", "3630313");
    //Создадим вторую таблицу - "Заказы"
    Table2 = new DataTable ("Заказы");
    DataColumn Column2 = new DataColumn ("Номер заказа");
    Column2.DataType = System.Type.GetType ("System.Int32");
    Column2.AutoIncrement = true; //Автоматический счётчик заказов
    Column2.ReadOnly = true; Column2.Unique = true; //Название организации - уникально!
    Table2.Columns.Add (Column2);
    Table2.Columns.Add ("Объем заказа");
    Table2.Columns.Add ("Организация-заказчик");
    //Добавим в DataSet вторую таблицу:
    dataSet1.Tables.Add (Table2);
    Table2.Rows.Add (1, "100000", "НГАСУ");
    Table2.Rows.Add (2, "200000", "НГАСУ");
    //Обеспечим отношение 1:N между первой и второй таблицами:
    DataColumn Parent = dataSet1.Tables ["Клиенты"].Columns ["Название организации"];
    DataColumn Child = dataSet1.Tables ["Заказы"].Columns ["Организация-заказчик"];
    DataRelation Link1 = new DataRelation ("Ссылка на заказы клиента", Parent, Child);
    // В Parent значения в связываемом столбце должны быть уникальными, в Child - нет
    dataSet1.Tables ["Заказы"].ParentRelations.Add (Link1);
   }
   dataGrid1.SetDataBinding (dataSet1, "Клиенты");
   dataGrid1.CaptionText = "Родительская таблица \"Клиенты\"";
   dataGrid1.CaptionFont = new System.Drawing.Font ("Consolas", 11);

На нажатие кнопки или выбор пункта меню «Переключить вид» будем переключаться между родительской и дочерней таблицами:

   if (ShowClients == true) {
    dataGrid1.SetDataBinding (dataSet1, "Клиенты");
    dataGrid1.CaptionText = "Родительская таблица \"Клиенты\"";
   }
   else {
    dataGrid1.SetDataBinding (dataSet1, "Заказы");
    dataGrid1.CaptionText = "Дочерняя таблица \"Заказы\"";
   }
   dataGrid1.Collapse (-1); //Свернуть все ветви
   ShowClients = !ShowClients;

На выбор команды «Сохранить» будем сохранять все данные в файле типа .xml текущей папки:

   dataSet1.WriteXml ("data.xml", XmlWriteMode.WriteSchema);

На выбор команды «Загрузить» будем обновлять все данные из файла, сбросив несохранённые изменения, если таковые есть:

   dataSet1 = new DataSet ();
   dataSet1.ReadXml ("data.xml");
   ShowClients = true;
   переключитьВидToolStripMenuItem_Click (this, e);

Приложение запускается и редактирует связанные таблицы.

 Скачать пример Lab4_2 в архиве .zip с проектом C# Visual Studio 2019 (12 Кб)

Проект Lab4_3. Реализуем больше возможностей компоненты DataGridView. Форма приложения будет такой же, как в проекте 4.1, а действия можно запрограммировать как реакцию на выбор пунктов верхнего меню.

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

  • Rows — это коллекция строк, имеет тип DataGridRowCollection.
  • Columns — это коллекция столбцов типа DataGridColumnCollection.
    Оба свойства индексируются как массивы для доступа к конкретной строке/столбцу, нумерация производится с нуля.
  • Cells — это коллекция ячеек из объекта DataGridRowCollection, приведём пример доступа к конкретной ячейке:
    try {
        MessageBox.Show (dataGridView1.Rows [1].Cells [1].Value.ToString ());
       }
       catch (Exception) { MessageBox.Show ("Нет такой ячейки"); }
  • RowCount, ColumnCount — количество строк и столбцов.

В несвязанном режиме компонента может отображать любые табличные данные.

Методы для добавления/удаления/редактирования строк и столбцов относятся к коллекциям Rows и Columns и имеют типовые имена: Add, Insert, Clear, AddCopy, InsertCopy, Remove, RemoveAt, а также могут иметь по несколько перегрузок каждая, например, для метода Add добавления строки есть версии Add(), Add(int count), Add (DataGridViewRow row), Add (object []values).

  private static int Cnt; //Счётчик столбцов в классе формы
  //...	

   if (dataGridView1.ColumnCount < 1) { //Сначала нужно создать столбец
    dataGridView1.Columns.Add ("Столбец " + Cnt, "Заголовок " + Cnt);
    Cnt++;
   }
   dataGridView1.Rows.Add ();

Настройка внешнего вида компонента также типовая: такие свойства, как BackColor, Alignment, Font и т.д. находятся в объекте типа DataGridViewCellStyle.

Каждая ячейка представлена объектом System.Windows.Forms.DataViewCell, за «личный» внешний вид ячейки отвечает свойство InheritedStyle, а за вид по умолчанию — DefaultCellStyle.

Очередной командой перекрасим фон таблицы в розовый цвет:

   dataGridView1.DefaultCellStyle.BackColor = Color.Pink;

А теперь поменяем фон только выбранной ячейки:

   if (cell_y > -1 && cell_x > -1)
    dataGridView1.Rows[cell_y].Cells[cell_x].Style.BackColor = Color.Green;

Предполагается, что значения cell_y, cell_x описаны глобально в классе формы:

   private int cell_y, cell_x;

и инициализируются в обработчике её события Load:

   cell_y = cell_x = -1;

а затем получают значения в обработчиках событий KeyUp и MouseUp компоненты dataGridView1 (одинаковым кодом):

   cell_y = dataGridView1.CurrentCell.RowIndex;
   cell_x = dataGridView1.CurrentCell.ColumnIndex;

Когда требуется форматирование содержимого ячейки DataGridView для отображения, возникает событие CellFormatting, вот пример его обработчика:

   e.CellStyle.SelectionBackColor = Color.Yellow;
   e.CellStyle.SelectionForeColor = Color.Black;

Сделаем в dataGridView1 таблицу со значениями функции. Вот код соответствующей команды:

   dataGridView1.Columns.Clear ();
   dataGridView1.ColumnCount = 2;
   dataGridView1.Rows.Add (10); //Добавили 10 строк
   dataGridView1.Columns [0].Name = "X";
   dataGridView1.Columns [1].Name = "Y(X)";
   double x; int i;
   for (x = 1.5, i = 0; i < 10; x += 0.1, i++) {
    dataGridView1.Rows [i].Cells [0].Value = Convert.ToString (x);
    dataGridView1.Rows [i].Cells [1].Value = Math.Round (x * x, 2).ToString ();
    //или dataGridView1.Rows[i].Cells[1].Value = (x*x).ToString("f");
   }

Существует также множество событий, связанных с редактированием ячейки: CellBeginEdit, CellEndEdit, CellParsing, CellValidating, CellValidated и т.д.

Например, по умолчанию наша таблица редактируется. Чтобы разрешить в первом столбце (Y(X)) ввод только числовых значений, напишем следующий код, выполняемый по событию CellValueChanged компоненты DataGridView:

   String Val =
    dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex].Value.ToString ();
   if (e.ColumnIndex == 1) {
    double val;
    bool A = Double.TryParse (Val,
     System.Globalization.NumberStyles.Number,
     System.Globalization.NumberFormatInfo.CurrentInfo, out val);
    if (A == false) {
     dataGridView1.Rows[e.RowIndex].Cells [e.ColumnIndex].Value = lastValue;
     MessageBox.Show ("Неверное число: " + Val, "Ошибка");
    }
   }

Здесь предполагается, что величина lastValue описана в классе формы:

  private double lastValue;

и по событию CellBeginEdit, сохраняет предыдущее значение, хранимое в ячейке:

   if (e.ColumnIndex == 1) lastValue =
    Convert.ToDouble (dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex].Value);

Запретить редактирование данных можно стандартно – с помощью свойства ReadOnly. Чтобы запретить редактирование конкретной ячейки, строки или столбца, также воспользуйтесь этим свойством:

   if (dataGridView1.ColumnCount > 0) {
    dataGridView1.Rows [0].Cells [0].ReadOnly = true;
    dataGridView1.Columns [0].ReadOnly = true;
   }

 Скачать пример Lab4_3 в архиве .zip с проектом C# Visual Studio 2019 (12 Кб)

Проект Lab4_4. Пример выполнения варианта задания. Написать табличный редактор ведомости студенческой группы со столбцами: Фамилия, 1, 2, …, 17 (номера недель), итого (отметка о зачете)

Данные автоматически загружаются из файла и сохраняются в файле формата xml.

Поддерживаются редактирование, в том числе, произвольное добавление и удаление строк, проверка правильности ввода данных.

Первый вариант решения — создать таблицу dataGridView, не связанную с DataSet и работать непосредственно с ячейками через методы класса dataGridView. Сложности такого подхода – придётся «вручную» писать поддержку сохранения и загрузки таблицы.
Второй вариант – связать таблицу с DataSet, чтобы легко загружать и сохранять данные XML, но тогда работа с добавлением/удалением ячеек делается через методы кэша DataSet, иначе компилятор и не разрешит выполнять этого.

Создав новый проект Windows Forms с главной формой Form1, добавим на неё компоненту DataGridView и растянем на всю форму (свойство Dock = Fill). Также добавим к проекту контекстное меню contextMenuStrip с пунктами «Добавить», «Удалить», «Вычислить» и укажем его в свойстве ContextMenuStrip компоненты dataGridView1.

Пропишем в классе формы глобальные величины:

  private String BaseName;
  private DataTable  Table;
  private DataSet Set;
  private String Val; //предыдущее значение из текущей ячейки
  private int Column; //текущий столбец
  private bool Changed; //признак изменения текущей ячейки

Столбцы таблицы (фамилия, 17 граф для оценок или иных отметок, графа «зачёт») создадим программно по событию Load формы:

   BaseName = "table.xml"; 
   Table = new DataTable (); 
   Set = new DataSet ();
   Table.Columns.Add ("ФИО");
   for (int i = 1; i <= 17; i++) Table.Columns.Add ("" + i);
   Table.Columns.Add ("Итого");
   Set.Tables.Add (Table); 
   dataGridView1.DataSource = Set;
   Table.TableName = "Успеваемость";
   if (System.IO.File.Exists (BaseName) == true) {
    Set.ReadXml (BaseName);
   }
   dataGridView1.DataMember = "Успеваемость";
   dataGridView1.Columns [0].Width = 100;
   for (int i = 1; i <= 17; i++) dataGridView1.Columns [i].Width = 25;

«Подогнать» ширину столбцов под ширину формы можно, например, в обработчике события SizeChanged формы (при старте приложения ширина «подогнана» не будет):

   Rectangle Rect = this.ClientRectangle;
   int w = Rect.Width; //клиентская ширина формы
   if (w < 400) w = Rect.Width = 400;
   int border = dataGridView1.Columns [0].DividerWidth,
    left = dataGridView1.Rows [0].HeaderCell.Size.Width;
   //ширина разделителя столбцов и закрепленного столбца слева
   int w1 = 100,
       w2 = (int) Math.Floor (( w - 2 * w1 - 19 * border - left ) / 17.0);
   //под 1-й и последний столбец по 100 пикселей, остальное место делим поровну
   dataGridView1.Columns [0].Width = dataGridView1.Columns [18].Width = w1;
   for (int i = 1; i <= 17; i++) dataGridView1.Columns [i].Width = w2;

Также будем автоматически сохранять данные при выходе из программы (событие формы FormClosing):

   Table.TableName = "Успеваемость";
   Set.WriteXml (BaseName);

По выбору пункта меню «Добавить» выполняется следующее:

   DataRow newR = Set.Tables ["Успеваемость"].NewRow ();
   newR [0] = "Студент";
   try {
    int i = dataGridView1.CurrentCell.RowIndex;
    Set.Tables ["Успеваемость"].Rows.InsertAt (newR, i);
    Set.Tables ["Успеваемость"].AcceptChanges ();
   }
   catch (Exception) { }

А пункт «Удалить» проще всего запрограммировать так:

   try {
    if (dataGridView1.CurrentCell == null) return;
    int i = dataGridView1.CurrentCell.RowIndex;
    Set.Tables ["Успеваемость"].Rows [i].Delete ();
    Set.Tables ["Успеваемость"].AcceptChanges ();
   }
   catch (Exception) { }

Применение метода AcceptChanges нужно, чтобы изменение данных немедленно отобразилось в таблице.

Контроль правильности ввода (не более 1 символа в графы отметок, не более 20 символов в графы «ФИО» и «Итого») сделаем следующими обработчиками событий компоненты dataGridView1:

CellBeginEdit(на начало редактирования):

   Column = e.ColumnIndex; //запоминаем  столбец
   Val = dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex].Value.ToString ();
    //что было в ячейке
   Changed = false; //ячейка не изменена

CellValueChanged (по изменению ячейки):

   String newVal =
    dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex].Value.ToString ();
   if (e.ColumnIndex > 0 && e.ColumnIndex < 18) {
    if (newVal.Length > 1) newVal = Val;
   }
   else if (newVal.Length > 20) newVal = newVal.Substring(0,20);
   dataGridView1.Rows[e.RowIndex].Cells [e.ColumnIndex].Value = newVal;

EditingControlShowing (для установки своих обработчиков в дополнение к стандартным обработчиками событий компоненты):

   if (Column > 0 && Column < 18) {
    TextBox tb = (TextBox)e.Control;
    tb.MaxLength = 1;
    tb.KeyPress += new KeyPressEventHandler (tb_KeyPress);
   }

В проект добавлен метод tb_KeyPress – дополнение к обработке KeyPress, разрешающее вводить буквы, цифры, Backspace и пробел:

  void tb_KeyPress (object sender, KeyPressEventArgs e) {
   char c = e.KeyChar;
   if (!( Char.IsLetterOrDigit (c) || c == (char) Keys.Back || 
     c == (char) Keys.Space )) e.Handled = true;
   //а вот обработчик KeyDown так не сделать
  }

CellLeave (покидая ячейку, изменим правила перехода к следующей ячейке, по умолчанию это вниз, а мы хотим вправо):

   //конечно, "костыль", по идее, надо писать класс-наследник 
   //DataGridView и там управлять клавиатурой
   if (!Changed) {
    Changed = true;
    int c = dataGridView1.CurrentCell.ColumnIndex;
    if (c == dataGridView1.Columns.Count - 1) {
     SendKeys.Send ("{Home}");
    }
    else {
     SendKeys.Send ("{Up}");
     SendKeys.Send ("{Right}");
    }
   }

Проблема состоит в том, что DataGridView обрабатывает многие клавиши своими собственными событиями и «не пускает» коды до уровня KeyPress или KeyDown (KeyUp выполняется).

Как пример расчёта добавим вычисление среднего балла по выставленным отметкам, код можно выполнить по соответствующему пункту меню:

   for (int i = 0; i < dataGridView1.RowCount - 1; i++) {
    int sum = 0, cnt = 0, val = 0;
    for (int j = 1; j < dataGridView1.ColumnCount - 1; j++) {
     String str = dataGridView1.Rows [i].Cells [j].Value.ToString ().Trim();
     try {
      if (str.Length > 0) val = Int32.Parse (str);
      else continue;
     }
     catch (Exception) {
      continue;
     }
     sum += val;
     cnt++;
    }
    if (cnt > 0) {
     double avg = (sum + 0.0) / cnt; //чтобы результат был double
     dataGridView1.Rows [i].Cells [dataGridView1.ColumnCount - 1].Value =
      String.Format ("{0:f2}", avg);
    }
   }

Мы выбираем для обработки только оценки (не проверяя их корректность), так как в ведомости могли быть другие отметки, например «б» (болен), «н» (отсутствует) и т.п.

Границей внешнего цикла, равной dataGridView1.RowCount — 1, мы исключаем при обработке пустую строку в конце таблицы, не содержащую данных.

 Скачать пример Lab4_4 в архиве .zip с проектом C# Visual Studio 2019 (12 Кб)

Задание: реализовать табличный редактор в соответствии с вариантом задания. Предусмотреть в приложении следующие возможности:

  • загрузка табличных данных из файла и их сохранение в файл;
  • редактирование, добавление, удаление записей;
  • при необходимости – поиск и выделение (или отображение в новом окне) записей, отвечающих заданным условиям;
  • реализация расчётов, указанных в варианте задания.

 Разработка базового редактора несвязанной таблицы на основе DataGridView (файл .pdf) (483 Кб)

 Проект C# (Visual Studio 2019) из статьи .pdf, развернуть архив .zip в новую папку (12 Кб)

09.03.2023, 10:49 [6601 просмотр]


К этой статье пока нет комментариев, Ваш будет первым

DataGridView in Windows Forms – Tips, Tricks and Frequently Asked Questions(FAQ)

DataGridView control is a Windows Forms control that gives you the ability to customize and edit tabular data. It gives you number of properties, methods and events to customize its appearance and behavior. In this article, we will discuss some frequently asked questions and their solutions. These questions have been collected from a variety of sources including some newsgroups, MSDN site and a few, answered by me at the MSDN forums.

Tip 1 – Populating a DataGridView

In this short snippet, we will populate a DataGridView using the LoadData() method. This method uses the SqlDataAdapter to populate a DataSet. The table ‘Orders’ in the DataSet is then bound to the BindingSource component which gives us the flexibility to choose/modify the data location.

C#

public partial class Form1 : Form

    {

        private SqlDataAdapter da;

        private SqlConnection conn;

        BindingSource bsource = new BindingSource();

        DataSet ds = null;

        string sql;

        public Form1()

        {

            InitializeComponent();

        }

        private void btnLoad_Click(object sender, EventArgs e)

        {

            LoadData();

        }

        private void LoadData()

        {

string connectionString = «Data Source=localhost;Initial Catalog=Northwind;» + «Integrated Security=SSPI;»;

            conn = new SqlConnection(connectionString);

sql = «SELECT OrderID, CustomerID, EmployeeID, OrderDate, Freight,» + «ShipName, ShipCountry FROM Orders»;

            da = new SqlDataAdapter(sql, conn);

            conn.Open();

            ds = new DataSet();

            SqlCommandBuilder commandBuilder = new SqlCommandBuilder(da);          

            da.Fill(ds, «Orders»);

            bsource.DataSource = ds.Tables[«Orders»];

            dgv.DataSource = bsource;          

        }

    }

VB.NET

Public Partial Class Form1

      Inherits Form

            Private da As SqlDataAdapter

            Private conn As SqlConnection

            Private bsource As BindingSource = New BindingSource()

            Private ds As DataSet = Nothing

            Private sql As String

            Public Sub New()

                  InitializeComponent()

            End Sub

Private Sub btnLoad_Click(ByVal sender As Object, ByVal e As EventArgs)

                  LoadData()

            End Sub

            Private Sub LoadData()

Dim connectionString As String = «Data Source=localhost;Initial Catalog=Northwind;» & «Integrated Security=SSPI;»

                  conn = New SqlConnection(connectionString)

sql = «SELECT OrderID, CustomerID, EmployeeID, OrderDate, Freight,» & «ShipName, ShipCountry FROM Orders»

                  da = New SqlDataAdapter(sql, conn)

                  conn.Open()

                  ds = New DataSet()

Dim commandBuilder As SqlCommandBuilder = New SqlCommandBuilder(da)

                  da.Fill(ds, «Orders»)

                  bsource.DataSource = ds.Tables(«Orders»)

                  dgv.DataSource = bsource

            End Sub

End Class

Tip 2 – Update the data in the DataGridView and save changes in the database

After editing the data in the cells, if you would like to update the changes permanently in the database, use the following code:

C#

        private void btnUpdate_Click(object sender, EventArgs e)

        {

            DataTable dt = ds.Tables[«Orders»];

           this.dgv.BindingContext[dt].EndCurrentEdit();

            this.da.Update(dt);

        }

VB.NET

Private Sub btnUpdate_Click(ByVal sender As Object, ByVal e As EventArgs)

                  Dim dt As DataTable = ds.Tables(«Orders»)

                  Me.dgv.BindingContext(dt).EndCurrentEdit()

                  Me.da.Update(dt)

      End Sub

Tip 3 – Display a confirmation box before deleting a row in the DataGridView

Handle the UserDeletingRow event to display a confirmation box to the user. If the user confirms the deletion, delete the row. If the user clicks cancel, set e.cancel = true which cancels the row deletion.

C#

private void dgv_UserDeletingRow(object sender, DataGridViewRowCancelEventArgs e)

        {

            if (!e.Row.IsNewRow)

            {

                DialogResult res = MessageBox.Show(«Are you sure you want to delete this row?», «Delete confirmation»,

                         MessageBoxButtons.YesNo, MessageBoxIcon.Question);

                if (res == DialogResult.No)

                    e.Cancel = true;

            }

        }

VB.NET

Private Sub dgv_UserDeletingRow(ByVal sender As Object, ByVal e As DataGridViewRowCancelEventArgs)

                  If (Not e.Row.IsNewRow) Then

                        Dim res As DialogResult = MessageBox.Show(«Are you sure you want to delete this row?», «Delete confirmation», MessageBoxButtons.YesNo, MessageBoxIcon.Question)

                        If res = DialogResult.No Then

                              e.Cancel = True

                        End If

                  End If

End Sub

Tip 4 – How to autoresize column width in the DataGridView

The snippet shown below, first auto-resizes the columns to fit its content. Then the AutoSizeColumnsMode is set to the ‘DataGridViewAutoSizeColumnsMode.AllCells’ enumeration value which automatically adjust the widths of the columns when the data changes.

C#

private void btnResize_Click(object sender, EventArgs e)

        {

            dgv.AutoResizeColumns();

            dgv.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells;

        }

VB.NET

Private Sub btnResize_Click(ByVal sender As Object, ByVal e As EventArgs)

                  dgv.AutoResizeColumns()

                  dgv.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells

End Sub

Tip 5 — Select and Highlight an entire row in DataGridView

C#

int rowToBeSelected = 3; // third row

if (dgv.Rows.Count >= rowToBeSelected)

{             

       // Since index is zero based, you have to subtract 1

        dgv.Rows[rowToBeSelected — 1].Selected = true;

}

VB.NET

Dim rowToBeSelected As Integer = 3 ‘ third row

If dgv.Rows.Count >= rowToBeSelected Then

         ‘ Since index is zero based, you have to subtract 1

            dgv.Rows(rowToBeSelected — 1).Selected = True

End If

Tip 6 — How to scroll programmatically to a row in the DataGridView

The DataGridView has a property called FirstDisplayedScrollingRowIndex that can be used in order to scroll to a row programmatically.

C#

int jumpToRow = 20;

if (dgv.Rows.Count >= jumpToRow && jumpToRow >= 1)

{             

        dgv.FirstDisplayedScrollingRowIndex = jumpToRow;

        dgv.Rows[jumpToRow].Selected = true;

}

VB.NET

Dim jumpToRow As Integer = 20

If dgv.Rows.Count >= jumpToRow AndAlso jumpToRow >= 1 Then

            dgv.FirstDisplayedScrollingRowIndex = jumpToRow

            dgv.Rows(jumpToRow).Selected = True

End If

Tip 7 — Calculate a column total in the DataGridView and display in a textbox

A common requirement is to calculate the total of a currency field and display it in a textbox. In the snippet below, we will be calculating the total of the ‘Freight’ field. We will then display the data in a textbox by formatting the result (observe the ToString(«c»)) while displaying the data, which displays the culture-specific currency.

C#

private void btnTotal_Click(object sender, EventArgs e)

        {

            if(dgv.Rows.Count > 0)

             txtTotal.Text = Total().ToString(«c»);

        }

        private double Total()

        {

            double tot = 0;

            int i = 0;

            for (i = 0; i < dgv.Rows.Count; i++)

            {

                tot = tot + Convert.ToDouble(dgv.Rows[i].Cells[«Freight»].Value);

            }

            return tot;

        }

VB.NET

Private Sub btnTotal_Click(ByVal sender As Object, ByVal e As EventArgs)

                  If dgv.Rows.Count > 0 Then

                   txtTotal.Text = Total().ToString(«c»)

                  End If

End Sub

Private Function Total() As Double

                  Dim tot As Double = 0

                  Dim i As Integer = 0

                  For i = 0 To dgv.Rows.Count — 1

                        tot = tot + Convert.ToDouble(dgv.Rows(i).Cells(«Freight»).Value)

                  Next i

                  Return tot

End Function

Tip 8 — Change the Header Names in the DataGridView

If the columns being retrieved from the database do not have meaningful names, we always have the option of changing the header names as shown in this snippet:

C#

private void btnChange_Click(object sender, EventArgs e)

        {

            dgv.Columns[0].HeaderText = «MyHeader1»;

            dgv.Columns[1].HeaderText = «MyHeader2»;

        }

VB.NET

Private Sub btnChange_Click(ByVal sender As Object, ByVal e As EventArgs)

                  dgv.Columns(0).HeaderText = «MyHeader1»

                  dgv.Columns(1).HeaderText = «MyHeader2»

End Sub

Tip 9 — Change the Color of Cells, Rows and Border in the DataGridView

C#

private void btnCellRow_Click(object sender, EventArgs e)

        {

            // Change ForeColor of each Cell

            this.dgv.DefaultCellStyle.ForeColor = Color.Coral;

            // Change back color of each row

            this.dgv.RowsDefaultCellStyle.BackColor = Color.AliceBlue;

            // Change GridLine Color

            this.dgv.GridColor = Color.Blue;

            // Change Grid Border Style

            this.dgv.BorderStyle = BorderStyle.Fixed3D;

        }

VB.NET

Private Sub btnCellRow_Click(ByVal sender As Object, ByVal e As EventArgs)

                  ‘ Change ForeColor of each Cell

                  Me.dgv.DefaultCellStyle.ForeColor = Color.Coral

                  ‘ Change back color of each row

                  Me.dgv.RowsDefaultCellStyle.BackColor = Color.AliceBlue

                  ‘ Change GridLine Color

                  Me.dgv.GridColor = Color.Blue

                  ‘ Change Grid Border Style

                  Me.dgv.BorderStyle = BorderStyle.Fixed3D

End Sub

Tip 10 — Hide a Column in the DataGridView

If you would like to hide a column based on a certain condition, here’s a snippet for that.

C#

private void btnHide_Click(object sender, EventArgs e)

        {

            this.dgv.Columns[«EmployeeID»].Visible = false;

        }

VB.NET

Private Sub btnHide_Click(ByVal sender As Object, ByVal e As EventArgs)

                  Me.dgv.Columns(«EmployeeID»).Visible = False

End Sub

Tip 11 — Handle SelectedIndexChanged of a ComboBox in the DataGridView

To handle the SelectedIndexChanged event of a DataGridViewComboBox, you need to use the DataGridView.EditingControlShowing event as shown below. You can then retrieve the selected index or the selected text of the combobox.

C#

private void dataGridView1_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)

        {

            ComboBox editingComboBox = (ComboBox)e.Control;

            if(editingComboBox != null)

                editingComboBox.SelectedIndexChanged += new System.EventHandler(this.editingComboBox_SelectedIndexChanged);

        }

private void editingComboBox_SelectedIndexChanged(object sender, System.EventArgs e)

        {

            ComboBox comboBox1 = (ComboBox)sender;

            // Display index

            MessageBox.Show(comboBox1.SelectedIndex.ToString());

            // Display value

            MessageBox.Show(comboBox1.Text);

        }

VB.NET

Private Sub dataGridView1_EditingControlShowing(ByVal sender As Object, ByVal e As DataGridViewEditingControlShowingEventArgs)

                  Dim editingComboBox As ComboBox = CType(e.Control, ComboBox)

                  If Not editingComboBox Is Nothing Then

                        AddHandler editingComboBox.SelectedIndexChanged, AddressOf editingComboBox_SelectedIndexChanged

                  End If

End Sub

Private Sub editingComboBox_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs)

                  Dim comboBox1 As ComboBox = CType(sender, ComboBox)

                  ‘ Display index

                  MessageBox.Show(comboBox1.SelectedIndex.ToString())

                  ‘ Display value

                  MessageBox.Show(comboBox1.Text)

End Sub

Tip 12 — Change Color of Alternate Rows in the DataGridView

C#

private void btnAlternate_Click(object sender, EventArgs e)

        {

            this.dgv.RowsDefaultCellStyle.BackColor = Color.White;

            this.dgv.AlternatingRowsDefaultCellStyle.BackColor = Color.Aquamarine;

        }

VB.NET

Private Sub btnAlternate_Click(ByVal sender As Object, ByVal e As EventArgs)

                  Me.dgv.RowsDefaultCellStyle.BackColor = Color.White

                  Me.dgv.AlternatingRowsDefaultCellStyle.BackColor = Color.Aquamarine

End Sub

Tip 13 — Formatting Data in the DataGridView

The DataGridView exposes properties that enable you to format data such as displaying a currency column in the culture specific currency or displaying nulls in a desired format and so on.

C#

private void btnFormat_Click(object sender, EventArgs e)

        {

            // display currency in culture-specific currency for

            this.dgv.Columns[«Freight»].DefaultCellStyle.Format = «c»;

            // display nulls as ‘NA’

            this.dgv.DefaultCellStyle.NullValue = «NA»;

        }

VB.NET

Private Sub btnFormat_Click(ByVal sender As Object, ByVal e As EventArgs)

                  ‘ display currency in culture-specific currency for

                  Me.dgv.Columns(«Freight»).DefaultCellStyle.Format = «c»

                  ‘ display nulls as ‘NA’

                  Me.dgv.DefaultCellStyle.NullValue = «NA»

End Sub

Tip 14 – Change the order of columns in the DataGridView

In order to change the order of columns, just set the DisplayIndex property of the DataGridView to the desired value. Remember that the index is zero based.

C#

private void btnReorder_Click(object sender, EventArgs e)

        {

             dgv.Columns[«CustomerID»].DisplayIndex = 5;

             dgv.Columns[«OrderID»].DisplayIndex = 3;

             dgv.Columns[«EmployeeID»].DisplayIndex = 1;

             dgv.Columns[«OrderDate»].DisplayIndex = 2;

             dgv.Columns[«Freight»].DisplayIndex = 6;

             dgv.Columns[«ShipCountry»].DisplayIndex = 0;

             dgv.Columns[«ShipName»].DisplayIndex = 4;

        }

VB.NET

Private Sub btnReorder_Click(ByVal sender As Object, ByVal e As EventArgs)

                   dgv.Columns(«CustomerID»).DisplayIndex = 5

                   dgv.Columns(«OrderID»).DisplayIndex = 3

                   dgv.Columns(«EmployeeID»).DisplayIndex = 1

                   dgv.Columns(«OrderDate»).DisplayIndex = 2

                   dgv.Columns(«Freight»).DisplayIndex = 6

                   dgv.Columns(«ShipCountry»).DisplayIndex = 0

                   dgv.Columns(«ShipName»).DisplayIndex = 4

End Sub

I hope this article was useful and I thank you for viewing it.

This article has been editorially reviewed by Suprotim Agarwal.

Absolutely Awesome Book on C# and .NET

C# and .NET have been around for a very long time, but their constant growth means there’s always more to learn.

We at DotNetCurry are very excited to announce The Absolutely Awesome Book on C# and .NET. This is a 500 pages concise technical eBook available in PDF, ePub (iPad), and Mobi (Kindle).

Organized around concepts, this Book aims to provide a concise, yet solid foundation in C# and .NET, covering C# 6.0, C# 7.0 and .NET Core, with chapters on the latest .NET Core 3.0, .NET Standard and C# 8.0 (final release) too. Use these concepts to deepen your existing knowledge of C# and .NET, to have a solid grasp of the latest in C# and .NET OR to crack your next .NET Interview.

Click here to Explore the Table of Contents or Download Sample Chapters!

Suprotim Agarwal, MCSD, MCAD, MCDBA, MCSE, is the founder of DotNetCurry, DNC Magazine for Developers, SQLServerCurry and DevCurry. He has also authored a couple of books 51 Recipes using jQuery with ASP.NET Controls and The Absolutely Awesome jQuery CookBook.

Suprotim has received the prestigious Microsoft MVP award for Sixteen consecutive years. In a professional capacity, he is the CEO of A2Z Knowledge Visuals Pvt Ltd, a digital group that offers Digital Marketing and Branding services to businesses, both in a start-up and enterprise environment.

Get in touch with him on Twitter @suprotimagarwal or at LinkedIn

Сегодня я решил написать Вам о том, как работать с элементом управления DataGridView в языке программирования C#.
DataGridView — очень мощный инструмент для работы с данными. Он очень гибок и позволяет автоматизировать вывод информации. Я постараюсь рассказать Вам о базовой работе с данным элементом, а так же привести пример автоматизации обновления данных в таблице.

Для тех, кто не знает о элементе управления DataGridView — скажу что данный контрол позволяет вывести таблицу с данными в Вашей программе. Данный элемент управления имеет огромное число настроек. Благодаря этому Вы скорей всего сможете подстроить внешний вид таблицы так, как Вам нужно. Все настройки доступны в панели свойств элемента в Visual Studio. Я буду задавать свойства нашего элемента пряма в коде, для большей наглядности.

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

private void Form1_Load(object sender, EventArgs e)
        {
            //создадим таблицу вывода товаров с колонками 
            //Название, Цена, Остаток
            
            var column1 = new DataGridViewColumn();
            column1.HeaderText = "Название"; //текст в шапке
            column1.Width = 100; //ширина колонки
            column1.ReadOnly = true; //значение в этой колонке нельзя править
            column1.Name = "name"; //текстовое имя колонки, его можно использовать вместо обращений по индексу
            column1.Frozen = true; //флаг, что данная колонка всегда отображается на своем месте
            column1.CellTemplate = new DataGridViewTextBoxCell(); //тип нашей колонки

            var column2 = new DataGridViewColumn();
            column2.HeaderText = "Цена"; 
            column2.Name = "price";
            column2.CellTemplate = new DataGridViewTextBoxCell();

            var column3 = new DataGridViewColumn();
            column3.HeaderText = "Остаток";
            column3.Name = "count";
            column3.CellTemplate = new DataGridViewTextBoxCell();

            dataGridView1.Columns.Add(column1);
            dataGridView1.Columns.Add(column2);
            dataGridView1.Columns.Add(column3);

            dataGridView1.AllowUserToAddRows = false; //запрешаем пользователю самому добавлять строки

            for (int i = 0; i < 5; ++i)
            {
                //Добавляем строку, указывая значения колонок поочереди слева направо
                dataGridView1.Rows.Add("Пример 1, Товар " + i, i * 1000, i); 
            }

            for (int i = 0; i < 5; ++i)
            {
                //Добавляем строку, указывая значения каждой ячейки по имени (можно использовать индекс 0, 1, 2 вместо имен)
                dataGridView1.Rows.Add();
                dataGridView1["name", dataGridView1.Rows.Count - 1].Value = "Пример 2, Товар " + i;
                dataGridView1["price", dataGridView1.Rows.Count - 1].Value = i * 1000;
                dataGridView1["count", dataGridView1.Rows.Count - 1].Value = i;
            }

            //А теперь простой пройдемся циклом по всем ячейкам
            for (int i = 0; i < dataGridView1.Rows.Count; ++i)
            {
                for (int j = 0; j < dataGridView1.Columns.Count; ++j)
                {
                    //Значения ячеек хряняться в типе object
                    //это позволяет хранить любые данные в таблице
                    object o = dataGridView1[j, i].Value;
                }
            }
        }
    }

В целом это и есть базовая работа с элементом DataGridView. По сути мы имеем работу с матрицей (двумерным массивом). Давайте теперь рассмотрим базовый вариант автоматизации обновления данных в таблице — механизм привязки (Binding).
Для этого создадим класс, который будет играть роль одной строки. Т.е. по сути нам нужно создать класс, описывающий колонки нашей таблицы.

class SampleRow
    {
        public string Name { get; set; } //обязательно нужно использовать get конструкцию
        public float Price { get; set; }
        public int Count { get; set; }

        public string Hidden = ""; //Данное свойство не будет отображаться как колонка

        public SampleRow(string name, float price, int count)
        {
            this.Name = name;
            this.Price = price;
            this.Count = count;
        }
    }

Теперь мы можем написать следующий код в событии формы OnLoad:

private void Form1_Load(object sender, EventArgs e)
        {
            BindingList<SampleRow> data = new BindingList<SampleRow>(); //Специальный список List с вызовом события обновления внутреннего состояния, необходимого для автообновления datagridview
            data.Add(new SampleRow("Товар 1", 100, 1));
            data.Add(new SampleRow("Товар 2", 200, 2));
            data.Add(new SampleRow("Товар 3", 300, 3));

            dataGridView1.DataSource = data;
            data.Add(new SampleRow("Товар 4", 400, 4));
      }

DataGridView. This control displays data from SQL databases. This tutorial takes a specific table from a database (as a DataTable) and displays it on a DataGridView.

DataGridView

Getting started. In Visual Studio, select the File menu and then New Project, and select a Windows Forms application. Next drag the DataGridView icon into the window.

Make data source. Here we need to generate an SDF file to interact with. We will use Visual Studio for this part. Go to Data and then Add New Data Source.

Then Select Database, and click Next. Click on New Connection, as we need to create an all-new connection and database.

Detail Type in a database name, and then click Create. We are using SQL Server CE, which is the compact edition of SQL Server 2005.

Shows a datagridview

Data Source=C:\Users\Sam\Documents\Data.sdf

Data. Next, we need to have actual data in the table we created. Return to the Server Explorer, and right-click on the table, such as the Animals table. Type in 10 for Weight, and brown for Color.

Note This data is for the example, but your program may have similar fields. We could be working on a table for a veterinarian’s office.

Weight: 10
Color: Brown

Weight: 15
Color: Black

Weight: 5
Color: Green

Weight: 20
Color: White

Data adapter. In C# code, you must open a connection to your database first, and then create a DataAdapter. There are different adapter implementations, but they all work in similar ways.

Detail The example calls FillData after InitializeComponent. In FillData we put the database contents into the DataGridView.

Step 1 It opens a connection to the database. We use Properties.Settings Default.DataConnectionString, which was generated.

Step 2 It uses a new DataAdapter: SqlCeDataAdapter. A DataAdapter specifies a command that directly fills a DataSet or DataTable.

Step 3 It assigns the DataSource in the DataGridView, which renders the contents of the database onto the screen.

using System.Data;
using System.Data.SqlServerCe;
using System.Windows.Forms;

namespace WindowsFormsApplication5
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
FillData();
}

void FillData()
{
// Step 1: Open connection.
using (SqlCeConnection c = new SqlCeConnection( Properties.Settings.Default.DataConnectionString))
{
c.Open();
// Step 2: Create new DataAdapter.
using (SqlCeDataAdapter a = new SqlCeDataAdapter(«SELECT * FROM Animals», c))
{
// Step 3: Use DataAdapter to fill DataTable.
DataTable t = new DataTable();
a.Fill(t);
// Step 4: Render data onto the screen.
dataGridView1.DataSource = t;
}
}
}
}
}

Alternating colors. For usability, you want to have alternating row colors on your DataGridView. The .NET Framework provides the AlternatingRowsDefaultCellStyle attribute.

Tip Change the BackColor to something your users will enjoy, and that will improve the program’s usability. I chose aqua.

DataSet. The DataSet object available in System.Data has a slightly different purpose from DataTable, as it allows more information to be stored.

Improvements. The default DataGridView appearance and functionality is not usually the best. Here we make some tweaks to the DataGridView to make it more pleasing.

Note Use the Anchor property on the DataGridView in the designer view to «pin» it to each edge of your window.

Note 2 Make the DataGridView white by changing the Appearance and then BackgroundColor from AppWorkspace to Window.

Note 3 Hide the row headers. Go to Appearance and then RowHeadersVisible and change it to False.

Add columns. With the DataGridView, you can predefine columns for display. This doesn’t display anything. You create the rules by which the actual data will be displayed.

Step 1 Go to Properties and then locate Columns. This shows the Edit Columns dialog box—click on the «Add» button.

Step 2 Enter Header text. The text you type into the Header text box will ensure that the text is always shown.

Step 3 Change the properties of the DataGridViewColumn in the dialog box. I set 110 pixels as the width and AutoSizeMode of None.

DataPropertyName. You need to specify that a certain column in your database be inserted into the Column you just added in the Columns dialog box.

Tip To do this, you must assign the DataPropertyName of the column to the column name from your database.

So To specify that your DataGridViewColumn be used for the Weight column for your database, type «Weight» into the DataPropertyName box.

A summary. We used a database and displayed its contents in a usable DataGridView. This is critical for many data-driven Windows Forms application.

Dot Net Perls is a collection of pages with code examples, which are updated to stay current. Programming is an art, and it can be learned from examples.

Donate to this site to help offset the costs of running the server. Sites like this will cease to exist if there is no financial support for them.

Sam Allen is passionate about computer languages, and he maintains 100% of the material available on this website. He hopes it makes the world a nicer place.

This page was last updated on Dec 22, 2023 (edit).

Понравилась статья? Поделить с друзьями:
0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии
  • Как поменять основной монитор на windows 10
  • Как сделать дополнительный экран на windows 10
  • Настройка парольной политики windows
  • Оптимизация windows 10 для слабого железа
  • Включить панель задач поверх всех окон windows 7