БлогNot. Visual С++: 3 примера для вывода текста на канву

Visual С++: 3 примера для вывода текста на канву

Во всех примерах на единственной форме приложения с именем MyForm размещён только pictureBox1, выровненный по клиентской части формы (Dock=Fill).

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

1. Рисуем прямоугольники и текст, выровненный по центру
using namespace System::Drawing::Drawing2D;
using namespace System::Drawing::Imaging;
int w = this->pictureBox1->Size.Width,
    h = this->pictureBox1->Size.Height;
Bitmap^ bmp = gcnew Bitmap(w, h, PixelFormat::Format32bppArgb);
Graphics^ grfx = Graphics::FromImage(bmp);

SolidBrush^ backBrush = gcnew SolidBrush(Color::Black);
SolidBrush^ frontBrush = gcnew SolidBrush(Color::Yellow);
SolidBrush^ fontBrush = gcnew SolidBrush(Color::Blue);
System::Drawing::Font^ drawFont = gcnew System::Drawing::Font("Verdana", 20);

Rectangle client = Rectangle(0, 0, w, h);
Rectangle rect = Rectangle(100, 100, w-200, h-200);

grfx->FillRectangle(backBrush, client);
grfx->FillRectangle(frontBrush, rect);
		
StringFormat ^ stringFormat = gcnew StringFormat();
stringFormat->Alignment = StringAlignment::Center;
stringFormat->LineAlignment = StringAlignment::Center;

grfx->DrawString("Hello, world!", drawFont, fontBrush, w/2, h/2, stringFormat);

pictureBox1->Image = bmp;

Здесь хорошо виден общий подход к рисованию - получить размеры области рисования из PictureBox, создать Bitmap нужного размера и взять из него канву Graphics, отрисовать на ней нужные объекты, после отрисовки назначить этот существующий только в памяти Bitmap в качестве рисунка, отображаемого на компоненте.

2. Перенос текста по строкам при горизонтальном и вертикальном выравнивании по центру

Как небольшое дополнение к первому примеру. Если хотим переносить текст в заданному прямоугольнике, делаем так:

System::Drawing::Font ^ font2 = gcnew System::Drawing::Font("Arial", 20, FontStyle::Bold, GraphicsUnit::Point);
TextFormatFlags flags = TextFormatFlags::HorizontalCenter |	TextFormatFlags::VerticalCenter | TextFormatFlags::WordBreak;
TextRenderer::DrawText(grfx, "Hello, world, i am long string!", font2, rect, Color::Blue, flags);

Конечно, текст может и "не влезть".

3. Рисуем цифры как в почтовом индексе или на электронных часах

Пример разработки законченного приложения, связанного с отрисовкой на канве.

Цифры придётся нарисовать, так как готовый шрифт подберём вряд ли. Максимум одна цифра может состоять из 8 линий, занумеруем линии как на схеме:

.
     0
   -----
  |   / | 
 1| 2/  |3
  |-/-4-|
  |   / | 
 5|  /6 |7
  | /   |
   -----
     8

В этой программе рисование активизируется просто по таймеру, который мы тоже создадим программно в обработчике события Load формы, перед этим установим размеры формы 100x300 пикселов и вызовем метод make_Tmpl, который запомнит их в свойстве client формы (введём это свойство, чтобы сохранить координаты прямоугольника, предназначенного для вывода цифр):

private: Rectangle client;
private: void make_Tmpl(Rectangle client) {
 this->client = client;
}

private: static int cnt; //текущая цифра
Timer ^timer1;

private: System::Void timer1_Tick(System::Object^  sender, System::EventArgs^  e) {
 draw_Tmpl(cnt++);
 if (cnt == 10) cnt = 0;
}

private: System::Void MyForm_Load(System::Object^  sender, System::EventArgs^  e) {
 this->Width = 100;
 this->Height = 300;
 make_Tmpl(Rectangle(0, 0, this->pictureBox1->Size.Width, this->pictureBox1->Size.Height));
 timer1 = gcnew Timer();
 timer1->Interval = 300;
 timer1->Tick += gcnew EventHandler(this, &MyForm::timer1_Tick);
 timer1->Enabled = true;
}

Важно, что мы не забыли включить в код обработчик события таймера timer1_Tick и подключить его к таймеру.

Метод timer1_Tick будет просто вызывать метод отрисовки очередной цифры draw_Tmpl и следить, чтобы счётчик цифр менялся "по кругу", не превышая значения 9.

В методе draw_Tmpl мы опишем каждую из 10 цифр как последовательность линий схемы, которые нужно отрисовать. Самый удобный путь для этого - двумерный управляемый массив, разные строки которого могут содержать различное количество элементов (так как разные цифры рисуются на почтовом индексе разным количеством линий). Обратите внимание, как потом получить к элемент такого массива - a[i]->GetValue(j), но не a[i,j] или a[i][j].

private: void draw_Tmpl(int n) {
 using namespace System::Drawing::Drawing2D;
 using namespace System::Drawing::Imaging;
 Bitmap^ bmp = gcnew Bitmap(client.Width, client.Height, PixelFormat::Format32bppArgb);
 Graphics^ g = Graphics::FromImage(bmp);
 Pen ^ p = gcnew Pen(Color::Red, 20.0);
 array<array<int^>^> ^a = gcnew array<array<int^>^>(10);
 a[0] = gcnew array<int^>(6) { 0, 1, 3, 5, 7, 8 };
 a[1] = gcnew array<int^>(3) { 2, 3, 7 };
 a[2] = gcnew array<int^>(4) { 0, 3, 6, 8 };
 a[3] = gcnew array<int^>(5) { 0, 3, 4, 7, 8 };
 a[4] = gcnew array<int^>(4) { 1, 3, 4, 7 };
 a[5] = gcnew array<int^>(5) { 0, 1, 4, 7, 8 };
 a[6] = gcnew array<int^>(6) { 0, 1, 4, 5, 7, 8 };
 a[7] = gcnew array<int^>(3) { 0, 2, 5 };
 a[8] = gcnew array<int^>(7) { 0, 1, 3, 4, 5, 7, 8 };
 a[9] = gcnew array<int^>(6) { 0, 1, 3, 4, 7, 8 };
 if (n > -1 && n < a->Length) {
  for (int j = 0; j < a[n]->Length; j++)
   draw_Line(g, p,(int)a[n]->GetValue(j));
 }
 pictureBox1->Image = bmp;
}

Я не придерживался здесь какого-либо стандарта рисования цифр, при необходимости поменяйте массив a сами.

Наконец, метод draw_Line будет рисовать одну линию с шаблона нашей схемы. Можно было бы заменить его просто переключателем switch в draw_Tmpl, но так нагляднее.

private: void draw_Line(Graphics^g, Pen ^ p, int n) { //графика, перо, номер линии
 switch (n) {
  case 0:
   g->DrawLine(p, client.Left, client.Top, client.Right, client.Top);
  break;
  case 1:
   g->DrawLine(p, client.Left, client.Top, client.Left, client.Top + client.Height/2);
  break;
  case 2:
   g->DrawLine(p, client.Right, client.Top, client.Left, client.Top + client.Height / 2);
  break;
  case 3:
   g->DrawLine(p, client.Right, client.Top, client.Right, client.Top + client.Height / 2);
  break;
  case 4:
   g->DrawLine(p, client.Left, client.Top + client.Height / 2, client.Right, client.Top + client.Height / 2);
  break;
  case 5:
   g->DrawLine(p, client.Left, client.Top + client.Height / 2, client.Left, client.Bottom);
  break;
  case 6:
   g->DrawLine(p, client.Right, client.Top + client.Height / 2, client.Left, client.Bottom);
  break;
  case 7:
   g->DrawLine(p, client.Right, client.Top + client.Height / 2, client.Right, client.Bottom);
  break;
  case 8:
   g->DrawLine(p, client.Left, client.Bottom, client.Right, client.Bottom);
  break;
 }
}

Стоило бы уменьшать толщину линий пера для диагоналей, попробуйте сделать это сами. Также я не ставил здесь цели сделать красивые цифры :)

30.11.2015, 13:29 [7682 просмотра]


теги: графика c++/cli

К этой статье пока нет комментариев, Ваш будет первым