Создание собственного элемента на примере таблицы на C# для Windows Form

от автора

Доброго времени суток!

В данной статье я опишу создания своих элементов для C# Windows Form.

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

Для написания будем использовать .Net FrameWork 4.7.x, среда разработки Visual Studio 2019.

В первую очередь создадим обычный проект Windows Form. Думаю не нужно это показывать. А уже потом создаем проект «Библиотека элементов управления Windows Form»(Назовем его CustomControl).

Далее у нас будет создан файл UserControl.cs. Удаляем его и создаем обычный класс TableCustoms.cs.Наш класс будет наследоваться от класса Control.

Далее в этом же файле создадим еще несколько классов, а именно:Column,Row,Cell. Рассмотрим каждый по отдельности. Начнем с Column:

    [Serializable]     public class Column     {         public string Name { get; set; } = "NameColumn";//Наименование Столбца          public string Caption { get; set; } = "CaptionColumn";//Текст заголовка         public int Width { get; set; } = 100;//Стандартная ширина         public Color Back { get; set; } = Color.White;//Цвет фона                  public Column()         {                   }     } 

Класс Row:

    [Serializable]     public class Row     {         public int Heigth { get; set; } = 20;// Высота строки         public List<Cell> Cells { get; set; } = new List<Cell>();//список ячеек         public Row()         {           }         public Row(List<Cell> cells)         {             Cells = cells;         }      } 

Класс Cell(Для поддержки копирования добавляем интерфейс ICloneable):

    [Serializable]     public class Cell : ICloneable     {         public object Value { get; set; } = null;//значение ячейки          public Cell()         {         }         public object Clone()         {             return MemberwiseClone();         }     } 

Теперь настроим наш основной класс TableCustoms:

   public class TableCustoms : Control     {         #region Перемененные         public ObservableCollection<Column> Columns { get; set; } = new ObservableCollection<Column>();//Список столбцов таблицы         private ObservableCollection<Row> rows = new ObservableCollection<Row>();//Список строк         private int countRow = 0;//количество строк         #endregion         #region Свойства         public int CountRow // гетер и сетер при увеличении переменной на N раз         {             get { return countRow; }             set             {                 //При увеличении добавляем                  if (value > countRow)                 {                     int iteration = value - countRow;                     for (int i = 0; i < iteration; i++)                     {                         rows.Add(new Row());                     }                 }                 //при уменьшении удаляем с конца                 if (value < countRow)                 {                     int iteration = countRow - value;                     for (int i = 0; i < iteration; i++)                     {                         rows.Remove(rows[rows.Count - 1]);                     }                 }                  countRow = value;             }         }         //гетер и сетер для списка строк, будет использоваться позже         public ObservableCollection<Row> Rows         {             get { return rows; }             set { }         }          public int ColumnHeaderHeigth { get; set; } = 20;//высота шапки таблицы         public int RowHeaderWidth { get; set; } = 20;//высота заголовков строк         public Color ColumnHeaderBack { get; set; } = SystemColors.Control;//Основной цвет фона заголовков таблицы         public Color BorderColor { get; set; } = Color.Black;//Стандартный цвет границ таблицы         public bool NumerableRows { get; set; } = false;//Флаг автоматической нумерации         #endregion         //Метода изменения столбцов, будет использоваться в следующем уроке         private void EditColumn()         {          }         //Метод изменения строк         private void EditRows()         {             if (countRow < rows.Count)//Увеличение количества строк             {                 rows[rows.Count - 1].Cells = CreatCells(Columns.Count);//Добавление пустых ячеек в строку                 countRow++;             }             if (CountRow > rows.Count)//уменьшение количества строк             {                 countRow--;             }          }         //метод создания N количества ячеек         private List<Cell> CreatCells(int Count)         {             // return Enumerable.Repeat(new Cell(), Count).ToList();             List<Cell> result = new List<Cell>();             for (int i = 0; i < Count; i++)             {                 result.Add(new Cell());             }             return result;         }         public TableCustoms()         {             rows.CollectionChanged += (e, v) => EditRows();//проверка изменения списка             Columns.CollectionChanged += (e, v) => EditColumn();//проверка изменения списка             BackColor = SystemColors.AppWorkspace;//Стандартный фон             PanelTable panelTable = new PanelTable(this);//Создание основной панели             panelTable.Dock = DockStyle.Fill;//Растягиваем основную панель по Control             Controls.Add(panelTable);//Добавление панели на Control         }     } 

Для того, чтобы у нас были полосы прокрутки нужно использовать ScrollableControl, поэтому создадим класс PanelTable наследуем ScrollableControl и помещаем его на Control(в следующем уроке объясню почему создаем два разных контрола, а не используем сразу ScrollableControl):

 internal class PanelTable : ScrollableControl//Control со ScrolLbar         {             private TableCustoms BParent;//переменная основного класса, для работы с свойствами             public PanelTable(TableCustoms bParent)             {                 HScroll = true;//Отображение ползунка по горизонтали                 VScroll = true;//Отображение ползунка по вертикали                 AutoScroll = true;//Автоматическое появление полос прокрутки                 BParent = bParent;             }             //переопределение метода             protected override void OnPaint(PaintEventArgs e)             {                 Matrix m = new Matrix();                 m.Translate(this.AutoScrollPosition.X, this.AutoScrollPosition.Y, MatrixOrder.Append);                 e.Graphics.Transform = m;                 Graphics graf = e.Graphics;                 int maxWidth = 0;//Высота AutoScrollMinSize                 int maxHeight = 0;//Ширина AutoScrollMinSize                                   //расчитываем ширину                 foreach (Column item in BParent.Columns)                 {                     maxWidth += item.Width;                 }                 //расчитываем высоту                 foreach (Row item in BParent.Rows)                 {                     maxHeight += item.Heigth;                 }                 AutoScrollMinSize = new Size(maxWidth + 100, maxHeight + 100);//назначаем AutoScrollMinSize относительно этого будут появляться полосы прокрутки                 graf.Clear(BParent.BackColor);                 DrawHeaderColumns(graf);//Отрисовка заголовков столбцов таблицы                 DrawHeaderRows(graf);//Отрисовка заголовков строк таблицы                 DrawCells(graf);//Отрисовка ячеек                 base.OnPaint(e);             }             /// <summary>             /// Отрисока заголовков столбцов             /// </summary>             /// <param name="graf"></param>             private void DrawHeaderColumns(Graphics graf)             {                 int x = 2;                 Rectangle rect;                 rect = new Rectangle(x, 1, BParent.RowHeaderWidth, BParent.ColumnHeaderHeigth);                 graf.DrawRectangle(new Pen(BParent.BorderColor), rect);                 graf.FillRectangle(new SolidBrush(BParent.ColumnHeaderBack), rect);                 x += BParent.RowHeaderWidth + 1;                 foreach (Column item in BParent.Columns)                 {                     rect = new Rectangle(x, 1, item.Width, BParent.ColumnHeaderHeigth);                     graf.DrawRectangle(new Pen(BParent.BorderColor), rect);                     graf.FillRectangle(new SolidBrush(BParent.ColumnHeaderBack), rect);                     if (item.Caption.Length != 0)                     {                         StringFormat sf = new StringFormat();                         sf.Alignment = StringAlignment.Center;                         sf.LineAlignment = StringAlignment.Center;                         graf.DrawString(item.Caption, new Font("Times", 9), Brushes.Black, rect, sf);                     }                     x += item.Width + 1;                 }             }             //Отрисовка заголовков строк             private void DrawHeaderRows(Graphics graf)             {                 int y = 1;                 int i = 0;                 Rectangle rect;                 y += BParent.RowHeaderWidth + 1;                 foreach (Row item in BParent.Rows)                 {                     rect = new Rectangle(2, y, BParent.RowHeaderWidth, item.Heigth);                     graf.DrawRectangle(new Pen(BParent.BorderColor), rect);                     graf.FillRectangle(new SolidBrush(BParent.ColumnHeaderBack), rect);                     if (BParent.NumerableRows)                     {                         StringFormat sf = new StringFormat();                         sf.Alignment = StringAlignment.Center;                         sf.LineAlignment = StringAlignment.Center;                         graf.DrawString(i.ToString(), new Font("Times", 9), Brushes.Black, rect, sf);                     }                     i++;                     y += item.Heigth + 1;                 }             }             //отрисовка ячеек             private void DrawCells(Graphics graf)             {                  int x = 2 + BParent.RowHeaderWidth + 1;                 int y = 2 + BParent.ColumnHeaderHeigth;                 Rectangle rect;                 int i = 0;                 foreach (Row itemRow in BParent.Rows)                 {                     foreach (Column itemColumn in BParent.Columns)                     {                         rect = new Rectangle(x, y, itemColumn.Width, itemRow.Heigth);                         graf.DrawRectangle(new Pen(BParent.BorderColor), rect);                         graf.FillRectangle(new SolidBrush(Color.White), rect);                         if (itemRow.Cells[i].Value != null)                         {                             StringFormat sf = new StringFormat();                             sf.Alignment = StringAlignment.Center;                             sf.LineAlignment = StringAlignment.Center;                             graf.DrawString(itemRow.Cells[i].Value.ToString(), new Font("Times", 9), Brushes.Black, rect, sf);                         }                         x += itemColumn.Width + 1;                         i++;                     }                     i = 0;                     y += itemRow.Heigth + 1;                     x = 2 + BParent.RowHeaderWidth + 1;                 }             }         } 

После этого «Пересобираем проект» элемента и добавляем элемент на форму(в основном проекте):

image

Теперь проверим некоторые методы нашего элемента:

  public partial class Form1 : Form     {         public Form1()         {             InitializeComponent();              tableCustoms1.Rows.Add(new Row());//добавление строки              tableCustoms1.Rows.Add(new Row());              tableCustoms1.Rows[0].Cells[0].Value = "1";//Изменение значения ячейки              tableCustoms1.Rows[1].Cells[1].Value = "2";             tableCustoms1.CountRow++;//увеличение числа строк             tableCustoms1.Rows[0].Cells[0].Value = "привет";         }     } 

Запускаем наш проект:

image

ссылка на оригинал статьи https://habr.com/ru/post/487616/


Комментарии

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

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