Brush fillBrush = new SolidBrush(backColor);
// рисуем отрезки и круги для округленного прямоугольника
g.DrawLine(p, rc.Left + size.Width / 2, rc.Top,
rc.Right - size.Width / 2, rc.Top);
g.FillEllipse(fillBrush, rc.Right - size.Width, rc.Top,
size.Width, size.Height);
g.DrawEllipse(p, rc.Right - size.Width, rc.Top, size.Width, size.Height);
g.DrawLine(p, rc.Right, rc.Top + size.Height / 2, rc.Right,
rc.Bottom - size.Height /2);
g.FillEllipse(fillBrush, rc.Right - size.Width, rc.Bottom - size.Height,
size.Width, size.Height);
g.DrawEllipse(p, rc.Right - size.Width, rc.Bottom - size.Height,
size.Width, size.Height);
g.DrawLine(p, rc.Right - size.Width / 2, rc.Bottom,
rc.Left + size.Width / 2, rc.Bottom);
g.FillEllipse(fillBrush, rc.Left, rc.Bottom - size.Height,
size.Width, size.Height);
g.DrawEllipse(p, rc.Left, rc.Bottom - size.Height,
size.Width, size.Height);
g.DrawLine(p, rc.Left, rc.Bottom - size.Height / 2,
rc.Left, rc.Top + size.Height / 2);
g.FillEllipse(fillBrush. rc.Left, rc.Top, size.Width, size.Height);
g.DrawEllipse(p, rc.Left, rc.Top, size.Width. size.Height);
// заполняем прямоугольник, скрывая внутренние эллипсы
g.FillPolygon(fillBrush, points);
// освобождаем ресурсы
fillBrush.Dispose();
}
private void butDrawRoundedRectangle_Click(object sender, EventArgs e) {
Graphics g = CreateGraphics();
Rectangle rc = new Rectangle(10, 10, 200, 50);
DrawRoundedRectangle(g,
new Pen(Color.Black), Color.CadetBlue, rc, new Size(8, 8));
}
Результат работы этого кода показан на рис. 6.8.
Рис. 6.8. Отображение закрашенного прямоугольника со скругленными углами
Создание экранных снимков
Если при работе с мобильным устройством необходимо сделать скриншоты, то для реализации замысла необходимо использовать внешние устройства. Конечно, можно просто сфотографировать экран, но настоящий программист будет использовать функции Windows API. В этом разделе главы будет рассматриваться пример копирования определенной области окна, всего рабочего окна программы или любого другого окна. Для демонстрации примера надо разместить на форме список, три кнопки и один таймер. Сам код приведен в листинге 6.20.
Листинг 6.20
[DllImport("coredll.dll", EntryPoint = "GetDesktopWindow")]
public static extern IntPtr GetDesktopWindow();
[DllImport("coredll.dll", EntryPoint = "GetDC")]
public static extern IntPtr GetDC(IntPtr hWnd);
[DllImport("coredll.dll", EntryPoint = "ReleaseDC")]
public static extern IntPtr ReleaseDC(IntPtr hWnd, IntPtr hDC);
[DllImport("coredll.dll")]
public static extern int BitBlt(IntPtr hdcDest, int nXDest, int nYDest,
int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, uint dwRop);
const int SRCCOPY = 0x00CC0020;
private void screenshot(string filename, Graphics gx, Rectangle rect) {
Bitmap bmp = new Bitmap(rect.Width, rect.Height);
Graphics g = Graphics.FromImage(bmp);
BitBlt(g.GetHdc(), 0, 0, rect.Width, rect.Height, gx.GetHdc(),
rect.Left, rect.Top, SRCCOPY);
bmp.Save(filename, System.Drawing.Imaging.ImageFormat.Bmp);
bmp.Dispose();
g.Dispose();
}
private void butPartOfWindow_Click(object sender, EventArgs e) {
// Делаем снимок списка
ScreenShot(@"My Documentssave.bmp", this.CreateGraphics(),
listBox1.Bounds);
}
private void butScreen_Click(object sender, EventArgs e) {
// Делаем снимок экрана
Rectangle rect = new Rectangle(0,0,240,240);
Bitmap bmp = new Bitmap(rect.Width, rect.Height);
Graphics g = Graphics.FromImage(bmp);
IntPtr hwnd = GetDesktopWindow();
IntPtr hdc = GetDC(hwnd);
BitBlt(g.GetHdc(), 0, 0, rect.Width, rect.Height, hdc, rect.Left,
rect.Top, SRCCOPY);
bmp.Save(@"My Documentsscreen.bmp",
System.Drawing.Imaging.ImageFormat.Bmp);
// Освобождаем ресурсы
ReleaseDC(hwnd, hdc);
bmp.Dispose();
g.Dispose();
}
private void timer1_Tick(object sender, EventArgs e) {
// Делаем снимок экрана через 5 секунд
Rectangle rect = new Rectangle(0, 0. 240, 240);
Bitmap bmp = new Bitmap(rect.Width, rect.Height);
Graphics g = Graphics.FromImage(bmp);
IntPtr hwnd = GetDesktopWindow();
IntPtr hdc = GetDC(hwnd);
BitBlt(g.GetHdc(), 0, 0, rect.Width, rect.Height, hdc, rect.Left,
rect.Top, SRCCOPY);
bmp.Save(@"My Documents5sec.bmp", System.Drawing.Imaging.ImageFormat.Bmp);
// Освобождаем ресурсы
ReleaseDC(hwnd, hdc);
bmp.Dispose();
g.Dispose();
timer1.Enabled = false;
}
private void but5Sec_Click(object sender, EventArgs e) {
timer1.Enabled = true;
}
Функция ScreenShot позволяет быстро получить участок экрана и сохранить его в графическом файле. В рассмотренном примере внешний вид списка сохраняется в файле listbox.bmp. Для этого достаточно было указать имя файла, объект Graphics и размеры списка ListBox. Для получения снимка экрана пример пришлось несколько усложнить, добавив вызовы функций GetDesktopWindow и GetDC.
Если нужно получить снимок другой программы, то придется воспользоваться таймером. После запуска таймера в распоряжении пользователя будет 5 секунд, чтобы запустить другое приложение. Основная программа будет работать в фоновом режиме и сделает снимок экрана.
Чтобы проверить работу приложения, нужно запустить программу, нажать каждую кнопку, а затем с помощью программы File Explorer найти сохраненные файлы.
ВНИМАНИЕ
Нужно проявлять определенную осторожность при работе с методом Bitmap.Save(). Дело в том, что в Windows Mobile 2003 и более ранних версиях операционных систем библиотека .NET Compact Framework не поддерживает сохранение графических файлов в форматах GIF, JPEG или PNG. Сохранять файлы можно только в формате BMP. Причем во время написания кода редактор не заметит ошибки и позволит запустить программу с неправильным вызовом метода. Однако при вызове метода возникнет исключение NotSupportedException. К счастью, в Windows Mobile 5.0 поддерживаются все четыре графических формата.
В .NET Compact Framework 2.0 появилась ограниченная поддержка метода LockBits, при помощи которого можно манипулировать массивом пикселов изображения. Перечисление ImageLockMode в данном методе позволяет использовать значения ReadWrite, ReadOnly и WriteOnly. А перечисление PixelFormat поддерживает значения, перечисленные в следующем списке:
□ Format16bppRgb555;
□ Format16bppRgb565;
□ Format24bppRgb;
□ Format32bppRgb.
На сайте MSDN можно найти статью «How to: Use LockBits» с примером, в котором создается картинка и меняется интенсивность синих пикселов с помощью метода LockBits. В листинге 6.21 приведен пример, который для большей наглядности пришлось немного изменить.
Листинг 6.21
private Bitmap CreateBitmap(int width, int height) {
Bitmap bmp = new Bitmap(@"Windowsmsn.gif");
width = bmp.Size.Width;
height = bmp.Size.Height;
Graphics g = Graphics.FromImage(bmp);
g.Dispose();
return bmp;
}
protected override void OnPaint(PaintEventArgs e) {
Bitmap bmp = CreateBitmap(100, 100);
// Выводим картинку-оригинал
e.Graphics.DrawImage(bmp, 0, 0);
MakeMoreBlue(bmp);
// Рисуем модифицированную картинку ниже исходного изображения
e.Graphics.DrawImage(bmp, 0, 50);
bmp.Dispose();
}
private void MakeMoreBlue(Bitmap bmp) {
// Задаём формат данных о цвете для каждой точки изображения
PixelFormat pxf = PixelFormat.Format24bppRgb;
// Блокируем изображение в памяти
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadWrite, pxf);
// Получаем адрес первой строки развертки
IntPtr ptr = bmpData.Scan();
// Массив, содержащий байты изображения
int numBytes = bmp.Width * bmp.Height * 3;
byte[] rgbValues = new byte[numBytes];
// Копируем значения RGB в массив
Marshal.Copy(ptr, rgbValues, 0, numBytes);
// Модифицируем изображение, устанавливая
// синий цвет для каждой точки в картинке
for (int counter = 0; counter < rgbValues.Length; counter += 6)
rgbValues[counter] = 255;
// Копируем значения RGB обратно в изображение
Marshal.Сору(rgbValues, 0, ptr, numBytes);
// Разблокируем биты в памяти
bmp.UnlockBits(bmpData);
}
После запуска приложения на экране будут показаны две копии картинки, причем нижнее изображение будет немного отличаться от верхнего насыщенностью цветов.
Теперь, когда мы ознакомились с графическими методами, настало время написать простейший графический редактор с минимальными возможностями. В этом приложении можно будет рисовать при помощи стилуса линии, а также прямые цветные линии из трех цветов. Процесс рисования узоров на экране КПК при помощи стилуса гораздо больше похож на реальное рисование, чем рисование мышью в стандартных графических редакторах.
Весь код программы сводится к обработке событий мыши MouseDown, MouseMove и MouseUp. В принципе, приемы создания графических эффектов ничем не отличаются от соответствующих приемов, применяемых на обычных персональных компьютерах. Я взял два примера из своей книги «Занимательное программирование на Visual Basic .NET» и перенес код в проект с учетом синтаксиса языка С#, что иллюстрирует листинг 6.22.