- •Основы работа с графикой на с#
- •Глава 1. Использование графики в Windows приложениях
- •Параграф 2. Создание линейных графиков
- •2.1. В качестве постановки задачи
- •2.2. Постановка задачи
- •2.3. Исходные данные
- •2.4. Проект решения
- •2.5. Конструкторы класса
- •2.6. Создаем объект для рисования
- •2.7. Рисуем оси
- •2.8. Рисуем сетку
- •2.8. Рисуем линию графика
- •2.10. Надписи на графике
- •Параграф 3. Создание гистограмм
- •Параграф 5. Базовый класс для рисования графиков
Параграф 5. Базовый класс для рисования графиков
В заключении главы приводим полностью класс, созданный нами. Вы его можете без труда не только использовать в своих приложениях, но и развивать дальше - включая новые типы диаграмм.
Автор будет благодарен, если кто сможет дополнить класс новыми типами графиков, и пришлет по почте. Любые интересные Ваши находки будут помещены (включены) в данный материал с указанием Вашего участия (авторства).
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Drawing.Drawing2D;
namespace graph1
{
class PaintCl
{
//Основные объекты для рисования
private Bitmap bmp = null;
private Graphics graph = null;
private Font objFont = new Font("Arial", 8, FontStyle.Bold);
private Brush objBrush = Brushes.Black;
private Pen objPenLine = new Pen(Color.Black, 1);
//Размеры холста
private int viX = 200;
private int viY = 100;
//Параметры для рисования круговой диаграммы
private float vfDiamX = 100;
private float vfDiamY = 100;
private float vfXcirc = 100;
private float vfYcirc = 100;
//Массив предопределенных цветов для отображения легенды
private Color[] color ={Color.LightGreen,Color.Chartreuse,Color.LimeGreen,
Color.Green,Color.DarkGreen,Color.DarkOliveGreen,
Color.LightPink,Color.LightSeaGreen,Color.LightCoral,
Color.DarkCyan,Color.Crimson,Color.CornflowerBlue,
Color.Chocolate,Color.CadetBlue,Color.BlueViolet,
Color.Maroon,Color.Blue,Color.Brown,Color.DarkBlue,
Color.Red,Color.Coral,Color.DarkRed, Color.DarkMagenta,
Color.DarkOrange,Color.DarkOrchid};
//Массив предопределенных цветов для отображения круговой диаграммы и гистограммы
private Brush[] br = {Brushes.LightGreen,Brushes.Chartreuse,Brushes.LimeGreen,
Brushes.Green,Brushes.DarkGreen,Brushes.DarkOliveGreen,
Brushes.LightPink,Brushes.LightSeaGreen,Brushes.LightCoral,
Brushes.DarkCyan,Brushes.Crimson,Brushes.CornflowerBlue,
Brushes.Chocolate,Brushes.CadetBlue,Brushes.BlueViolet,
Brushes.Maroon, Brushes.Blue,Brushes.Brown,Brushes.DarkBlue,
Brushes.Red,Brushes.Coral,Brushes.DarkRed,
Brushes.DarkMagenta, Brushes.DarkOrange,Brushes.DarkOrchid};
//Массив значений для рисования графика
private string[,] rgsValues = null;
//Размер массива
private int viMaxRg = 20;
//Отступы от краев холста
private int viDeltaaxisL = 50;
private int viDeltaaxisR = 50;
private int viDeltaaxisH = 20;
#region Конструктор
public PaintCl(int a, int b)
{
bmp = new Bitmap(a, b);
graph = Graphics.FromImage(bmp);
viX = a;
viY = b;
}
#endregion
#region Установка цвета фона диаграммы
public void vSetBackground(Color bcl)
{
graph.Clear(bcl);
}
#endregion
#region Доступ к переменным класса
public Bitmap Bmp
{
get { return bmp; }
}
public int MaxRg
{
set { viMaxRg = value; }
}
public string[,] RgValue
{
set { rgsValues = value; }
}
public Font font
{
set { objFont = value; }
}
public Brush brush
{
set { objBrush = value; }
}
#endregion
#region Карандаш, шрифт, кисть
//Цвет карандаша
public void vSetPenColorLine(Color pcl)
{
if (objPenLine == null)
{
objPenLine = new Pen(Color.Black, 1);
}
objPenLine.Color = pcl;
}
//Установка толщина карандаша
public void vSetPenWidthLine(int penwidth)
{
if (objPenLine == null)
{
objPenLine = new Pen(Color.Black, 1);
}
objPenLine.Width = penwidth;
}
#endregion
#region Рисование Осей
//Отступ от левого края - deltaaxis, отступ от правого края - deltaaxisR,
//отступ от нижнего и верхнего края - deltaaxisH, цвет оси - colorpenaxis,
//толщина пера для оси - widthpen, наличие стрелок на оси - fArrow(true)
public void vDravAxis(int deltaaxisL, int deltaaxisR, int deltaaxisH,
Color colorpenaxis,int widthpen, bool fArrow)
{
//Запоминаем отступы
viDeltaaxisL = deltaaxisL;
viDeltaaxisR = deltaaxisR;
viDeltaaxisH = deltaaxisH;
//Запоминаем цвет осей и толщину
vSetPenColorLine(colorpenaxis);
if (widthpen > 0) vSetPenWidthLine(widthpen);
//Точка начала рисования по х и y
int x = deltaaxisL;
int y = viY - deltaaxisH;
int x1 = viX - deltaaxisR;
int y1 = deltaaxisH;
int d = 0;
if (fArrow) d = widthpen * 10;
//Оси на d пикселей длинней для стрелок
graph.DrawLine(objPenLine, x, y, x1 + d, y);
graph.DrawLine(objPenLine, x, y, x, y1 - d);
if (fArrow)
{
int a = 10 * (int)objPenLine.Width;
int b = 2 * (int)objPenLine.Width;
int x2 = x1 - a;
int y2 = y + b;
//Стрелки
graph.DrawLine(objPenLine, x1 + 20, y, x2 + d, y2);
y2 = y - b;
graph.DrawLine(objPenLine, x1 + 20, y, x2 + d, y2);
x2 = x - b;
y2 = y1 + a;
graph.DrawLine(objPenLine, x, y1 - d, x2, y2 - d);
x2 = x + b;
graph.DrawLine(objPenLine, x, y1 - d, x2, y2 - d);
}
}
#endregion
#region Рисование сетки
public void vDravGrid()
{
float x = viDeltaaxisL;
float y = viY - viDeltaaxisH;
float x1 = viX - viDeltaaxisR;
float y1 = viDeltaaxisH;
float f = (y - y1) / (float)viMaxRg;
for (int i = 1; i < viMaxRg + 1; i++)
{
graph.DrawLine(objPenLine, x, y - f * i, x1, y - f * i);
}
f = (x - x1) / (float)(viMaxRg - 1);
for (int i = 1; i < viMaxRg; i++)
{
graph.DrawLine(objPenLine, x - f * i, y, x - f * i, y1);
}
}
#endregion
#region Рисование линий графика для линейного графика
public void vDrawGraphLines()
{
string s = string.Empty;
string s1 = string.Empty;
string s2 = string.Empty;
float f = 0;
float f1 = 0;
float x1 = 0;
float x = viDeltaaxisL;//-objPenLine.Width;
float y = viY - viDeltaaxisH;// +objPenLine.Width;
float x2 = 0;
float fMax = float.MinValue;
//float fMin = int.MaxValue;
for (int i = 0; i < viMaxRg; i++)
{
s = rgsValues[i, 0];
if (fMax < float.Parse(s)) fMax = float.Parse(s);
}
//Пикселей для рисования по оси х
float fdeltax = viX - viDeltaaxisL - viDeltaaxisR;
//Пикселей на один отсчет
fdeltax = fdeltax / (float)(viMaxRg - 1);
//Пикселей для рисования по оси y
float fdeltay = viY - 2 * viDeltaaxisH;
//Пикселей на одну единицу массива значений
fdeltay = fdeltay / fMax;
for (int i = 0; i < viMaxRg; i++)
{
//Первый раз запоминаем точку старта
if (i == 0)
{
s = rgsValues[i, 0];
s2 = rgsValues[i, 1];
f = y - (float.Parse(s) * fdeltay);
x1 = x;
}
else
{
s1 = rgsValues[i, 0];
f1 = y - (float.Parse(s1) * fdeltay);
x2 = x + (int)(fdeltax * i);
graph.DrawLine(objPenLine, x1, f, x2, f1);
s = rgsValues[i, 0];
s2 = rgsValues[i, 1];
f = f1;
x1 = x + (int)(i * fdeltax);
}
}
}
#endregion
#region Текст по оси X - цифры
//Параметр = false - без сдвига по оси Y, true - со здвигом по оси Y
public void vDrawTextAxXNumber(bool f)
{
//Пикселей для надписей по оси х
float fdeltax = viX - viDeltaaxisL - viDeltaaxisR;
//Пикселей на один отсчет
fdeltax = fdeltax / (float)(viMaxRg - 1);
float x = viDeltaaxisL;
float y = viY - viDeltaaxisH + objPenLine.Width;
for (int i = 1; i < viMaxRg + 1; i++)
{
if (!f || i % 2 == 0)
{
graph.DrawString(Convert.ToString(i), objFont,
objBrush, x + (i - 1) * fdeltax, y);
}
else
{
graph.DrawString(Convert.ToString(i), objFont,
objBrush, x + (i - 1) * fdeltax, y + objFont.Size);
}
}
}
#endregion
#region Текст по оси X
//Параметр = false - без сдвига по оси Y, true - со здвигом по оси Y
public void vDrawTextAxXValues(bool f)
{
string s = string.Empty;
//Пикселей для надписей по оси х
float fdeltax = viX - viDeltaaxisL - viDeltaaxisR;
//Пикселей на один отсчет
fdeltax = fdeltax / (float)(viMaxRg - 1);
float x = viDeltaaxisL;
float y = viY - viDeltaaxisH;// +objPenLine.Width;
for (int i = 0; i < viMaxRg; i++)
{
if (!f || i % 2 == 0)
{
graph.DrawString(rgsValues[i, 1], objFont, objBrush, x + i * fdeltax, y);
}
else
{
graph.DrawString(rgsValues[i, 1], objFont,
objBrush, x + i * fdeltax, y + objFont.Size);
}
}
}
#endregion
#region Текст по оси Y - Значения по сетке
public void vDrawTextAxYValues()
{
string s = string.Empty;
float f = 0;
float fMax = float.MinValue;
for (int i = 0; i < viMaxRg; i++)
{
s = rgsValues[i, 0];
if (fMax < float.Parse(s)) fMax = float.Parse(s);
}
f = fMax / (float)(viMaxRg - 1);
//Пикселей для надписей по оси х
float fdeltay = viY - 2 * viDeltaaxisH;
//Пикселей на один отсчет
fdeltay = fdeltay / (float)(viMaxRg - 1);
float y = viY - viDeltaaxisH - objFont.Size;
for (int i = 0; i < viMaxRg; i++)
{
graph.DrawString(((float)(i * f)).ToString("0.00"), objFont,
objBrush, viDeltaaxisL - (objFont.Size) * 5 - 5, y - i * fdeltay);
}
}
#endregion
//*****************
#region Надписи по оси Y - Значения над точкой
public void vDrawTextAxYValuesPoint(bool a, bool b)
{
string s = string.Empty;
float fMax = float.MinValue;
float fSum = 0;
for (int i = 0; i < viMaxRg; i++)
{
s = rgsValues[i, 0];
fSum += float.Parse(s);
if (fMax < float.Parse(s)) fMax = float.Parse(s);
}
//Пикселей для надписей по оси х
float fdeltax = viX - viDeltaaxisL - viDeltaaxisR;
//Пикселей на один отсчет по х
fdeltax = fdeltax / (float)(viMaxRg - 1);
float x = viDeltaaxisL;
float fdeltay = viY - 2 * viDeltaaxisH;
float y = viY - viDeltaaxisH - objFont.Size;
//Пикселей на одну единицу
fdeltay = fdeltay / fMax;
float fdelta = 0;
for (int i = 0; i < viMaxRg; i++)
{
if (a)
{
if (i % 2 == 0) fdelta = objFont.Size;
else fdelta = 2 * objFont.Size;
}
else
{
fdelta = objFont.Size;
}
if (b)
{
graph.DrawString(rgsValues[i, 0], objFont, objBrush, x + i * fdeltax,
y - (float.Parse(rgsValues[i, 0]) * fdeltay) - fdelta);
}
else
{
float fp = float.Parse(rgsValues[i, 0]);
fp = (fp * 100) / fSum;
graph.DrawString(rgsValues[i, 0] + "-" + fp.ToString("0.0") + "%",
objFont, objBrush, x + i * fdeltax,
y - (float.Parse(rgsValues[i, 0]) * fdeltay) - fdelta);
}
}
}
#endregion
#region Рисование графика для графика прямоугольниками
public void vDrawGraphRectangular(int a, int c)
{
string s = string.Empty;
string s1 = string.Empty;
string s2 = string.Empty;
float f = 0;
float x1 = 0;
float x = viDeltaaxisL;
float y = viY - viDeltaaxisH;
float fMax = float.MinValue;
for (int i = 0; i < viMaxRg; i++)
{
s = rgsValues[i, 0];
if (fMax < float.Parse(s)) fMax = float.Parse(s);
}
//Пикселей для рисования по оси х
float fdeltax = viX - viDeltaaxisL - viDeltaaxisR;
//Пикселей на один отсчет
fdeltax = fdeltax / (float)(viMaxRg - 1);
//Пикселей для рисования по оси y
float fdeltay = viY - 2 * viDeltaaxisH;
//Пикселей на одну единицу массива значений
fdeltay = fdeltay / fMax;
float fdx = 0;
if (c != 0) fdx = (fdeltax * c / 100) / 2;
Random rand = new Random(DateTime.Now.Millisecond);
int arn = rand.Next((int)br.Length);
objBrush = br[arn];
for (int i = 0; i < viMaxRg - 1; i++)
{
s = rgsValues[i, 0];
f = float.Parse(s);
x1 = x + ((float)i * fdeltax);
if (a == 0)
{
graph.FillRectangle(objBrush, x1 + fdx, y - fdeltay * f, fdeltax - 2 * fdx, fdeltay * f);
}
else
{
int b = i % br.Length;
graph.FillRectangle(br[b], x1 + fdx, y - fdeltay * f, fdeltax - 2 * fdx, fdeltay * f);
}
if (i == viMaxRg - 2)
{
int b = (i + 1) % br.Length;
s = rgsValues[i + 1, 0];
f = float.Parse(s);
x1 = x + ((float)(i + 1) * fdeltax);
if (a == 0)
{
graph.FillRectangle(objBrush, x1 - 2, y - fdeltay * f, 4/*fdeltax*/, fdeltay * f);
}
else
{
graph.FillRectangle(br[b], x1 - 2, y - fdeltay * f, 4/*fdeltax*/, fdeltay * f);
}
}
}
}
#endregion
#region vDravCircle3D
//Параметры - Отступ от краев по X слева deltaaxisL, от краев по Y справа,
//deltaaxisH - отступа сверху и снизу, толщина диаграммы viH, сдвиг сектора viDx, viDy
public void vDravCircle3D(int deltaaxisL, int deltaaxisR, int deltaaxisH, int viH, int viDx, int viDy)
{
//Запоминаем отступы
viDeltaaxisL = deltaaxisL;
viDeltaaxisR = deltaaxisR;
viDeltaaxisH = deltaaxisH;
float a = viX - (deltaaxisL + deltaaxisR);
//Нужен ли выброс сектора
int viMov = 1;
if (viDx == 0 && viDy == 0)
{
viMov = 0;
}
//Запоминаем диаметр
vfDiamX = a;
vfDiamY = viY - 2 * viDeltaaxisH;
//Запоминаем центр элипса
vfXcirc = deltaaxisL + a / 2;
vfYcirc = viY / 2;
graph.SmoothingMode = SmoothingMode.AntiAlias;
float fSum = 0;
string s = string.Empty;
for (int i = 0; i < viMaxRg; i++)
{
s = rgsValues[i, 0];
fSum += float.Parse(s);
}
float f = 0;
float fBSum = 0;
float fDeltaGrad = (fSum / (float)360);
SolidBrush objBrush = new SolidBrush(Color.Aqua);
Random rand = new Random(DateTime.Now.Millisecond);
float[] frgZn = new float[viMaxRg];
float[] frgSumGr = new float[viMaxRg];
for (int i = 0; i < viMaxRg; i++)
{
s = rgsValues[i, 0];
frgZn[i] = float.Parse(s);
if (i == 0) frgSumGr[i] = 0;
else frgSumGr[i] = frgZn[i] + frgSumGr[i - 1];
}
//Рисуем диаграмму против часовой стрелки со штриховкой до 90градусав по часовой
//Штриховка убирается каждым следующим сектором от сектора предыдущего
//В первом нарисованном секторе она сохраняетсяна случай сдвига первого по часовой стрелке
for (int i = viMaxRg - 1; i >= 0; i--)
{
if (i != viMaxRg - 1 && fBSum < 90) break;
//f в градусах fBSum в градусах
f = frgZn[i] / fDeltaGrad;
//fBSum = frgSumGr[i] / fDeltaGrad;
if (i == viMaxRg - 1)
{
fBSum = 360 - f;
}
else
{
fBSum -= f;
}
//Для цвета
int j = i % br.Length;
float k = f;
if (f < 1) k = 1;
//objBrush.Color = Color.FromArgb(rand.Next(255), rand.Next(255), rand.Next(255));
if (i != 0)
{
if ((fBSum > 90 && fBSum < 180) || i == viMaxRg - 1)
{
for (int d = 0; d < viH; d++)
{
graph.FillPie(new HatchBrush(HatchStyle.Percent25, color[j]/*objBrush.Color*/),
vfXcirc - a / 2, vfYcirc - vfDiamY / 2 + d,
vfDiamX, vfDiamY, fBSum, k);
}
}
objBrush.Color = color[j];
graph.FillPie(objBrush, vfXcirc - a / 2, vfYcirc - vfDiamY / 2,
vfDiamX, vfDiamY, fBSum, k);
}
}
//Рисуем до 90градусов без первого сегмента в случае необходимости
//сдвига первого по часовой стрелке
fBSum = 0;
for (int i = viMov; i < viMaxRg; i++)
{
//f в градусах fBSum в градусах
f = frgZn[i] / fDeltaGrad;
if (i == 1)
{
fBSum = frgZn[0] / fDeltaGrad;
}
//Для цвета
int j = i % br.Length;
float k = f;
if (f < 1) k = 1;
if (fBSum < 90)
{
for (int d = 0; d < viH; d++)
{
graph.FillPie(new HatchBrush(HatchStyle.Percent25, color[j]), vfXcirc - a / 2,
vfYcirc - vfDiamY / 2 + d,
vfDiamX, vfDiamY, fBSum, k);
}
objBrush.Color = color[j];
graph.FillPie(objBrush, vfXcirc - a / 2, vfYcirc - vfDiamY / 2,
vfDiamX, vfDiamY, fBSum, k);
}
else
{
break;
}
fBSum += f;
}
//Рисуем сдвинутый сектор при необходимости
if (viMov == 1)
{
f = frgZn[0] / fDeltaGrad;
fBSum = 0;
float k1 = f;
if (f < 1) k1 = 1;
for (int d = 0; d < viH; d++)
{
graph.FillPie(new HatchBrush(HatchStyle.Percent25, color[0]),
vfXcirc - a / 2 + viDx, vfYcirc - vfDiamY / 2 + d - viDy,
vfDiamX, vfDiamY, fBSum, k1);
}
objBrush.Color = color[0];
graph.FillPie(objBrush, vfXcirc - a / 2 + viDx, vfYcirc - vfDiamY / 2 - viDy,
vfDiamX, vfDiamY, fBSum, k1);
}
}
#endregion
#region vDravTextCircle
public void vDravTextCircle1(bool vfGde)
{
float fSum = 0;
string s = string.Empty;
for (int i = 0; i < viMaxRg; i++)
{
s = rgsValues[i, 0];
fSum += float.Parse(s);
}
float f = 0;
float fBSum = 0;
float f1Radian = (float)Math.PI / 180;
float fDeltaGrad = fSum / 360;
for (int i = 0; i < viMaxRg; i++)
{
s = rgsValues[i, 0];
f = float.Parse(s);
//f в градусах
f = f / fDeltaGrad;
int j = i % br.Length;
//Угол в радианах
float fRad = (f + fBSum) * f1Radian;
float fty = 0;
float ftx = 0;
float fSin = (float)Math.Sin((360 - (f / 2 + fBSum)) * f1Radian);
float fCos = (float)Math.Cos((360 - (f / 2 + fBSum)) * f1Radian);
float c = (float)Math.Sqrt((vfDiamX / 2 * vfDiamX / 2 * vfDiamY / 2 * vfDiamY / 2) /
(vfDiamY / 2 * vfDiamY / 2 * fCos * fCos + vfDiamX
/ 2 * vfDiamX / 2 * fSin * fSin));
c -= 3 * objFont.Size;
if (c < 0) c = 0;
ftx = c * fCos;
fty = c * fSin;
ftx = vfXcirc + ftx;
fty = vfYcirc - fty;
if (vfGde)
{
graph.DrawString(Convert.ToString(i + 1), objFont, objBrush, ftx, fty);
}
else
{
graph.DrawString(rgsValues[i, 0], objFont, objBrush, ftx, fty);
}
fBSum += f;
}
}
#endregion
#region Смена шрифта
private void vSetFont(string name, float size, bool bold)
{
if (objFont != null) objFont = null;
if (bold)
{
objFont = new Font(name, size, FontStyle.Bold);
}
else
{
objFont = new Font(name, size);
}
}
#endregion
#region Текст легенды
public void vDravTextKeyCircle(bool vfGde)
{
float fSum = 0;
float f = 0;
string s = string.Empty;
for (int i = 0; i < viMaxRg; i++)
{
s = rgsValues[i, 0];
fSum += float.Parse(s);
}
//Сдвиг от круговой диаграммы
float vfSdvig = vfXcirc + vfDiamX / 2;
vfSdvig += (viX - vfSdvig) / 5;
//Высота места для легенды
//На одну строку по высоте отводится - +1 на заголовок
float vfHg = viY / (viMaxRg + 2);
vSetFont("Arial", 12, true);
if (viMaxRg > 100)
{
graph.DrawString("Легенда не может быть размещена",
objFont, Brushes.DarkBlue, vfSdvig + (viX - vfSdvig) / 10, objFont.Size);
}
else
{
//Шрифт в 2 раза меньше места на строку надписи
if (viMaxRg > 15)
{
vSetFont("Arial", (vfHg / 2), true);
}
else
{
if (viMaxRg > 10)
{
vSetFont("Arial", (vfHg / 3), true);
}
else
{
vSetFont("Arial", (vfHg / 6), true);
}
}
if (vfGde)
{
graph.DrawString("Пояснения к графику", objFont,
Brushes.DarkBlue, vfSdvig /*+ (viX - vfSdvig) / 10*/, objFont.Size);
}
else
{
graph.DrawString("Пояснения к графику", objFont,
objBrush, vfSdvig/* + (viX - vfSdvig) / 10*/, objFont.Size);
}
if (viMaxRg > 15)
{
vSetFont("Arial", (vfHg / 2) + 1, true);
}
else
{
if (viMaxRg > 10)
{
vSetFont("Arial", (vfHg / 4) + 1, true);
}
else
{
vSetFont("Arial", (vfHg / 7) + 1, true);
}
}
for (int i = 0; i < rgsValues.Length / 2; i++)
{
Brush brTxt = null;
int j = i % br.Length;
if (vfGde) brTxt = br[j];
else brTxt = objBrush;
graph.DrawString(Convert.ToString(i + 1), objFont, brTxt, vfSdvig, vfHg * (i + 2));
f = float.Parse(rgsValues[i, 0]);
f = (f * 100) / fSum;
graph.DrawString(rgsValues[i, 0], objFont,
brTxt, vfSdvig + 1 * (viX - vfSdvig) / 5, vfHg * (i + 2));
graph.DrawString(f.ToString("0.0") + "%", objFont,
brTxt, vfSdvig + 2 * (viX - vfSdvig) / 5, vfHg * (i + 2));
graph.DrawString(rgsValues[i, 1], objFont,
brTxt, vfSdvig + 3 * (viX - vfSdvig) / 5, vfHg * (i + 2));
}
}
}
#endregion
}
}
