БлогNot. Visual C++: рисуем мышью на форме

Visual C++: рисуем мышью на форме

Предыдущая заметка по теме была вот здесь, а дальше легко найти по цепочке :) Тем не менее, простого примера на рисование мышкой "как в Paint" что-то не видно, попробуем сделать его прямо сейчас.

1. Рисовать будем непосредственно на канве формы, хотя вообще-то лучше на PictureBox, растянутом на всю форму. Тогда просто получите MyGraphics от этого PictureBox. В классе формы приложения Windows Forms пропишем нужные данные:

private:
 bool isPaint; //Флажок "рисовать"
 Graphics ^MyGraphics; //Объект Graphics для рисования
 Brush ^ MyBrush; //Кисть для рисования
 Point p; //Последняя точка, где рисовали

На событие загрузки формы (Load) проинициализируем данные:

MyGraphics = this->CreateGraphics();
MyBrush = gcnew SolidBrush(Color::Red);
this->Text = "Рисуй кнопкой, стирай двойным щелчком";
isPaint = false;
p.X = -1; p.Y = -1;

По событию MouseDown формы (нажатие кнопки мыши) будем разрешать рисование:

isPaint = true;

а по событию MouseUp (отпускание кнопки мыши) - запрещать:

isPaint = false;

По событию. "движение мыши" формы (MouseMove) будем рисовать, если включён флажок isPaint и текущие координаты мыши отличаются от последних координат, сохранённых в переменной p. Также запомним новые координаты:

if (!isPaint) return;
else if (p.X != e->X || p.Y != e->Y) {
 p.X = e->X; p.Y = e->Y;
 MyGraphics->FillEllipse(MyBrush, p.X, p.Y, 2, 2); 
}

Вместо точки мы здесь выводим эллипс 2x2 пиксела. Работать с массивами отдельных пикселов можно, например, через System::Windows::Media::Imaging::WriteableBitmap.

Очищать рисунок можно по любому удобному событию или выбору кнопки (пункта меню). Используем для этого двойной щелчок (MouseDoubleClick) по форме:

MyGraphics->Clear(this->BackColor);

2. Увы, при быстром движении мыши обработчик события MouseMove не будет успевать рисовать маленькие эллипсы и линия получится разрывной. Проще всего решить проблему, соединяя текущую точку p и точку, полученную из текущего положения мыши, линией.

Вот как изменится весь написанный нами код:

private:
 bool isPaint; //Флажок "рисовать"
 Graphics ^MyGraphics; //Объект Graphics для рисования
 Pen ^ MyPen; //Перо для рисования
 Point p; //Последняя точка, где рисовали
private: System::Void Form1_Load(System::Object^  sender, System::EventArgs^  e) {
 MyGraphics = this->CreateGraphics();
 MyPen = gcnew Pen(Color::Red);
 this->Text = "Рисуй кнопкой, стирай двойным щелчком";
 isPaint = false;
 p.X = -1; p.Y = -1; 
}
private: System::Void Form1_MouseDown(System::Object^  sender, System::Windows::Forms::MouseEventArgs^  e) {
 isPaint = true;
 p.X = e->X; p.Y = e->Y;
}
private: System::Void Form1_MouseUp(System::Object^  sender, System::Windows::Forms::MouseEventArgs^  e) {
 isPaint = false;
 p.X = e->X; p.Y = e->Y;
}
private: System::Void Form1_MouseMove(System::Object^  sender, System::Windows::Forms::MouseEventArgs^  e) {
 if (!isPaint) return;
 else if (p.X != e->X || p.Y != e->Y) {
  MyGraphics->DrawLine(MyPen, p.X, p.Y, e->X, e->Y);
  p.X = e->X; p.Y = e->Y;
 }
}
private: System::Void Form1_MouseDoubleClick(System::Object^  sender, System::Windows::Forms::MouseEventArgs^  e) {
 MyGraphics->Clear(this->BackColor);
}

3. Наконец, существует удобный способ рисования, связанный с классом System::Drawing::Drawing2D::GraphicsPath.

Ниже прилагается полный листинг для вставки в приложение Windows Forms. Его внимательный анализ поможет вам освоить и этот способ. Кроме того, здесь MouseDown различает кнопки мыши, MouseUp определяет число кликов, а MouseMove можно заставить рисовать "непрерывно", раскомментарив одну строчку.

private:
 System::Drawing::Drawing2D::GraphicsPath ^mousePath;
 Graphics ^MyGraphics;
 Point p;
 int fontSize;
 bool dblClicked;
 
private: System::Void Form1_Load(System::Object^  sender, System::EventArgs^  e) {
 MyGraphics = this->CreateGraphics();
 this->Text = "Рисуй кнопкой, стирай двойным щелчком";
 mousePath = gcnew System::Drawing::Drawing2D::GraphicsPath();
 p.X = -1; p.Y = -1;
 fontSize = 20;
 dblClicked = false;
}
private: System::Void Form1_MouseDown(System::Object^  sender, System::Windows::Forms::MouseEventArgs^  e) {
 Point mouseDownLocation = Point(e->X, e->Y);
 String^ eventString = nullptr;
 switch (e->Button) { //Различаем все возможные кнопки мыши
  case System::Windows::Forms::MouseButtons::Left: eventString = "L"; break;
  case System::Windows::Forms::MouseButtons::Right: eventString = "R"; break;
  case System::Windows::Forms::MouseButtons::Middle: eventString = "M"; break;
  case System::Windows::Forms::MouseButtons::XButton1: eventString = "X1"; break;
  case System::Windows::Forms::MouseButtons::XButton2: eventString = "X2"; break;
  case System::Windows::Forms::MouseButtons::None: default: break;
 }
 if (eventString != nullptr) {
  mousePath->AddString(eventString, FontFamily::GenericSerif, 
   (int)FontStyle::Bold, (float)fontSize, mouseDownLocation, StringFormat::GenericDefault);
 }
 else {
  mousePath->AddLine(mouseDownLocation, mouseDownLocation);
 }
 this->Invalidate();
}
private: System::Void Form1_MouseUp(System::Object^  sender, System::Windows::Forms::MouseEventArgs^  e) {
 Point mouseUpLocation = System::Drawing::Point(e->X, e->Y);
 if (dblClicked) { dblClicked = false; return; }
 int numberOfClicks = e->Clicks;
 mousePath->AddString (String::Format("   {0}", numberOfClicks), 
  FontFamily::GenericSerif, (int)FontStyle::Bold, (float)fontSize, mouseUpLocation, StringFormat::GenericDefault);
 this->Invalidate();
}
private: System::Void Form1_MouseMove(System::Object^  sender, System::Windows::Forms::MouseEventArgs^  e) {
 if (p.X == -1 || p.Y == -1) { p.X = e->X; p.Y = e->Y; }
 else if (p.X != e->X || p.Y != e->Y) {
  mousePath->AddLine(p.X, p.Y, e->X, e->Y);
  p.X = e->X; p.Y = e->Y;
  //this->Invalidate(); //раскомментарить для "непрерывной" перерисовки
 }
}
private: System::Void Form1_MouseDoubleClick(System::Object^  sender, System::Windows::Forms::MouseEventArgs^  e) {
 MyGraphics->Clear(this->BackColor);
 delete mousePath;
 mousePath = gcnew System::Drawing::Drawing2D::GraphicsPath;
 this->Invalidate();
 dblClicked = true; //Был двойной щелчок, не рисовать лишнее число кликов в MouseUp
}

private: System::Void Form1_Paint(System::Object^  sender, System::Windows::Forms::PaintEventArgs^  e) {
 //главный метод - именно Paint. Это его вызывают Invalidate
 MyGraphics->DrawPath(System::Drawing::Pens::Red, mousePath);
}
//Часть ниже совсем необязательна :)
private: System::Void Form1_MouseEnter(System::Object^  sender, System::EventArgs^  e) {
 this->Text = String::Concat(sender->GetType(), ": MouseEnter"); //Просто пишем в заголовке окна о событии
}
private: System::Void Form1_MouseHover(System::Object^  sender, System::EventArgs^  e) {
 this->Text = String::Concat(sender->GetType(), ": MouseHover");
}
private: System::Void Form1_MouseLeave(System::Object^  sender, System::EventArgs^  e) {
 this->Text = String::Concat(sender->GetType(), ": MouseLeave");
}

15.11.2016, 14:54 [8484 просмотра]


теги: графика программирование c++/cli

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