Как рисовать в Windows Forms?
Я проверил все варианты, начиная от Paint Event до CreateGraphics.
Думаю мне не стоит объяснять почему они не работают.
Просто скажите, какой способ САМЫЙ верный, чтобы нарисовать к примеру квадрат?
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
Ответы с готовыми решениями:
Как можно рисовать на форме Windows Forms с помощью обычного GDI
Здравствуйте! Как можно рисовать на форме Windows Forms с помощью обычного GDI. Создавал класс.
Windows.Forms — Процедуры, цикл for и System.Windows.Forms.Button
Доброго времени суток. Есть WinForms программа: . private ClickedButtonId, ClickedButtonX.
Как рисовать текст на RGBA картинке ? (Windows)
Подскажите как написать текст с выбранным шрифтом на картинке RGBA? Или получить из текста с.
Как переделать Windows Forms приложение в Windows Service с UI?
Здравствуйте, уважаемые посетители форума! Вопрос такой. У меня есть приложение C# Win Forms с.
1126 / 859 / 501
Регистрация: 09.04.2014
Сообщений: 2,030
Сообщение от Ziya
Я проверил все варианты, начиная от Paint Event до CreateGraphics.
Думаю мне не стоит объяснять почему они не работают.
Сообщение от Ziya
нарисовать к примеру квадрат
1 2 3 4 5 6 7 8 9 10 11 12
public partial class Form1 : Form { public Form1() { InitializeComponent(); } protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); e.Graphics.DrawRectangle(Pens.Red, 20, 20, 50, 50); } }
Регистрация: 02.06.2012
Сообщений: 248
Сообщение от nedel
Думаю стоит
Проблем до фига! К примеру пару проблем:
1. Я не могу объявить OnPaint в другом классе.
2. Я не могу добавить Paint Event, когда DoubleBuffered = true, когда я делаю это с MyPanel Class.
3. Я не могу добавить второй Paint Event и когда я удаляю первый Paint Event, то рисунок исчезает.
Я не могу найти ответ на msdn и тут, это вообще возможно рисовать в C#? Или все эти побрикушки вроде Graphics сделали для роскоши? Если возможно, прошу написать один единйственный универсальный код, вы можете меня спасти.
Заблокирован
Зачем рисовать на форме, для этого есть PictureBox.
Как вариант:
1. Создаем рисунок в PictureBox (это нужно сделать только 1 раз при создании формы, или чтобы удалить старый рисунок)
pictureBox1.Image = new Bitmap(pictureBox1.Width, pictureBox1.Height);
2. Рисуем (например, в событии Shown, но можно и просто по кнопке и т.д.)
using (var g = Graphics.FromImage(pictureBox1.Image))
g.DrawLine(Pens.Blue, 0, 0, 200, 300);
>
290 / 271 / 107
Регистрация: 10.06.2011
Сообщений: 696
Регистрация: 02.06.2012
Сообщений: 248
Я нашёл способ, вроде как вы предложили:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; namespace _123 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } MyPanel mypanel1; Bitmap bitmap1; private void button1_Click(object sender, EventArgs e) { mypanel1 = new MyPanel(); mypanel1.Parent = this; mypanel1.Location = new Point(10, 10); mypanel1.Width = 50; mypanel1.Height = 50; mypanel1.BackColor = Color.Black; bitmap1 = new Bitmap(mypanel1.Width, mypanel1.Height); mypanel1.Paint += Draw; } private void Draw(object sender, PaintEventArgs e) { Graphics g = e.Graphics; Pen p = new Pen(Color.White, 1); g.DrawImage(bitmap1, 0, 0, bitmap1.Width, bitmap1.Height); } private void button2_Click(object sender, EventArgs e) { Graphics g = Graphics.FromImage(bitmap1); Pen p = new Pen(Color.White, 1); g.DrawRectangle(p, 10, 10, 10, 10); mypanel1.Invalidate(); } } public class MyPanel : Panel { public MyPanel() { this.DoubleBuffered = true; } } }
Как нарисовать пиксель в picture box?
Ковыряюсь в Windows Forms. По итогу мне нужно будет имея какую-то функцию (математическую) строить график. В связи с этим хочется понять, как изобразить на форме точку.
Отслеживать
28.5k 12 12 золотых знаков 58 58 серебряных знаков 118 118 бронзовых знаков
задан 12 окт 2017 в 20:26
41 3 3 бронзовых знака
3 ответа 3
Сортировка: Сброс на вариант по умолчанию
С помощью Bitmap, а именно функцией Bitmap.SetPixel().
Но если вы хотите рисовать графики, то это не лучший способ. В Windows Forms есть элемент Chart. Пользоваться им можно примерно так:
chart->Series[0]->Points->AddXY(x, f(x));
Там можно настроить тип интерполирования функции. Думаю, spline будет наиболее подходящий для вас.
Отслеживать
ответ дан 12 окт 2017 в 21:39
1,027 7 7 серебряных знаков 15 15 бронзовых знаков
Этот ответ был в английском so
e.Graphics.FillRectangle(aBrush, x, y, 1, 1);
Отслеживать
ответ дан 12 окт 2017 в 21:40
HasmikGaryaka HasmikGaryaka
2,447 9 9 серебряных знаков 17 17 бронзовых знаков
В Windows Forms для построения графиков используется инструмент Chart. Для выбора типа «График» выберите в Properties -> Series -> Revenue property -> ChartType. В выпадающем списке выберите необходимый тип графика. Методы Chart рассмотрим на примере следующего кода по построению функций косинуса и синуса с комментариями:
private void buttonCalc_Click(object sender, EventArgs e) < // Считываем с формы требуемые значения double Xmin = double.Parse(textBoxXmin.Text); double Xmax = double.Parse(textBoxXmax.Text); double Step = double.Parse(textBoxStep.Text); // Количество точек графика int count = (int)Math.Ceiling((Xmax - Xmin) / Step) + 1; // Массив значений X – общий для обоих графиков double[] x = new double[count]; // Два массива Y – по одному для каждого графика double[] y1 = new double[count]; double[] y2 = new double[count]; // Расчитываем точки для графиков функции for (int i = 0; i < count; i++) < // Вычисляем значение X x[i] = Xmin + Step * i; // Вычисляем значение функций в точке X y1[i] = Math.Sin(x[i]); y2[i] = Math.Cos(x[i]); >// Настраиваем оси графика chart1.ChartAreas[0].AxisX.Minimum = Xmin; chart1.ChartAreas[0].AxisX.Maximum = Xmax; // Определяем шаг сетки chart1.ChartAreas[0].AxisX.MajorGrid.Interval = Step; // Добавляем вычисленные значения в графики chart1.Series[0].Points.DataBindXY(x, y1); chart1.Series[1].Points.DataBindXY(x, y2); >
Как рисовать в windows forms c
На этом шаге мы рассмотрим один из алгоритмов, используемых для рисования в форме .
В заключение покажем, как в форме можно рисовать графики. Вид формы графиками функций sin(x) и tan(x) показан на рисунке 1. Коды обработчиков кнопок (фрагменты h -файла приложения, выводящего графики фукнций) приведены ниже.
#pragma once namespace My126_1 < using namespace System; using namespace System::ComponentModel; using namespace System::Collections; using namespace System::Windows::Forms; using namespace System::Data; using namespace System::Drawing; //Объявление функций приложения //--sin(x)--------------------- //математические функции берутся из класса Math double fs(double x) < return(Math::Floor(Math::Sin(x))); > //--tan(x)--------------------- double ft(double x) < return(Math::Floor(Math::Tan(x))); > /// /// Сводка для Form1 /// public ref class Form1 : public System::Windows::Forms::Form < . . . . . >#pragma endregion //=====Обработчики кнопок вывода графиков функций =========== private: System::Void button1_Click(System::Object^ sender, System::EventArgs^ e) < Color ^col = gcnew Color(); Pen ^pen = gcnew Pen (col->Red); //чтобы создать графический объект, надо получить ссылку на него, //выполнив метод CreateGraphics() компонента (формы) Graphics ^im = this->CreateGraphics(); //вывод графика функции с использованием DrawLine() im->Clear(col->White); //закрасить предыдущее графическое //изображение белым цветом float x1, y1, x2, y2; //текущие координаты в пикселях в форме x1 = 0; y1 = 0; //начальные координаты графика функции f(х) //вычисление функции y=f(x) в точках x1, x2, . x2 = x1; while ( x2 < this->Width && y2 < this->Height) < x2+=5; //следующая точка в пикселях по оси X y2 = fs(x2) * 100; // эта функция определена в начале модуля //(*100 - для увеличения амплитуды, чтобы //было нагляднее) if ( y2 < 0 ) y2*=-1; //чтобы выводить отрицательные значения im->DrawLine(pen, x1, y1, x2, y2); // вывод линии между 2-мя точками // точка 2 должна стать точкой 1, // а точкой 2 должна стать следующая текущая точка: x1 = x2; y1 = y2; continue; > > private: System::Void button2_Click(System::Object^ sender, System::EventArgs^ e) < Color ^col = gcnew Color(); Pen ^pen = gcnew Pen (col->Red); //чтобы создать графический объект, надо получить ссылку на него, //выполнив метод CreateGraphics() компонента (формы) Graphics ^im = this->CreateGraphics(); //вывод графика функции с использованием DrawLine() im->Clear(col->White); //закрасить предыдущее графическое //изображение белым цветом float x1, y1, x2, y2; //текущие координаты в пикселях в форме x1 = 0; y1 = 0; //начальные координаты графика функции f(х) //вычисление функции y=f(x) в точках x1, x2, . x2 = x1; while ( x2 < this->Width && y2 < this->Height) < x2+=10; //следующая точка в пикселях по оси X y2 = ft(x2) * 50; // эта функция определена в начале модуля //(*50 - для увеличения амплитуды, //чтобы было нагляднее) if ( y2 < 0 ) y2*=-1; //чтобы выводить отрицательные значения im->DrawLine(pen, x1, y1, x2, y2); // вывод линии между 2-мя точками // точка 2 должна стать точкой 1, // а точкой 2 должна стать следующая текущая точка: x1 = x2; y1 = y2; continue; > > private: System::Void button3_Click(System::Object^ sender, System::EventArgs^ e) < this->Close(); //завершение приложения >
Архив проекта можно взять здесь.
Рис.1. Графики синусоиды и тангенсоиды, построенные в форме
- ::identifier — это идентификатор (возможно, глобальный);
- class-name::identifier — это идентификатор, относящийся к классу;
- namespace::identifier — это идентификатор, тносящийся к пространству имен.
При выводе графика любой функции мы применили метод DrawLine() класса Graphics . Но напрямую сформировать указатель на этот класс, чтобы воспользоваться его членами (в частности методом DrawLine() ), нельзя. Над сначала получить ссылку на этот графический объект через специальный метод формы, который называется CreateGraphics() .
Если написать оператор
Graphics ^im = this->CreateGraphics();
то получим желаемое (метод, по его определению, формирует ссылку на графический объект).
Теперь через определенную нами ссылку типа Graphics можем добраться до членов класса Graphics (в частности до необходимого нам метода DrawLine() , который рисует прямую линию между двумя точками). Рисование происходит с помощью специального механизма, находящегося в классе Pen (перо или ручка с пером). Чтобы добраться до этого механизма, надо объявить ссылку на этот класс, а потом через нее добраться до нужного нам члена класса.
Формирование ссылки на класс происходит не само по себе, а с помощью утилиты gcnew , запускающей специальную программу-конструктор класса, который, в свою очередь, создает в выделенной утилитой памяти экземпляр класса, чтобы с ним можно было работать. Конструктор всегда имеет то же имя, что и класс, для которого он создается, только это обычная функция, выполняющая определенные действия по инициализации членов-данньх класса (она придает им некоторые начальные значения).
Конструктор для класса Pen имеет один параметр (цвет), потому что перо должно рисовать линии определенным цветом. Следовательно, прежде чем задавать работу конструктору класса Pen , нам нужно как-то определиться с цветом, который потом следует задать в качестве параметра этому конструктору. Цвета находятся в специальном классе Color (цвет). Чтобы добраться до нужного цвета в этом классе, надо сформировать ссылку на этот класс, а потом уже через нее достать нужный цвет. Отсюда имеем:
Color^ col = gcnew Color();
Утилита gcnew запускает конструктор класса Color , формирует экземпляр класса в памяти и выдает ссылку на этот экземпляр в переменную col . Теперь любой цвет из Color можно достать через полученную ссылку так:
При этом откроется окно подсказчика, из которого остается только выбрать подходящий цвет. Когда вы начнете вводить начальные буквы нужного вам объекта, среда сразу установит подсветку на ближайший объект, имя которого начинается на вводимые вами символы, что ускоряет процесс выбора нужной строки. После выбора строки нажмите клавишу Enter и она попадет в ваш оператор. Если вы не нашли подходящую строку, значит в объекте, на который вы сформировали ссылку, такого члена-данного нет.
После определения цвета можно выполнять конструктор пера:
Pen^ pen = gcnew Pen (col->Red);
Чтобы график выводился на поле формы, не занятое предыдущим графиком, следует графический объект, которым мы пользуемся для рисования (он фактически задает специальный холст для рисования, состоящий из точек-пикселей), закрасить нейтральным цветом, на фоне которого будет выводиться новый график.
Чтобы рисовать график, из непрерывной функции получают в цикле дискретные значения ее точек и между двумя соседними точками проводят прямую линию. Естественно, чем больше точек на данной поверхности, тем более точным будет график.
Со следующего шага мы начнем знакомиться с компонентами, используемыми при создании интерфейса приложения .
Как рисовать в windows forms c
БлогNot. C#: работаем с графикой и графическими компонентами в приложениях Windows Forms
C#: работаем с графикой и графическими компонентами в приложениях Windows Forms
Для работы с изображениями в библиотеке .NET определён базовый класс System.Drawing.Image , который предоставляет функциональные возможности для производных классов System.Drawing.Bitmap (растровая графика) и System.Drawing.MetaFile (векторная графика). Этот класс содержит методы для создания (и сохранения) объектов типа Image из указанного файла, потока данных и т.д.
Когда требуется перерисовка элемента управления, происходит событие Paint , которое, в зависимости от задачи, можно как программировать явно, так и полагаться на его автоматический вызов, происходящий, когда изменилась графическая канва объекта. Для отрисовки готового файла с изображением, имя которого задано или получено из диалога открытия файла OpenFileDialog , мы должны создать или получить из аргумента PaintEventArgs метода Paint графическую канву типа System.Drawing.Graphics а затем уже вывести на неё изображение.
Для использования готовых методов обработки изображений (поворот, масштабирование, изменение цвета и т.п.) мы программно создаём объект типа System.Drawing.Bitmap , копирующий имеющееся изображение, выполняем его обработку, а затем выводим изменённый объект в компоненту, предназначенную для отображения, такую как PictureBox .
Проект Lab5_1. Выведем выбранный в стандартном диалоге открытия файла рисунок на форму (пункт меню Файл — Открыть) и принудительно перерисуем по пункту меню Правка — Перерисовать. В свойствах диалога открытия файла openFileDialog1 указано Filter = Все файлы|*.*|Рисунки BMP|*.bmp|Рисунки JPEG|*.jpg|Рисунки PNG|*.png а свойство FileName равно пустой строке. В классе формы пропишем объекты «Изображение» и «Имя файла»:
private Image Img; private String Name;
Напишем обработчик открытия файла:
//Обработка меню Файл - Открыть openFileDialog1.ShowDialog(); Name = openFileDialog1.FileName.Trim(); if (String.IsNullOrEmpty(Name)) return; try < Img = new Bitmap(Name); //или так: Img = Image.FromFile(Name); >catch (Exception ex) < MessageBox.Show(ex.Message + Environment.NewLine + "(не могу открыть файл)", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); toolStripLabel1.Text = "Файл не выбран"; Img = null; return; >this.ClientSize = new System.Drawing.Size(Img.Width, Img.Height); //Размер формы подгогнали под размер картинки toolStripLabel1.Text = Name; //Имя файла вывели в панель инструментов this.Refresh(); //Потребовать перерисовки!
По событию Paint формы будет выполняться отрисовка объекта Img на текущей канве, Y-координата для вставки рисунка учитывает пространство, занимаемое по вертикали компонентами menuStrip1 и toolStripLabel1 :
private void Form1_Paint(object sender, PaintEventArgs e) < if (Img != null) < Point p = new Point(0, menuStrip1.Size.Height+ toolStripLabel1.Size.Height); e.Graphics.DrawImage(Img, p); >>
Объект «Графика», представляющий собой поверхность для рисования, также может быть получен для канвы формы (вот обработчик пункта меню Правка — Перерисовать):
if (Img != null)
или из загруженного (сгенерированного) изображения, например, см. код для пункта меню Файл — Создать:
Img = new Bitmap(200, 200, System.Drawing.Imaging.PixelFormat.Format24bppRgb); Graphics Gr = Graphics.FromImage(Img); // Теперь становятся доступными методы класса Graphics! Pen pen = new Pen(Color.ForestGreen, 4.0F); Gr.DrawLine(pen, 0, 0, 199, 199); Gr.RotateTransform(180.0F); //поворот на 180 градусов Img.Save("example.jpg", System.Drawing.Imaging.ImageFormat.Jpeg); //сохранение this.Refresh();
-
Image — рисунок. Само изображение можно загрузить программно через свойство ImageLocation , например, в методе Load формы:
openFileDialog1.ShowDialog(); if (openFileDialog1.FileName != null) this.pictureBox1.ImageLocation = this.openFileDialog1.FileName;
Для возможности прокрутки загруженного изображения достаточно разместить PictureBox на элементе Panel с установленным свойством AutoScroll = true (и Dock = Fill , если панель должна занимать всю клиентскую часть окна) и при этом для PictureBox указать SizeMode = AutoSize . После этого можно загрузить рисунок кодом вида
Image Img = new Bitmap(openFileDialog1.FileName); pictureBox1.Image = Img;
- ErrorImage — позволяет задать изображение, выводимое при ошибке загрузки;
- InitialImage — позволяет задать изображение, выводимое в процессе загрузки.
- ImageList (вкладка Компоненты) — список изображений, который можно использовать для «прокрутки» картинок или как список иконок для меню, вкладок и т.п.
- Timer (вкладка Компоненты), позволяет обрабатывать периодическое событие Tick и организовывать смену картинок в реальном времени, частота повторения события в миллисекундах задаётся свойством Interval .
Мы используем их в следующей теме.
Проект Lab5_2. Основные операции над изображениями. Кроме pictureBox1 , размещённого на Panel как описано выше, форма включает в себя стандартный диалог открытия файла openFileDialog1 , меню Файл — Открыть (обработчик аналогичен предыдущему примеру) и меню «Правка», откуда мы будем вызывать обработчики загруженного изображения.
5.2.1. Поворот и отражение изображений. В этом примере выведенный в PictureBox рисунок поворачивается на 180 градусов и выводится обратно в PictureBox :
if (pictureBox1.Image != null) < Bitmap bitmap1 = new Bitmap(pictureBox1.Image); if (bitmap1 != null) < bitmap1.RotateFlip(RotateFlipType.Rotate180FlipY); pictureBox1.Image = bitmap1; >>
Остальные повороты (отражения) – другие значения параметра RotateFlipType .
5.2.2. Масштабирование изображения или его части. Код ниже показывает, как можно программно уменьшить загруженное в компоненту PictureBox изображение в 2 раза:
if (pictureBox1.Image == null) return; Bitmap bitmap1 = new Bitmap (pictureBox1.Image); //взяли рисунок из компоненты Graphics Gr1 = Graphics.FromImage (bitmap1); //получили поверхность рисования из исходного рисунка Bitmap bitmap2 = new Bitmap (bitmap1.Width / 2, bitmap1.Height / 2, Gr1); //сделали вдвое меньший рисунок с тем же разрешением Graphics Gr2 = Graphics.FromImage (bitmap2); //получили поверхность рисования из меньшего рисунка Rectangle compressionRectangle = new Rectangle (0, 0, bitmap1.Width / 2, bitmap1.Height / 2); //определили масштабирующий прямоугольник Gr2.DrawImage (bitmap1, compressionRectangle); //отрисовали на поверхности второго рисунка первый со сжатием Pen MyPen = new Pen (Color.Red); //на измененном рисунке можно что-то подрисовать Gr2.DrawRectangle (MyPen, 0, 0, bitmap2.Width - 1, bitmap2.Height - 1); //например, сделать красную рамку pictureBox1.Image = bitmap2; //назначили второй рисунок компоненте pictureBox1.Size = bitmap2.Size; //поставили размер компоненты по размерам нового рисунка this.ClientSize = pictureBox1.Size; //. и такой же размер клиентской формы
Добавим пункт меню Файл — Сохранить и сохраним изображение:
if (pictureBox1.Image == null) return; Bitmap bitmap1 = new Bitmap (pictureBox1.Image); try < bitmap1.Save (openFileDialog1.FileName); //Сохраняем под именем из диалога открытия файла //Может вызвать исключение, если исходный файл ещё открыт >catch (Exception ex)
pictureBox1.Image.Save (openFileDialog1.FileName);
К сожалению, этот код может вызвать исключение, если исходный файл ещё открыт. Для «надёжных» операций с файлами при открытии изображений используйте FileStream , чтобы контролировать весь процесс (перепишем обработчик пункта меню «Открыть»):
openFileDialog1.ShowDialog (); if (openFileDialog1.FileName.Trim () != "" && openFileDialog1.FileName != null) < System.IO.FileStream file; try < file = new System.IO.FileStream (openFileDialog1.FileName, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Inheritable); >catch (Exception ex) < MessageBox.Show (ex.Message + "\nНе удалось открыть файл", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); return; >pictureBox1.Image = Image.FromStream (file); file.Close (); >
5.2.3. Изменение цвета на изображении. На форму добавлен стандартный СolorDialog и выбранный в нём цвет ставится прозрачным.
if (pictureBox1.Image == null) return; Bitmap bitmap1 = new Bitmap (pictureBox1.Image); if (colorDialog1.ShowDialog () == DialogResult.OK)
5.2.4. Фильтрация всего изображения или его части (по пикселям). В качестве примера уменьшим вдвое интенсивность синего цвета на картинке, избегая пикселей, цвет которых близок к белому (интенсивности красной, зелёной и синей компонент больше значения 250):
if (pictureBox1.Image == null) return; Bitmap bitmap1 = new Bitmap (pictureBox1.Image); for (int x = 0; x < bitmap1.Width; x++) for (int y = 0; y < bitmap1.Height; y++) < Color pixelColor = bitmap1.GetPixel (x, y); if (pixelColor.R >250 && pixelColor.G > 250 && pixelColor.B > 250) continue; //не фильтруем пиксели, чей цвет близок к белому Color newColor = Color.FromArgb (pixelColor.R, pixelColor.G, pixelColor.B / 2); bitmap1.SetPixel (x, y, newColor); > pictureBox1.Image = bitmap1;
Аналогично можно реализовать любую другую фильтрацию цветов, но из-за «ручного» прохода по пикселям скорость выполнения процесса может быть заметно ниже, чем для пп. 5.2.1-5.2.3. Более быстрый способ преобразования всех цветов рисунка даёт применение фильтрации на основе класса ColorMatrix . В качестве примера приведём код, преобразующий цветное изображение к оттенкам серого:
if (pictureBox1.Image == null) return; Bitmap bitmap1 = new Bitmap (pictureBox1.Image); Bitmap bitmap2 = new Bitmap (bitmap1.Width, bitmap1.Height); Graphics g = Graphics.FromImage (bitmap2); float [] [] Map = < new float[] , new float[] , new float[] , new float[] , new float[] >; System.Drawing.Imaging.ColorMatrix GrayscaleMatrix = new System.Drawing.Imaging.ColorMatrix (Map); System.Drawing.Imaging.ImageAttributes attributes = new System.Drawing.Imaging.ImageAttributes (); attributes.SetColorMatrix (GrayscaleMatrix); Rectangle rect = new Rectangle (0, 0, bitmap1.Width, bitmap1.Height); g.DrawImage (bitmap1, rect, 0, 0, bitmap1.Width, bitmap1.Height, GraphicsUnit.Pixel, attributes); pictureBox1.Image = bitmap2;
О классе ColorMatrix можно почитать, например, по ссылке. В нашем фильтре соотношение «весов» красной, зелёной и синей цветовых компонент 0.3 — 0.59 — 0.11 отражает чувствительность человеческого глаза к оттенкам красного, зелёного и синего.
В некоторых случаях фильтровать изображения можно и сменой свойства Image.PixelFormat , но вариант Format16bppGrayScale в GDI+ не сработал.
5.2.5. Сохранить рисунок так, как он выглядит на компоненте. Следует понимать, что свойство SizeMode управляет отображением рисунка в компоненте, при сохранении пропорции рисунка не изменятся от того, что он был выведен, например, при SizeMode=StretchImage (принудительно растянут по размерам компоненты, возможно, с нарушением пропорций). Тем не менее — а можно ли сохранить рисунок так, как он был выведен в компоненту? Да, можно, например, так:
if (pictureBox1.Image == null) return; pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage; pictureBox1.Dock = DockStyle.Fill; //установили растягивание Bitmap bitmap1 = new Bitmap (pictureBox1.Image); Bitmap bitmap2 = new Bitmap (pictureBox1.Width, pictureBox1.Height); //у 2-го рисунка - размер компоненты Graphics g = Graphics.FromImage (bitmap2); //получили графический контекст из 2-го рисунка g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Bicubic; //настроили режим интерполяции g.DrawImage (bitmap1, new Rectangle(0,0, bitmap2.Width, bitmap2.Height)); //отрисовали в контекст 2-го рисунка исходный, неискажённый рисунок pictureBox1.Image = bitmap2; //назначили искажённый рисунок компоненте сохранитьToolStripMenuItem_Click (this, e); //вызвали метод сохранения pictureBox1.SizeMode = PictureBoxSizeMode.AutoSize; pictureBox1.Dock = DockStyle.None; //восстановили свойства pictureBox1.Image = bitmap1; //вернули старый рисунок
Убедиться в том, что рисунок был пересохранён в фоновом режиме с размерами, соответствующими клиентской части формы можно, заново открыв его с диска.
5.2.6. Выделить часть рисунка и реализовать обрезку по выделенной области. В класс формы добавим следующие глобальные данные:
Rectangle selRect; //выделенный прямоугольник Point orig; //точка для привязки прямоугольника Pen pen; //перо для отрисовки bool flag; //флажок показывает, находимся ли в режиме выделения части рисунка
Инициализируем их, например, в имеющемся конструкторе формы:
public Form1() < InitializeComponent(); pen = new Pen (Brushes.Blue, 0.8f); //цвет и толщина линии выделения pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash; //штрихи selRect = new Rectangle (0, 0, 0, 0); flag = false; >
Реализация динамического выделения мышью потребует взаимодействия нескольких событий (нажатие кнопки мыши, отпускание кнопки мыши и перемещение мыши). Для удобства используем также динамическое переключение обработчика события Paint (чтобы рисовать рамку выделения только тогда, когда она нужна – происходит перемещение курсора мыши на pictureBox при зажатой левой кнопке).
private void pictureBox1_Paint (object sender, PaintEventArgs e) < //Этот обработчик мы создали в конструкторе //Для ситуации, когда выделяем рамку e.Graphics.DrawRectangle (Pens.Black, selRect); >private void Selection_Paint (object sender, PaintEventArgs e) < //Добавили свой обработчик Paint для остальных ситуаций e.Graphics.DrawRectangle (pen, selRect); >private void pictureBox1_MouseDown (object sender, MouseEventArgs e) < //Этот обработчик мы создали в конструкторе //Нажали мышку - включаем наш обработчик и выключаем стандартный pictureBox1.Paint -= new PaintEventHandler (pictureBox1_Paint); pictureBox1.Paint += new PaintEventHandler (Selection_Paint); orig = e.Location; //запомнили, где начало выделения flag = true; >private void pictureBox1_MouseUp (object sender, MouseEventArgs e) < //Этот обработчик мы создали в конструкторе //отжали мышку - всё наоборот pictureBox1.Paint -= new PaintEventHandler (Selection_Paint); pictureBox1.Paint += new PaintEventHandler (pictureBox1_Paint); pictureBox1.Invalidate (); //принудительно перерисовать flag = false; //выйти из режима выделения >private void pictureBox1_MouseMove (object sender, MouseEventArgs e) < //Этот обработчик мы создали в конструкторе if (flag) < //если в режиме выделения selRect = GetSelectionRectangle (orig, e.Location); //запоминаем выделенное if (e.Button == MouseButtons.Left) < pictureBox1.Refresh (); //рефрешим картинку по нажатию левой кнопки >> > private Rectangle GetSelectionRectangle (Point orig, Point location) < //Этот метод пришлось написать, чтобы координаты выделения правильно запоминались //независимо от того, в какую сторону тащим курсор мыши Rectangle rect = new Rectangle (); int dX = location.X - orig.X, dY = location.Y - orig.Y; System.Drawing.Size size = new System.Drawing.Size (Math.Abs (dX), Math.Abs (dY)); //размеры текущего выделения if (dX >= 0 && dY >= 0) rect = new Rectangle (orig, size); else if (dX < 0 && dY >0) rect = new Rectangle (location.X, orig.Y, size.Width, size.Height); else if (dX > 0 && dY
Теперь, при наличии на изображении выделенной рамки selRect можно, например, реализовать его обрезание по границам этой рамки:
if (pictureBox1.Image == null) return; if (selRect.Width > 0 && selRect.Height > 0)
Проект Lab5_3. Рисование фигур. Показанный выше подход нетрудно применить для рисования геометрических примитивов на канве PictureBox или формы.
Создадим форму как в предыдущем примере с PictureBox , расположенным на Label , у компонент установлены те же свойства.
Рассмотрим варианты рисования линии на канве PictureBox . При движении мыши с зажатой левой кнопкой наша линия должна динамически обновляться, а при отпускании кнопки — добавляться на существующий рисунок.
Опишем в классе формы необходимые данные:
Point p1, p2; //начало и конец линии Pen pen1; //перо Brush brush1; //кисть Bitmap Img1, Img2; //основная картинка, на которой рисуем и буферная Graphics gr; //графический контекст bool isPressed; //флажок "кнопка мыши зажата"
Для самой формы нам понадобится запрограммировать событие Load , где мы инициализируем эти объекты, то есть, создадим рисунок размером с клиентскую часть окна формы, назначим его компоненте, создадим перо и выставим в «ложь» флажок:
Img1 = new Bitmap (ClientSize.Width, ClientSize.Height); pictureBox1.Image = Img1; gr = Graphics.FromImage (Img1); pen1 = new Pen (Color.Black); isPressed = false;
Всё остальное запрограммируем в событиях PictureBox . На нажатие кнопки мыши будем включать флажок и запоминать место клика p1 :
private void pictureBox1_MouseDown (object sender, MouseEventArgs e)
На отпускание кнопки получим координаты второй точки p2 и соединим её с первой, проведя линию на образе Img1 . Заметим, что в реальном коде можно добавлять точки в контейнер, например, в список List из объектов Point . Если при этом запоминать, какой именно объект рисовался, можно в нужные моменты просто перерисовывать объекты по списку (что может предотвратить «утечки памяти» при работе приложения), а также удалять или динамически изменять их.
private void pictureBox1_MouseUp (object sender, MouseEventArgs e)
На перемещение мыши обработка будет немного хитрей. Если кнопка не зажата, ничего делать не нужно, а в противном случае будем проводить текущую линию на копии рисунка Img2 , созданной из Img1 , чтобы не получилось «веера» из линий при перемещении мыши с зажатой кнопкой. Img2 всё равно придётся временно назначить рисунком для PictureBox , чтобы линия была видна в процессе движения мыши.
private void pictureBox1_MouseMove (object sender, MouseEventArgs e) < if (!isPressed) return; //Кнопка не зажата - выйти p2 = e.Location; Img2 = new Bitmap (Img1); pictureBox1.Image = Img2; gr = Graphics.FromImage (Img2); gr.DrawLine (pen1, p1, p2); pictureBox1.Invalidate (); >
В показанном примере все координаты отсчитывались «внутри PictureBox » и получались непосредственно из аргумента MouseEventArgs обработчика события. По-другому можно сделать, используя координаты курсора относительно экрана Cursor.Position.X , Cursor.Position.Y , а затем вычитая из них координаты верхнего левого угла формы Location.X , Location.Y (и, возможно, дополнительные значения, учитывающие занятое другими компонентами пространство на форме):
int x1 = Cursor.Position.X - Location.X, y1 = Cursor.Position.Y - Location.Y;
Расположив на панели инструментов приложения дополнительные кнопки для выбора геометрического примитива, цвета и т. п., мы можем получить приложение графический-редактор (см. прикреплённый проект, где ряд возможностей уже добавлен).
Обратите внимание в коде проекта Lab5_3: при рисовании линии и эллипса координаты второй точки могут быть и «меньше» (ближе к левому верхнему углу холста), чем первой. При рисовании же прямоугольника область экрана должна быть задана, начиная с левого верхнего угла. Метод GetRectangle , вызываемый при обработке событий pictureBox1_MouseUp и pictureBox1_MouseMove , корректирует эту проблему, как и при выделении прямоугольником в проекте Lab5_2.
Простая рисовалка на Windows Forms C#
Проект Lab5_4. Другой контекст. В завершение заметим, что отрисовка, выполняемая в объекте графического контекста, позволяет без каких-либо изменений основного кода «перенести» графический объект на другую канву, например, на экран вместо окна приложения.
Пусть в классе формы имеется метод Draw , создающий некоторый рисунок:
void Draw (System.Drawing.Graphics g) < //Графический контекст передан в наш метод Pen [] pens = < Pens.Red, Pens.Yellow, Pens.Green >; //Разные перья для рисования int width = ( this.ClientSize.Width - 1 ) / 2, height = ( this.ClientSize.Height - 1 ) / 2; //Половинки ширины и высоты клиентской части окна for (int i = 0; i < 3; i++) //Демо - рисуем перьями g.DrawRectangle (pens [i], i * width, i * height, width, height); Brush [] brushes = < Brushes.Red, Brushes.Yellow, Brushes.Green >; //Разные кисти для выполнения заливки цветом for (int i = 0; i < 3; i++) //Демо - рисуем кистями g.FillEllipse (brushes [i], i * width, i * height, width, height); g.DrawLine (pens [2], 0, 0, width * 2, height * 2); //Рисуем линию пером >
Как и в начале статьи, мы могли бы вызвать его кодом вида
Graphics g = this.CreateGraphics (); Draw (g);
для отображения картинки непосредственно на канве формы.
Теперь выведем рисунок в контексте графического экрана Windows поверх всех окон.
Убедимся, что к файлу формы подключены нужные пространства имён:
using System.Runtime.InteropServices; using System.Drawing;
В классе формы (например, после конструктора) укажем ссылки на нужные методы библиотеки user32.dll , которые нам потребуются:
[DllImport ("user32.dll")] public static extern IntPtr GetDC (IntPtr hwnd); [DllImport ("user32.dll")] public static extern void ReleaseDC (IntPtr hwnd, IntPtr dc);
В методе рисования (например, по событию Paint формы) вызовем наш метод с другим контекстом:
private void Form1_Paint (object sender, PaintEventArgs e)
Также можно потребовать где-нибудь принудительной перерисовки, например, по клику на форме:
private void Form1_Click (object sender, EventArgs e)
- в верхнем меню выбрать команду Проект — Добавить существующий элемент. ;
- в списке типов файлов выбрать «Исполняемые файлы», показать расположение нужного файла ( c:\Windows\System32\user32.dll ) и нажать «Добавить»;
- выбрать добавленный файл в Обозревателе решений, в окне «Свойства» указать для него значение «Копировать в выходной каталог» равным «Копировать более новую версию»;
- после этого прототипы нужных функций библиотеки можно описать в классе формы с атрибутами public static extern и предшествующей директивой [DllImport («user32.dll»)] , как мы делали выше.
- сохранение полученных графических файлов;
- прокрутка файлов, если они «не помещаются» в окне компоненты PictureBox ;
- возможность построить изображение более, чем в одном масштабе (или масштабировать его программно);
- не менее двух настраиваемых параметров, позволяющих изменить внешний вид объекта (например, для объекта «дом» — количество этажей и количество окон на каждом этаже).
05.04.2023, 19:26 [2077 просмотров]