Доброго времени суток!
В данной статье я опишу создания своих элементов для 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:
Класс Row:
Класс Cell(Для поддержки копирования добавляем интерфейс ICloneable):
Теперь настроим наш основной класс TableCustoms:
Для того, чтобы у нас были полосы прокрутки нужно использовать ScrollableControl, поэтому создадим класс PanelTable наследуем ScrollableControl и помещаем его на Control(в следующем уроке объясню почему создаем два разных контрола, а не используем сразу ScrollableControl):
После этого «Пересобираем проект» элемента и добавляем элемент на форму(в основном проекте):
Теперь проверим некоторые методы нашего элемента:
Запускаем наш проект:
В данной статье я опишу создания своих элементов для 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;
}
}
}
После этого «Пересобираем проект» элемента и добавляем элемент на форму(в основном проекте):
Теперь проверим некоторые методы нашего элемента:
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 = "привет";
}
}
Запускаем наш проект: