Редактирование ячеек DataGridView.

Элемент управления DataGridView приложений Windows Forms MVS позволяет выводить массивы однотипных данных в виде таблицы. Кроме просмотра и обычного редактирования ячеек DataGridView позволяет программно подключить нестандартные редакторы для содержания ячеек. В результате работу с приложением можно сделать более удобной. Для наших экспериментов с DataGridView создадим небольшое приложение по учёту персонала.

Содержание

Описание тестового приложения

Данные для DataGridView можно получить как из базы данных так и из внутренних структур программы — массивов, коллекций из простых типов или специально созданных классов. В тестовых целях в этом приложении базу данных использовать не будем, а наши сведения будем хранить в файле. Эти сведения реализуем как список экземпляров нами разработанного класса данных о работнике. Запустим Microsoft Visual Studio и создадим новый проект по шаблону для Windows Forms Application. В главную форму добавим SplitContainer, разделив её по вертикали. Сверху поместим DataGridView, заполнив им всё пространство. В нижнюю часть добавим три кнопки. Также перетащим на форму OpenFileDialog. Его будем использовать при выборе файлов с фотографиями. Должно получиться как на рис. 1 ниже :

рис. 1 Главная форма приложения.

Для кнопок добавим обработчики событий их нажатия. Проще всего это сделать двойным кликом по каждой из них в окне конструктора формы. Для DataGridView: в RowTemplate установим Height (высота строк) равной 100, чтобы отображаемые в строке фото были хорошо видны, у AutoSizeColumnsMode выставим Fill, чтобы столбцы по ширине заполняли всю форму.

Определение класса данных работника

Чтобы сохранять информацию о каждом сотруднике создадим класс Person. В нём определим поля для хранения имени, фамилии, даты рождения и приёма на работу, должности и фотографии работника. В атрибуте DisplayName укажем названия столбцов, которые будут показаны в DataGridView. Код класса Person приведён ниже :

[Serializable]
public class Person : INotifyPropertyChanged
{
    private int _id;
    private string _name;
    private string _surname;
    private string _birthdate;
    private DateTime _tm;
    private string _position;
    private Image _foto = null;

    [DisplayName("Номер")]
    public int Num
    {
        get { return _id; }
        set { _id = value; OnPropertyChanged(); }
    }

    [DisplayName("Имя")]
    public string Name
    {
        get { return _name; }
        set { _name = value; OnPropertyChanged(); }
    }

    [DisplayName("Фамилия")]
    public string SurName
    {
        get { return _surname; }
        set { _surname = value; OnPropertyChanged(); }
    }

    [DisplayName("Дата рождения")]
    public string BirthDate
    {
        get { return _birthdate; }
        set { _birthdate = value; OnPropertyChanged(); }
    }

    [DisplayName("Дата приёма на работу")]
    public DateTime TM
    {
        get { return _tm; }
        set { _tm = value; OnPropertyChanged(); }
    }

    [DisplayName("Должность")]
    public string Position
    {
        get { return _position; }
        set { _position = value; OnPropertyChanged(); }
    }

    [DisplayName("Фото")]
    public Image PersonFoto
    {
        get { return _foto; }
        set { _foto = value; OnPropertyChanged(); }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

Для того, чтобы внесённые изменения отображались в DataGridView унаследуем наш класс от интерфейса INotifyPropertyChanged. Реализуем событие PropertyChanged и метод, вызывающий его обработку. В сеттере каждого свойства будем вызывать этот метод.

ComboBox для редактирования должности

Перечень должностей в организации, как правило, постоянен и правомерно его представить списком для выбора в ComboBox. Тип свойства для хранения должности string и для его редактирования DataGridView используется текстовый редактор. Давайте применим для выбора значения ComboBox. Создадим элемент управления ComboBox внутри класса главной формы, сделаем пока невидимым. Когда нужно будет редактировать ячейку столбца <Должность>, будем показывать на её месте этот ComboBox и выбранное значение установим в эту ячейку.

readonly ComboBox customCombo = new ComboBox();

В методе, выполняемом при загрузке главной формы MainFormDGV_Load(object sender, EventArgs e), проинициализируем наш ComboBox, подпишем на событие TextChanged метод combo_TextChanged и добавим этот элемент к списку элементов DataGridView :

customCombo.Visible = false;
customCombo.Items.Clear();
customCombo.Items.Add("Генеральный директор");
customCombo.Items.Add("Главный бухгалтер");
customCombo.Items.Add("Бухгалтер");
customCombo.Items.Add("Старший менеджер");
customCombo.Items.Add("Менеджер");
customCombo.Items.Add("Дизайнер");
customCombo.Items.Add("Кассир");
customCombo.Items.Add("Начальник склада");
customCombo.Items.Add("Секретарь");
customCombo.Items.Add("Курьер");
customCombo.Items.Add("Стажёр");
customCombo.TextChanged += new EventHandler(this.combo_TextChanged);
dataGridView1.Controls.Add(customCombo);

private void combo_TextChanged(object obj, System.EventArgs e)
{
    if (dataGridView1.CurrentCell.ColumnIndex == 5)
    {
         // фокус с ComboBox перемещается - спрячем его
         customCombo.Visible = false;
         // запомним значение, выбранное пользователем, в текущей ячейке
         try
         {
              dataGridView1.CurrentCell.Value = customCombo.Text;
         }
         catch { }
     }
}

Будем редактировать ячейки по стандартному двойному клику по ячейке (или F12) в методе для его обработки dataGridView1_CellDoubleClick. Из переданных аргументов получим номера столбца и строки ячейки. В зависимости от номера столбца будем применять редакторы ячеек в соответствии с их типами.

int nr = e.RowIndex, nc = e.ColumnIndex;
cec.Visible = false;  // прячем ещё раз редактор дат (календарь)
if (nc == 5)
{
     customCombo.Left = dataGridView1.Columns[5].Width * 5 + dataGridView1.RowHeadersWidth;
     customCombo.Top = dataGridView1.Rows[nr].Height * (dataGridView1.CurrentCell.RowIndex - dataGridView1.FirstDisplayedScrollingRowIndex) + dataGridView1.ColumnHeadersHeight;
     customCombo.Text = (string)dataGridView1.CurrentCell.Value;
     customCombo.DroppedDown = true;
     customCombo.Visible = true;
}

В первую очередь задаем левый верхний угол расположения ComboBox по номерам строк и столбцов, ширине столбцов и высоте строк (вычисляем). Передаём старое значение, открываем список выбора и показываем наш готовый ComboBox в нужной позиции. Пользователь выбирает значение, по клику выполняется метод combo_TextChanged : прячем ComboBox и выбранное значение отображаем в ячейке. Должно получиться как на рис. 2 ниже :

рис. 2 Наш ComboBox.

CalendarEditingControl для редактирования даты рождения

Дата рождения в классе поле типа String, а дата приёма на работу — поле типа DateTime. Но для их редактирования будем использовать элемент управления Календарь — CalendarEditingControl. Он создан на базе элемента DateTimePicker и интерфейса IDataGridViewEditingControl, чтобы его можно было использовать в качестве редактора ячеек в DataGridView. В классе необходимо реализовать методы интерфейса. Код класса взят из примера в документации по DataGridView «Инструкции по размещению элементов в DataGridView». На всякий случай приведу его полностью :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace ExampleDataGridView
{
    public class CalendarColumn : DataGridViewColumn
    {
        public CalendarColumn() : base(new CalendarCell())
        {
        }

        public override DataGridViewCell CellTemplate
        {
            get
            {
                return base.CellTemplate;
            }
            set
            {
                // Ensure that the cell used for the template is a CalendarCell.
                if (value != null &&
                    !value.GetType().IsAssignableFrom(typeof(CalendarCell)))
                {
                    throw new InvalidCastException("Must be a CalendarCell");
                }
                base.CellTemplate = value;
            }
        }
    }
    public class CalendarCell : DataGridViewTextBoxCell
    {

        public CalendarCell()
            : base()
        {
            // Use the short date format.
            this.Style.Format = "d";
        }

        public override void InitializeEditingControl(int rowIndex, object
            initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle)
        {
            // Set the value of the editing control to the current cell value.
            base.InitializeEditingControl(rowIndex, initialFormattedValue,
                dataGridViewCellStyle);
            CalendarEditingControl ctl =
                DataGridView.EditingControl as CalendarEditingControl;
            // Use the default row value when Value property is null.
            if (this.Value == null)
            {
                ctl.Value = (DateTime)this.DefaultNewRowValue;
            }
            else
            {
                ctl.Value = (DateTime)this.Value;
            }
        }

        public override Type EditType
        {
            get
            {
                // Return the type of the editing control that CalendarCell uses.
                return typeof(CalendarEditingControl);
            }
        }

        public override Type ValueType
        {
            get
            {
                // Return the type of the value that CalendarCell contains.

                return typeof(DateTime);
            }
        }

        public override object DefaultNewRowValue
        {
            get
            {
                // Use the current date and time as the default value.
                return DateTime.Now;
            }
        }
    }

    class CalendarEditingControl : DateTimePicker, IDataGridViewEditingControl
    {
        DataGridView dataGridView;
        private bool valueChanged = false;
        int rowIndex;

        public CalendarEditingControl()
        {
            this.Format = DateTimePickerFormat.Short;
        }

        // Implements the IDataGridViewEditingControl.EditingControlFormattedValue
        // property.
        public object EditingControlFormattedValue
        {
            get
            {
                return this.Value.ToShortDateString();
            }
            set
            {
                if (value is String)
                {
                    try
                    {
                        // This will throw an exception of the string is
                        // null, empty, or not in the format of a date.
                        this.Value = DateTime.Parse((String)value);
                    }
                    catch
                    {
                        // In the case of an exception, just use the
                        // default value so we're not left with a null
                        // value.
                        this.Value = DateTime.Now;
                    }
                }
            }
        }

        // Implements the
        // IDataGridViewEditingControl.GetEditingControlFormattedValue method.
        public object GetEditingControlFormattedValue(
            DataGridViewDataErrorContexts context)
        {
            return EditingControlFormattedValue;
        }

        // Implements the
        // IDataGridViewEditingControl.ApplyCellStyleToEditingControl method.
        public void ApplyCellStyleToEditingControl(
            DataGridViewCellStyle dataGridViewCellStyle)
        {
            this.Font = dataGridViewCellStyle.Font;
            this.CalendarForeColor = dataGridViewCellStyle.ForeColor;
            this.CalendarMonthBackground = dataGridViewCellStyle.BackColor;
        }

        // Implements the IDataGridViewEditingControl.EditingControlRowIndex
        // property.
        public int EditingControlRowIndex
        {
            get
            {
                return rowIndex;
            }
            set
            {
                rowIndex = value;
            }
        }

        // Implements the IDataGridViewEditingControl.EditingControlWantsInputKey
        // method.
        public bool EditingControlWantsInputKey(
            Keys key, bool dataGridViewWantsInputKey)
        {
            // Let the DateTimePicker handle the keys listed.
            switch (key & Keys.KeyCode)
            {
                case Keys.Left:
                case Keys.Up:
                case Keys.Down:
                case Keys.Right:
                case Keys.Home:
                case Keys.End:
                case Keys.PageDown:
                case Keys.PageUp:
                    return true;
                default:
                    return !dataGridViewWantsInputKey;
            }
        }

        // Implements the IDataGridViewEditingControl.PrepareEditingControlForEdit
        // method.
        public void PrepareEditingControlForEdit(bool selectAll)
        {
            // No preparation needs to be done.
        }

        // Implements the IDataGridViewEditingControl
        // .RepositionEditingControlOnValueChange property.
        public bool RepositionEditingControlOnValueChange
        {
            get
            {
                return false;
            }
        }

        // Implements the IDataGridViewEditingControl
        // .EditingControlDataGridView property.
        public DataGridView EditingControlDataGridView
        {
            get
            {
                return dataGridView;
            }
            set
            {
                dataGridView = value;
            }
        }

        // Implements the IDataGridViewEditingControl
        // .EditingControlValueChanged property.
        public bool EditingControlValueChanged
        {
            get
            {
                return valueChanged;
            }
            set
            {
                valueChanged = value;
            }
        }

        // Implements the IDataGridViewEditingControl
        // .EditingPanelCursor property.
        public Cursor EditingPanelCursor
        {
            get
            {
                return base.Cursor;
            }
        }

        protected override void OnValueChanged(EventArgs eventargs)
        {
            // Notify the DataGridView that the contents of the cell
            // have changed.
            valueChanged = true;
            this.EditingControlDataGridView.NotifyCurrentCellDirty(true);
            base.OnValueChanged(eventargs);
        }
    }

/*    public class Form1 : Form
    {
        private DataGridView dataGridView1 = new DataGridView();

        [STAThreadAttribute()]
        public static void Main()
        {
            Application.Run(new Form1());
        }

        public Form1()
        {
            this.dataGridView1.Dock = DockStyle.Fill;
            this.Controls.Add(this.dataGridView1);
            this.Load += new EventHandler(Form1_Load);
            this.Text = "DataGridView calendar column demo";
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            CalendarColumn col = new CalendarColumn();
            this.dataGridView1.Columns.Add(col);
            this.dataGridView1.RowCount = 5;
            foreach (DataGridViewRow row in this.dataGridView1.Rows)
            {
                row.Cells[0].Value = DateTime.Now;
            }
        }
    }*/
}

В документации демонстрация работы была выполнена на базе формы Form1. Нам это не нужно — закоментируем, но оставим для справки. В коде приведена реализация классов для ячеек и столбцов типа Calendar чтобы можно было их применять в DataGridView.

Давайте продолжим реализацию наших редакторов дат. Как и с ComboBox, в классе главной формы создадим экземпляр элемента управления CalendarEditingControl — cec, сделаем его невидимым. В методе, выполняемом при загрузке главной формы MainFormDGV_Load(object sender, EventArgs e), проинициализируем наш CalendarEditingControl, подпишем на событие ValueChanged метод cec_ValueChanged и добавим этот элемент к списку элементов DataGridView :

CalendarEditingControl cec = new CalendarEditingControl();

...
    cec.Visible = false;
    cec.ValueChanged += new EventHandler(this.cec_ValueChanged);
    cec.EditingControlDataGridView = dataGridView1;
    dataGridView1.Controls.Add(cec);

Будем также показывать на месте редактируемой ячейки наш календарь, инициализируя его старым значением и передавая в ячейку новое значение по завершению редактирования. Тут есть небольшая проблема — что считать завершением редактирования! По этому событию нужно спрятать наш элемент. Я решил, что редактирование завершено при клике или двойном клике в другой ячейке, поэтому в методах обработки этих событий прячу наш cec.Visible = false. А значение в ячейку передается при каждом изменении :

private void cec_ValueChanged(object sender, EventArgs e)
{
    try
    {
         if (dataGridView1.CurrentCell.ColumnIndex == 3)
              dataGridView1.CurrentCell.Value = string.Format("{0:D02}.{1:D02}.{2:D04}", cec.Value.Day, cec.Value.Month, cec.Value.Year);
         if (dataGridView1.CurrentCell.ColumnIndex == 4)
              dataGridView1.CurrentCell.Value = cec.Value;
    }
    catch { }
}

В коде приведена обработка и для даты рождения и для даты приема на работу. Для поля string (дата рождения) возвращаемое значение cec.Value преобразуем в строку по нашему формату. Для поля DateTime (дата приёма на работу) просто присваиваем, так как их типы одинаковые. В методе обработки двойного клика при передаче в календарь старого значения выполним обратное преобразование, вычислим местоположение левого верхнего угла ячейки, установим туда наш cec и покажем его :

if (nc == 3)
{
     if ((dataGridView1.CurrentCell.Value == null) || ((string)dataGridView1.CurrentCell.Value == "")) cec.Value = DateTime.Now;
     else
     {
          string[] sd = ((string)dataGridView1.CurrentCell.Value).Split('.');
          cec.Value = new DateTime(int.Parse(sd[2]), int.Parse(sd[1]), int.Parse(sd[0]));
     }
     cec.Left = dataGridView1.Columns[3].Width * 3 + dataGridView1.RowHeadersWidth;
     cec.Top = dataGridView1.Rows[nr].Height * (dataGridView1.CurrentCell.RowIndex - dataGridView1.FirstDisplayedScrollingRowIndex) + dataGridView1.ColumnHeadersHeight;
     cec.Visible = true;
}

Проблема 01.01.0001 00:00:00

Во время работы нашего приложения может возникнуть ситуация передачи в переменную типа DateTime значения null. Мы решили в cec для инициализации передавать старое значение ячейки и выводить в неё новое значение. Но когда пользователь начинает редактировать даты в новой строке, она создается в момент записи туда значения. Т.е. сама ячейка ещё null и этот null преобразуется в дату <01.01.0001 00:00:00>, которая не входит в диапазон допустимых значений от minValue до maxValue и DateTime выбрасывает соответствующее исключение. Чтобы программа не завершалась аварийно, два варианта решения проблемы приведены в коде выше. Если нет желания бороться с подобными мельницами, то можно запретить в DataGridView добавление новых строк и добавлять в нашем случае сотрудников в отдельной форме, вызываемой по кнопке <Добавить>. А потом уже обновлять вывод нашего списка. Хотя интуитивно понятно начать что-то писать в новой пустой строке снизу, если в самой таблице разрешено редактирование ячеек. Но это уже область дизайна интерфейсов а не собственно разработки.

CalendarEditingControl для редактирования даты приёма на работу

Для редактирования даты приёма на работу ( поле типа DateTime) будем использовать ранее созданный элемент управления Календарь — CalendarEditingControl (cec). Настройки те же. Это поле отображается в DataGridView в 4 считая с нуля столбце. В методе MainFormDGV_Load установим формат отображения даты :

...
    // если знаем, что в 4 столбце будет дата, установим ей формат вывода
    dataGridView1.Columns[4].DefaultCellStyle.Format = "dd.MM.yyyy";
...

С элементом cec по двойному клику работаем аналогично как и для даты рождения :

if (nc == 4)
{
     try
     {
         cec.Value = (DateTime)dataGridView1.CurrentCell.Value;
     }
     catch
     {
         cec.Value = DateTime.Now;
     }
     cec.Left = dataGridView1.Columns[4].Width * 4 + dataGridView1.RowHeadersWidth;
     cec.Top = dataGridView1.Rows[nr].Height * (dataGridView1.CurrentCell.RowIndex - dataGridView1.FirstDisplayedScrollingRowIndex) + dataGridView1.ColumnHeadersHeight;
     cec.Visible = true;
}

С проблемой <01.01.0001 00:00:00> боремся путем обработки исключения и присваивания текущей даты. Редактирование двух ячеек с датами показано разными способами. Возможно, Вам нужно будет реализовать что-то оригинальное и может не хватить стандартной обработки, как в случае с датой приёма на работу.

Загрузка фотографии работника

Для фото сотрудника в классе Person предусмотрено поле типа Image. Где-то на диске есть папка с фотографиями. В моём примере это каталог <Img> там же где находится программа. Используем для выбора файла фото в формате jpg стандартный диалог открытия файла. Размеры фото в файлах могут быть разными. Доверим их подгонку конструктору класса Bitmap, передав ему в качестве параметров ширину и высоту наших ячеек. Выбранный файл будет отображён в редактируемой ячейке. Также по двойному клику можно фото поменять на другое. Код ниже :

if (nc == 6)
{
     openFileDialog1.Filter = "Графические файлы (*.jpg)|*.jpg|Все файлы(*.*)|*.*";
     openFileDialog1.InitialDirectory = Application.StartupPath + "\\img";
     if (openFileDialog1.ShowDialog() == DialogResult.OK)
     {
          string name = openFileDialog1.FileName;
          int w = dataGridView1.Columns[6].Width;
          int h = dataGridView1.RowTemplate.Height;
          Image myImg = new Bitmap(new Bitmap(name), w, h);
          myTestCollection[nr].PersonFoto = myImg;
     }
}

Сохранение в файл и загрузка из файла содержимого таблицы

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

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Windows.Forms;
using System.ComponentModel;

namespace ExampleDataGridView
{
    class SaveLoadData
    {
        //Создание статичного класса позволит использовать методы без объявления его экземпляров
        public static class SaveLoad
        {
            public static string path = Application.StartupPath + "/person.fcs"; //Путь к сохранению. Вы можете использовать любое расширение
            public static string pathCfg = Application.StartupPath + "/ExampleDataGridView.cfg"; //Путь к сохранению файла конфигурации. Вы можете использовать любое расширение
            private static BinaryFormatter formatter = new BinaryFormatter();
            // Создание сериализатора 

            public static void SaveData(BindingList<Person> lt, string nam = "person.fcs")
            {
                string file_path = Application.StartupPath + "/" + nam;
                //MessageBox.Show(file_path);

                FileStream fs = new FileStream(file_path, FileMode.Create);

                formatter.Serialize(fs, lt);

                fs.Close();
            }

            public static BindingList<Person> LoadData(string file_path)
            {
                if (File.Exists(file_path))
                {   //Проверка существования файла сохранения
                    FileStream fs = new FileStream(file_path, FileMode.Open);
                    //Открытие потока

                    BindingList<Person> data = formatter.Deserialize(fs) as BindingList<Person>; //Получение данных

                    fs.Close(); //Закрытие потока

                    return data; //Возвращение данных
                }
                else
                {
                    return new BindingList<Person>();
                }
            }

            public static void SaveCfg(AppCfg cfg, string nam = "ExampleDataGridView.cfg")
            {
                string file_path = Application.StartupPath + "/" + nam;
                //MessageBox.Show(file_path);

                FileStream fs = new FileStream(file_path, FileMode.Create);

                formatter.Serialize(fs, cfg);

                fs.Close();
            }

            public static AppCfg LoadCfg(string file_path)
            {
                if (File.Exists(file_path))
                {   //Проверка существования файла сохранения
                    FileStream fs = new FileStream(file_path, FileMode.Open);
                    //Открытие потока

                    AppCfg cfg = formatter.Deserialize(fs) as AppCfg;
                    //Получение данных

                    fs.Close(); //Закрытие потока

                    return cfg; //Возвращение данных
                }
                else
                {
                    return new AppCfg();
                }
            }
        }
    }
}

Для сохранения передаём объект класса, сохраняем его в бинарном формате в файл. При загрузке обратная операция. Преобразованные из бинарного формата данные преобразуем в объект класса определенного типа и возвращаем его для присваивания переменной класса вызывающей формы. Функции сохранения и загрузки будем вызывать в методах обработки событий нажатия одноименных кнопок.

Минимальный файл конфигурации

Еще один штрих для завершения приложения. Создадим класс для хранения конфигурации нашего приложения всего из двух свойств. Будем сохранять имя файла данных и каталог с фотографиями сотрудников. Чтобы можно было их поменять разработаем маленькую форму настроек приложения. Её будем показывать по нажатию на кнопку настроек. Макет на рис. 3 ниже :

рис. 3 Форма редактирования настроек приложения.

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

[Serializable]
public class AppCfg
{
    public string DirName
    { get; set; }
    public string FileDataName
    { get; set; }
}

Для сериализуемых классов не забываем указать атрибут Serializable при объявлении. Код класса формы редактирования настроек :

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace ExampleDataGridView
{
    public partial class SettingsForm : Form
    {
        public string Namefile
        { get; set; }
        public string Imgdir
        { get; set; }
        public SettingsForm()
        {
            InitializeComponent();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            Namefile = textBox_file.Text;
            Imgdir = textBox_dir.Text;
        }

        private void but_change_Click(object sender, EventArgs e)
        {
            folderBrowserDialog1.SelectedPath = Imgdir;
            folderBrowserDialog1.Description = "Выберите папку с фотографиями персонала";
            if (folderBrowserDialog1.ShowDialog() == DialogResult.OK)
            {
                Imgdir = folderBrowserDialog1.SelectedPath;
                textBox_dir.Text = Imgdir;
            }
        }

        private void SettingsForm_Load(object sender, EventArgs e)
        {
            textBox_dir.Text = Imgdir;
            textBox_file.Text = Namefile;
        }
    }
}

Итоги

Настало время скомпилировать и запустить наше тестовое приложение. Поработаем за подразделение кадров, заполним данные на нескольких сотрудников. Надеюсь у Вас работают реальные люди, а не суслики, как у меня. Результат должен выглядеть примерно как на рис. 4 ниже :

рис. 4 Результат работы приложения

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

  • создание специального класса для хранения экземпляров отображаемых данных — наша модель представления
  • использование для редактирования ячеек стандартного элемента ComboBox
  • использование для редактирования ячеек пользовательского элемента Календарь
  • отображение картинок в ячейках таблицы
  • сериализация данных в файл в бинарном формате
  • файл конфигурации и форма настройки его свойств
  • использование диалоговых окон выбора файла и каталога

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

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *