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 [7903 просмотра]