habrahabr
Минималистичный скриншотер на .NET
Лучше один раз увидеть, чем 100 раз услышать.
Уверен, не я один часто делаю скриншоты.
Проиллюстрировать баг, отправить смешную картинку знакомому,
вставить в комментарий на каком-нибудь ресурсе, etc.
А началось всё с разочарования в некогда любимом Gyazo,
а именно сам скриншот уже трудно найти под кучей рекламных блоков.
Когда я пересмотрел аналоги, они мне показались чересчур громоздкими.
Пройти через несколько окон, чтобы просто сделать скриншот? Нет, спасибо.
И вот, желая облегчить себе жизнь, а заодно ответить на вопрос
было решено сделать всё самому.
Суть
А хочется то предельно простого.
Один клик - запустить приложение, второй клик - выделить область на экране.
Всё - после этого видим скриншот уже в браузере (в PNG) и имеем ссылку на него в буфере обмена.
Примерный алгоритм работы видится мне таким:
- При запуске программа делает скриншот.
- Устанавливает его фоном для окна, развёрнутого на весь экран.
- Мышкой выделяем нужную область на экране.
- Готово, картинка улетает на сервер.
Как и ожидалось, возникла аналогичная проблема с мерцанием.
Для устранения установим форме свойство DoubleBuffered.
DoubleBuffered = true;
К сожалению помогло только частично. На просторах интернета был найден совет
отправить функции CreateWindowEx сообщение WS_CLIPCHILDREN в параметре dwExStyle.
Сделать это можно переопределив CreateParams.
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle /= 0x02000000;
return cp;
}
}
Загружаются скриншоты на imgur, присутсвует простой апи.
Для использования нужен api key, регистрируем приложение - получаем тут же ключ.
Отправляем картинку:
class Uploader
{
private static string imgur_key = null;
public static string IMGUR_KEY
{
set { imgur_key = value; }
}
public static string UploadToImgur(Bitmap bitmap, ImageFormat imgFormat)
{
MemoryStream memStream = new MemoryStream();
bitmap.Save(memStream, imgFormat);
using (WebClient wc = new WebClient())
{
NameValueCollection values = new NameValueCollection
{
{ "key", imgur_key },
{ "image", Convert.ToBase64String(memStream.ToArray()) }
};
byte[] response = wc.UploadValues("http://api.imgur.com/2/upload.xml", values);
XDocument xDocument = XDocument.Load(new MemoryStream(response));
return (string)xDocument.Root.Element("links").Element("original");
}
}
}
И собственно код самой программы:
public partial class ScreenForm : Form
{
Rectangle rect;
Bitmap bmpScreenshot;
//формат скриншотов
ImageFormat imgFormat = ImageFormat.Png;
public ScreenForm()
{
InitializeComponent()
this.Paint += new System.Windows.Forms.PaintEventHandler(this.ScreenForms_Paint);
this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.ScreenForm_KeyDown);
this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.ScreenForm_MouseDown);
this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.ScreenForm_MouseMove);
this.MouseUp += new System.Windows.Forms.MouseEventHandler(this.ScreenForm_MouseUp);
this.FormBorderStyle = FormBorderStyle.None;
this.ShowInTaskbar = false;
this.TopMost = true;
this.DoubleBuffered = true;
this.Height = Screen.PrimaryScreen.Bounds.Height;
this.Width = Screen.PrimaryScreen.Bounds.Width;
//Указываем полученный ключ
Uploader.IMGUR_KEY = "83dd0aacde6c6fc74d16638d5724bf96";
MakeScreenShot();
this.BackgroundImage = (Image)bmpScreenshot;
this.Cursor = Cursors.Cross;
}
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle /= 0x02000000;
return cp;
}
}
private void MakeScreenShot()
{
Graphics gfxScreenshot;
bmpScreenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height,
PixelFormat.Format32bppArgb);
gfxScreenshot = Graphics.FromImage(bmpScreenshot);
gfxScreenshot.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0,
Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy);
gfxScreenshot.Dispose();
}
public Bitmap CropImage(Bitmap source, Rectangle section)
{
Bitmap bmp = new Bitmap(section.Width, section.Height);
Graphics g = Graphics.FromImage(bmp);
g.DrawImage(source, 0, 0, section, GraphicsUnit.Pixel);
return bmp;
}
private void ScreenForms_Paint(object sender, PaintEventArgs e)
{
//цвет заливаемой области
using (SolidBrush b = new SolidBrush(Color.FromArgb(75, 100, 100, 100)))
{
e.Graphics.FillRectangle(b, rect);
}
}
private void ScreenForm_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Escape)
base.Close();
}
private void ScreenForm_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
rect = new Rectangle(e.X, e.Y, 0, 0);
}
else if (e.Button == MouseButtons.Right)
{
base.Close();
}
}
private void ScreenForm_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
rect = new Rectangle(rect.Left, rect.Top, e.X - rect.Left, e.Y - rect.Top);
}
this.Invalidate();
}
private void ScreenForm_MouseUp(object sender, MouseEventArgs e)
{
this.Hide();
//вырезаем выделенную область
Bitmap croppedBmp = CropImage(bmpScreenshot, rect);
//мне понадобилось указывать в углу время скришнота, что и делаем
StringFormat strFormat = new StringFormat();
strFormat.Alignment = StringAlignment.Far;
strFormat.LineAlignment = StringAlignment.Far;
Graphics g = Graphics.FromImage(croppedBmp);
g.DrawString(DateTime.Now.ToString(), new Font("Tahoma", 8), Brushes.Purple, new RectangleF
(0, 0, croppedBmp.Width,
croppedBmp.Height), strFormat);
//загрузка на сервер, копирование ссылки в буфер и открытие url картинки в браузере
string link = Uploader.UploadToImgur(croppedBmp, imgFormat);
Clipboard.SetText(link);
Process.Start(link);
Application.Exit();
}
}
Что же получилось?
В результате имеем то, что хотели.
Простая быстрая программа, не делающая ничего лишнего.
Никаких мусорных временных файлов не создаётся.
Минимальная базовая функциональность, как готовое законченное решение
рассматривать пока рано.