Шаг 226.
Microsoft Visual C++ 2010. Язык С/С++.
Компоненты Windows Forms. Компонент Chart (продолжение)

    На этом шаге мы рассмотрим построение нескольких графиков.

    Рассмотрим пример построения построения 2-х графиков в одном компоненте Chart.

    Возьмем классический пример построения графиков функций y=sin(x) и y=cos(x) на заданном промежутке. Внешний вид формы на этапе разработки приведен на рисунке 1.


Рис.1. Внешний вид формы на этапе разработки

    Как видно из рисунка 1, мы убрали все домонстрационные данные с компонента. Для этого воспользовались коллекцией Series и в открывшемся окне редактора выделили серию и нажали кнопку Удалить.

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

// Построение графиков
private: System::Void button1_Click(System::Object^  sender, System::EventArgs^  e) {
	 // Левая граница графика
	 double XMin = Convert::ToDouble(textBox1->Text);
	 // Правая граница графика
	 double XMax = Convert::ToDouble(textBox2->Text);
	 // Шаг графика
	 double Step = Convert::ToDouble(textBox3->Text);
	 // Количество точек графика
	 int count = (int)Math::Ceiling((XMax - XMin) / Step) + 1;
	 // Создаём два словаря для размещения данных
	 Dictionary <double, double> f1 = gcnew Dictionary<double, double>();
	 Dictionary <double, double> f2 = gcnew Dictionary<double, double>();

	 // Расчитываем точки для графиков функции
	 for (double x = XMin; x <= XMax; x += Step)
	 {
		 f1.Add(x, (Math::Sin(x)));
		 f2.Add(x, (Math::Cos(x)));
	 }
			 
	 // Создаём новую область для построения графика
	 ChartArea^ area = gcnew ChartArea();
	 // Даём ей имя (чтобы потом добавлять графики)
	 area->Name = "myGraph";
	 // Задаём левую и правую границы оси X
	 area->AxisX->Minimum = XMin;
	 area->AxisX->Maximum = XMax;
	 // Задаём нижнюю и верхнюю границы оси Y
	 area->AxisY->Minimum = -1.0;
	 area->AxisY->Maximum = 1.0;
	 area->AxisY->Interval = 0.5;
	 // Определяем шаг сетки
	 area->AxisX->MajorGrid->Interval = Step;
	 // Отображение промежуточных линий
	 // area->AxisX->MinorGrid->Enabled = true;
	 area->AxisY->MinorGrid->Enabled = true;
	 // Position указывает (в процентах) положение и 
	 // размер графика относительно родительского контрола
	 area->Position->X = 3;
	 area->Position->Y = 1;
	 area->Position->Height = 90;
	 area->Position->Width = 75;
	 // Добавляем область в диаграмму
	 chart1->ChartAreas->Add(area);
	 // Создаём объект для первого графика
	 Series^ series1 = gcnew Series();
	 // Ссылаемся на область для построения графика
	 series1->ChartArea = "myGraph";
	 // Задаём тип графика - сплайны
	 series1->ChartType = SeriesChartType::Spline;
	 // Указываем ширину линии графика
	 series1->BorderWidth = 3;
	 // Название графика для отображения в легенде
	 series1->LegendText = "y=sin(x)";
	 // Добавляем в список графиков диаграммы
	 chart1->Series->Add(series1);
	 // Аналогичные действия для второго графика
	 Series^ series2 = gcnew Series();
	 series2->ChartArea = "myGraph";
	 series2->ChartType = SeriesChartType::Spline;
	 series2->BorderWidth = 3;
	 series2->LegendText = "y=cos(x)";
	 chart1->Series->Add(series2);
	 // Создаём легенду, которая будет показывать названия
	 Legend^ legend = gcnew Legend();
	 chart1->Legends->Add(legend);
	 // Добавляем вычисленные значения в графики
	 chart1->Series[0]->Points->DataBindXY(f1.Keys, f1.Values);	
	 chart1->Series[1]->Points->DataBindXY(f2.Keys, f2.Values);
 }
Архив проекта можно взять здесь.

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


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

    Прокомментируем интересные моменты приведенного обработчика.

    Понятно, что сначала читаются введенные параметры, определяющие левую, правую границы и шаг изменения аргумента. В строке

  // Количество точек графика
  int count = (int)Math::Ceiling((XMax - XMin) / Step) + 1;
определяется количество точек графика. Мы решили оставить это строку, так как сначала планировалось размещать вычисленные значения в массивах, и для их определения надо знать количество точек. В дальнейшем было решено отказаться от их использования, но эту строку в программе мы оставлили: вдруг кто-то пойдет по этому пути.

    Данные, используемые для построения графика, вы решили хранить в словарях, поэтому в строках:

  // Создаём два словаря для размещения данных
  Dictionary <double, double> f1 = gcnew Dictionary<double, double>();
  Dictionary <double, double> f2 = gcnew Dictionary<double, double>();

  // Расчитываем точки для графиков функции
  for (double x = XMin; x <= XMax; x += Step)
  {
 	 f1.Add(x, (Math::Sin(x)));
	 f2.Add(x, (Math::Cos(x)));
  }
мы сначала описываем два словаря, а затем в цикле заполняем их значениями синусов и косинусов аргумента, который изменяется от XMin до XMax с шагом Step. Таким образом, словарь f1 содержит данные для построения графика функции y=sin(x) (словарь состоит из пар "ключ-значение", где ключом является значение x, а значением - синус данного аргумента). Добавление в словарь осуществляется методом Add().

    После создания области для построения графика и придания ей имени:

  // Создаём новую область для построения графика
  ChartArea^ area = gcnew ChartArea();
  // Даём ей имя (чтобы потом добавлять графики)
  area->Name = "myGraph";
задаем некоторые параметры осей. В нашем примере определяем диапазон значений по осям X и Y (это свойства AxisX и AxisY объекта area), дополнительно еще задаем интервал (Interval), который определяет шаг изменения значений по осям:
  // Задаём левую и правую границы оси X
  area->AxisX->Minimum = XMin;
  area->AxisX->Maximum = XMax;
  // Задаём нижнюю и верхнюю границы оси Y
  area->AxisY->Minimum = -1.0;
  area->AxisY->Maximum = 1.0;
  area->AxisY->Interval = 0.5;
  // Определяем шаг сетки
  area->AxisX->MajorGrid->Interval = Step;

    Далее мы отображаем промежуточные линии (свойство MinorGrid, а свойство MajorGrid используется для задания параметров основных линий) только по оси Y.

  // Отображение промежуточных линий
  // area->AxisX->MinorGrid->Enabled = true;
  area->AxisY->MinorGrid->Enabled = true;

    При построении графика долго не удавалось разместить его во всю ширину компонента Chart, пока мы не воспользовались свойством Position области построения. Его подсвойства X, Y определяют координаты верхнего левого угла области построения графика относительно компонента Chart, а значения свойств Height и Width определяют (в процентах) область, которую будет занимать график:

  // размер графика относительно родительского контрола
  area->Position->X = 3;
  area->Position->Y = 1;
  area->Position->Height = 90;
  area->Position->Width = 75;

    Остальные действия не вызовут вопросов. Единственное, на что следует обратить внимание, это добавление легенды на график: сначала легенда определяется через свойство LegendText каждой серии, а потом создается объект легенты, который добавляется методом Add() в свойство Legends компонента Chart.

    Последние две строки:

  chart1->Series[0]->Points->DataBindXY(f1.Keys, f1.Values);	
  chart1->Series[1]->Points->DataBindXY(f2.Keys, f2.Values);
добавляют данные из словарей в серии: f1.Keys содержит значения x, а f1.Values - значения функции.

    На следующем шаге мы продолжим изучение этого вопроса.




Предыдущий шаг Содержание Следующий шаг