WPF实现截图功能
一、前言
上一篇文章已经介绍过了如何自定义窗口,对于一款文字OCR软件,如果只能打开本地图片进行识别,那就显得不够人性化,所以这篇文章主要介绍了WPF如何实现截图功能。
二、截图演示
三、 代码实现
1. 截图原理
在制作这个功能前,我在网上查了一些资料,了解到截图的原理就是将当前屏幕的图像拷贝下来,再置顶,之后选择截图区域即可。
2. 拷贝当前屏幕图像
#region 获取屏幕图像
/// <summary>
/// 获取屏幕图像
/// </summary>
/// <returns>图像文件</returns>
private Bitmap GetScreen()
{
System.Drawing.Rectangle rc = SystemInformation.VirtualScreen; // 获取屏幕的宽和高
Bitmap bitmap = new Bitmap(rc.Width, rc.Height);
using (Graphics g = Graphics.FromImage(bitmap))
{
g.CopyFromScreen(rc.X, rc.Y, 0, 0, rc.Size, CopyPixelOperation.SourceCopy); // 拷贝屏幕
}
return bitmap;
}
#endregion
- 使用
SystemInformation.VirtualScreen
时需要添加using System.Windows.Forms;
Bitmap
、Graphics
均包含在System.Drawing
中
3. 截取图像
如果需要将屏幕图像贴在桌面最上面一层,可以通过将屏幕图像放在一个新窗口中呈现出来。
创建一个名为SnipWin的新窗口。
窗口样式代码如下:
<Window x:Class="SnipOCR.SnapWin"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SnipOCR"
mc:Ignorable="d"
Title="SnapWin" Height="450" Width="800" WindowStyle="None" AllowsTransparency="True" Background="Transparent">
<Grid>
<InkCanvas Margin="0,0,0,0" Name="Ink" Visibility="Visible" EditingModeInverted="Select" EditingMode="Select" Cursor="Cross" MouseDown="Ink_MouseDown" MouseMove="Ink_MouseMove" MouseUp="Ink_MouseUp" MouseRightButtonDown="Ink_MouseRightButtonDown">
</InkCanvas>
<Border Name="Bord" Visibility="Collapsed" BorderThickness="2" HorizontalAlignment="Left" VerticalAlignment="Top" Cursor="Cross" BorderBrush="#FF09646D"/>
<Button Name="Conf" Visibility="Collapsed" Content="✔" FontSize="16" HorizontalAlignment="Left" Height="22" Margin="527,347,0,0" VerticalAlignment="Top" Width="30" BorderBrush="#FF32510C" Background="#66C4EF3C" Foreground="#FF006855" Click="Conf_Click"/>
</Grid>
</Window>
下面是cs文件代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Drawing;
using System.Runtime.InteropServices;
using Rectangle = System.Drawing.Rectangle;
using System.Windows.Interop;
namespace SnipOCR
{
/// <summary>
/// SnapWin.xaml 的交互逻辑
/// </summary>
///
public partial class SnapWin : Window
{
public delegate void SendMessage(Bitmap bitmap);
public SendMessage sendMessage;
#region Win32 API
[DllImport("user32.dll")]
static extern IntPtr GetDC(IntPtr ptr);
[DllImport("gdi32.dll")]
static extern int GetDeviceCaps(
IntPtr hdc, // handle to DC
int nIndex // index of capability
);
[DllImport("user32.dll", EntryPoint = "ReleaseDC")]
static extern IntPtr ReleaseDC(IntPtr hWnd, IntPtr hDc);
#endregion
#region DeviceCaps常量
const int HORZRES = 8;
const int VERTRES = 10;
const int LOGPIXELSX = 88;
const int LOGPIXELSY = 90;
const int DESKTOPVERTRES = 117;
const int DESKTOPHORZRES = 118;
#endregion
private bool IsSnap = false;
private Bitmap ScreenShot;
public SnapWin(Bitmap bitmap)
{
InitializeComponent();
this.Topmost = true;
ScreenShot = bitmap;
MouseDown += Ink_MouseDown;
MouseUp += Ink_MouseUp;
MouseMove += Ink_MouseMove;
MouseRightButtonDown += Ink_MouseRightButtonDown;
BitmapSource source;
IsSnap = true;
try
{
source = Imaging.CreateBitmapSourceFromHBitmap(ScreenShot.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
}
catch
{
source = null;
}
ImageBrush b = new ImageBrush();
b.ImageSource = source;
b.Stretch = Stretch.Fill;
Ink.Background = b;
Ink.EditingMode = InkCanvasEditingMode.None;
this.WindowStartupLocation = WindowStartupLocation.Manual;
this.Visibility = Visibility.Visible;
this.Left = 0;
this.Top = 0;
this.Height = SystemParameters.VirtualScreenHeight;
this.Width = SystemParameters.VirtualScreenWidth;
}
private bool ISDown = false; // 判断是否鼠标左键按下
System.Windows.Point Startpoint = new System.Windows.Point();
private void Ink_MouseDown(object sender, MouseButtonEventArgs e)
{
ISDown = true;
Conf.Visibility = Visibility.Collapsed;
Startpoint = Mouse.GetPosition(this);
}
Rect rect;
/// <summary>
/// 根据鼠标移动画出矩形标识区域
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Ink_MouseMove(object sender, MouseEventArgs e)
{
if (IsSnap)
{
if (ISDown == true)
{
System.Windows.Point point = Mouse.GetPosition(this);
rect = new Rect(Startpoint, point);
Bord.Visibility = Visibility.Visible;
Bord.Margin = new Thickness(rect.Left, rect.Top, 0, 0);
Bord.Height = rect.Height;
Bord.Width = rect.Width;
}
}
}
Rect snapArea = new Rect();
private void Ink_MouseUp(object sender, MouseButtonEventArgs e)
{
if (IsSnap)
{
ISDown = false;
if (Bord.Visibility == Visibility.Collapsed)
{
Conf.Visibility = Visibility.Collapsed;
}
else
{
Conf.Visibility = Visibility.Visible;
}
if (rect.Width >= 10 && rect.Height >= 10)
{
if (System.Windows.Forms.SystemInformation.VirtualScreen.Height - rect.Bottom >= 0)
{
snapArea = rect;
Conf.Margin = new Thickness(rect.Right - 40, rect.Bottom, 0, 0);
}
else
{
snapArea = rect;
Conf.Margin = new Thickness(rect.Left + 10, rect.Top + 22, 0, 0);
}
}
}
}
private void Ink_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
ISDown = false;
Conf.Visibility = Visibility.Collapsed;
Bord.Visibility = Visibility.Collapsed;
}
private void Window_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
if (e.KeyStates == Keyboard.GetKeyStates(Key.Escape))
{
ISDown = false;
Conf.Visibility = Visibility.Collapsed;
Bord.Visibility = Visibility.Collapsed;
this.Close();
}
}
/// <summary>
/// 确认区域
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Conf_Click(object sender, RoutedEventArgs e)
{
if (IsSnap)
{
double dpi = getDpi();
Rectangle rectangle = new Rectangle((int)((snapArea.X + 2) * dpi), (int)((snapArea.Y + 2) * dpi), (int)((snapArea.Width - 4) * dpi), (int)((snapArea.Height - 4) * dpi));
var bitmap1 = new Bitmap((int)(snapArea.Width * dpi), (int)(snapArea.Height * dpi), System.Drawing.Imaging.PixelFormat.Format32bppArgb);
using (Graphics g = Graphics.FromImage(bitmap1))
{
g.CopyFromScreen(rectangle.X, rectangle.Y, 0, 0, rectangle.Size, CopyPixelOperation.SourceCopy);
}
this.Cursor = Cursors.Wait;
sendMessage(bitmap1);
this.Close();
}
}
/// <summary>
/// 获取系统dpi,这一步很重要
/// </summary>
/// <returns></returns>
double getDpi()
{
double dDpi = 1;
IntPtr desktopDc = GetDC(IntPtr.Zero);
float horizontalDPI = GetDeviceCaps(desktopDc, LOGPIXELSX);
float verticalDPI = GetDeviceCaps(desktopDc, LOGPIXELSY);
int dpi = (int)(horizontalDPI + verticalDPI) / 2;
dDpi = 1 + ((dpi - 96) / 24) * 0.25;
if (dDpi < 1)
{
dDpi = 1;
}
ReleaseDC(IntPtr.Zero, desktopDc);
return dDpi;
}
}
}
- 窗口创建函数
public SnapWin(Bitmap bitmap){...}
中使用了参数,所以在创建这个窗口的时候记得加上这个参数,该参数即为获取到的整个屏幕的图像double getDpi(){...}
这个函数主要的作用是获取当前屏幕的缩放比,这一点很重要,否则你获得的截图区域可能与你的预期有着很大的区别。(因为屏幕缩放比为200%,导致截图反复失败后获得的经验之谈)- 在该程序中如果需要退出截图需要按下ESC键。
public delegate void SendMessage(Bitmap bitmap);
public SendMessage sendMessage;
是用来实现窗口间传值,将截图区域的图像以sendMessage(截图区域图像);
的形式发送到主窗口。- 控件选用的是InkCanvas,因为它可以添加墨迹,如果对于这方面感兴趣的,可以学习一下相关内容
主窗口
/// <summary>
/// 点击截图并识别
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Snap_Click(object sender, RoutedEventArgs e)
{
this.WindowState = WindowState.Minimized;
SnapWin snapWin = new SnapWin(GetScreen())
{
sendMessage = GetSnapArea // 主窗口接受子窗口发送的值的函数为GetSnapArea
};
snapWin.ShowDialog();
this.WindowState = WindowState.Normal;
}
/// <summary>
/// 获取截屏区域的文字
/// </summary>
/// <param name="bitmap"></param>
private void GetSnapArea(Bitmap bitmap)
{
string str = OCRApi.getFileBase64(bitmap);
string text = OCRApi.GetWords(str,true);
string CaText = OCRApi.Str2Json(text);
this.Visibility = Visibility.Visible;
}
- 在
private void GetSnapArea(Bitmap bitmap) {...}
函数中,我使用了自己重写的对图像进行文字识别的函数。
/// <summary>
/// 转换为用 Base64 数字编码的字符串表示形式(重载)
/// </summary>
/// <param name="bitmap">图像文件</param>
/// <returns>字符串</returns>
public static String getFileBase64(Bitmap bitmap)
{
using (MemoryStream stream = new MemoryStream())
{
bitmap.Save(stream, System.Drawing.Imaging.ImageFormat.Jpeg);
byte[] data = new byte[stream.Length];
stream.Seek(0, SeekOrigin.Begin);
stream.Read(data, 0, Convert.ToInt32(stream.Length));
String str = Convert.ToBase64String(data);
return str;
}
}
/// <summary>
/// 直接上传base64的字符串,获得内容
/// </summary>
/// <param name="base64"></param>
/// <param name="a">标识符(无明确含义)</param>
/// <returns></returns>
public static string GetWords(string base64, bool a)
{
string token = getAccessToken();
string host = "https://aip.baidubce.com/rest/2.0/ocr/v1/general_basic?access_token=" + token;
Encoding encoding = Encoding.Default;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(host);
request.Method = "post";
request.KeepAlive = true;
String str = "image=" + HttpUtility.UrlEncode(base64);
byte[] buffer = encoding.GetBytes(str);
request.ContentLength = buffer.Length;
request.GetRequestStream().Write(buffer, 0, buffer.Length);
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8);
string result = reader.ReadToEnd();
return result;
}
四、结语
从演示中可以看出我已经实现了不少功能,这些功能将在接下来的文章内向大家介绍。