C#WPF制作仿QQ截图工具

本文是讲述使用C# WPF制作仿QQ截图工具的方法。


1. 注册快捷键

QQ的截图工具,当我们按下Ctrl + Alt + A键的时候就可以激活截图程序。

首先第一步就是要注册快捷键。这里需要引用到“user32.dll”。对于Win32的API,调用起来还是需要dllimport的。

我们声明一个Hotkey类,导入相应的方法。

    class HotKey
    {
        //调用WIN32的API
        [DllImport("user32.dll", SetLastError = true)]
        //声明注册快捷键方法,方法实体dll中。参数为窗口句柄,快捷键自定义ID,Ctrl,Shift等功能键,其他按键。
        public static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vk);
        [DllImport("user32.dll", SetLastError = true)]
        //注销快捷键方法的声明。
        public static extern bool UnregisterHotKey(IntPtr hWnd, int id);
    }

在程序开始,Windows_Loaded方法中就要对快捷键进行注册。

方法是首先获取窗口句柄。可能C#的程序员对于句柄这个概念比较陌生,因为语言的高度封装。但是因为我们调用的是Win32的方法,还是要自己一步一步去做的。

然后再注册表中注册一个键值,添加hook监听窗口事件。通过重写winproc,相应键盘快捷键。

这一部分都是Win32程序设计的内容。

/// 
/// 窗体建立完成时调用
/// 
/// 
/// 
private void Window_Loaded(object sender, RoutedEventArgs e)
{
    Handle = new WindowInteropHelper(this).Handle;  //获取窗口句柄
    RunHotKey();  //注册并监听HotKey
}

/// 
/// 添加快捷键监听
/// 
private void RunHotKey()
{
    RegisterHotKey();  //注册截图快捷键
    HwndSource source = HwndSource.FromHwnd(Handle);
    if (source != null)
        source.AddHook(WndProc);  //添加Hook,监听窗口事件
}

/// 
/// 注册快捷键
/// 
private void RegisterHotKey()
{
    //101为快捷键自定义ID,0x0002为Ctrl键, 0x0001为Alt键,或运算符|表同时按住两个键有效,0x41为A键。
    bool isRegistered = HotKey.RegisterHotKey(Handle, 101, (0x0002 | 0x0001), 0x41);
    if (isRegistered == false)
    {
        System.Windows.MessageBox.Show("截图快捷键Ctrl+Alt+A被占用", "警告", MessageBoxButton.OK, MessageBoxImage.Warning);
    }
}

/// 
/// 重写WndProc函数,类型为虚保护,响应窗体消息事件
/// 
/// 
/// 消息内容
/// 
/// 
/// 是否相应完成
/// 
protected virtual IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
    switch (msg)
    {
        //0x0312表示事件消息为按下快捷键
        case 0x0312:
            CatchScreen();
            break;
    }
    return IntPtr.Zero;
}


2. 程序思路

当我们使用QQ截图时,一开始会有一个灰色的遮罩,然后鼠标按下后移动,建立选区,选区中遮罩消失并可以拖动选区。

为了实现这一功能,我们在设置遮罩时可以考虑如下方法:

C#WPF制作仿QQ截图工具_第1张图片

首先拷贝当前屏幕作为底层。定义4个遮罩层,当鼠标按下后,捕捉鼠标移动的位置,实时调整遮罩区的大小和位置。

选取就是没有遮罩而露出底层的部分。


3. 初始化遮罩

我制作的时候遮罩和底层都使用的是Canvas。

初始化时,将整个屏幕拷贝到底层,添加黑色遮罩。

/// 
/// 获取本机分辨率
/// 
private void GetScreenSize()
{
    Width = SystemParameters.PrimaryScreenWidth;
    Height = SystemParameters.PrimaryScreenHeight;
}

/// 
/// 初始化截图,截取把整个屏幕并显示
/// 
private void InitializeImage()
{
    GetScreenSize();
    image = new Bitmap(Convert.ToInt32(Width), Convert.ToInt32(Height));  //设置截图区域大小为整个屏幕
    using (Graphics g = Graphics.FromImage(image))
    {
        g.CopyFromScreen(0, 0, 0, 0, new System.Drawing.Size(Convert.ToInt32(Width), Convert.ToInt32(Height)));  //复制当前屏幕到画板上,即将截屏图片的内容设置为当前屏幕
    }
    BitmapSource bimage = BitmapToBitmapSource(image);
    ImageBrush b = new ImageBrush();
    b.ImageSource = bimage;
    b.Stretch = Stretch.None;
    this.Background = b;  //将截屏设为背景
    mask.Height = Height;
    mask.Width = Width;
    InitializeMask();  //添加黑色遮罩
    CompositionTarget.Rendering += UpdateSelection;  //注册窗体重绘事件
}
上方的代码中涉及到了一个Bitmap与BitmapSource的转化,因为在WPF程序中控件只能用BitmapSource,所以会比较麻烦,如果是做WInform则直接使用Bitmap。

添加黑色遮罩的代码在此略去,就是在底层Canva中添加4个Canva作为遮罩层。


4. 鼠标动作捕捉

截图中涉及到的鼠标动作为:鼠标按下表示开始截图,按下后拖动表示改变选区,鼠标放开表示完成截图,完成截图后鼠标按下拖动选区,在选取边缘按下鼠标后移动缩放选区。

对于这些事件,只需要对于底层Canvas的MouseLeftButtonDown,MouseMove和MouseLeftButtonUp事件进行处理即可。处理时记录状态是正在截图还是截图完成后调整大小。

对于这些事件的处理就只是根据当前鼠标位置改变4个Canvas遮罩区的大小,代码省略。


5.选区框的制作

设计如下图所示的选取框的方法:

 C#WPF制作仿QQ截图工具_第2张图片
鼠标按下时记录起始位置,鼠标放开记录终止位置。根据这两个位置可以确定一个矩形,即上图中的选区。矩形有4个顶点和边的4个中点处分别画小矩形(System.Windows.Shapes.Rectangle)。记录矩形的中间位置。设定鼠标移动到特定位置时光标的图案。


6. 保存图片

根据选区位置,直接切割底层显示的原始图片,获取截图,然后保存。这一部分没什么说的,直接看代码。

/// 
/// 保存图片
/// 
private void SaveImage()
{
    //用当前时间作为文件名
    string time = DateTime.Now.ToString();
    //去除时间中的非法字符
    string filename = "截图";
    foreach (char symbol in time)
    {
        if (symbol != '/' && symbol != ':' && symbol != ' ')
            filename += symbol;
    }
    if (StartPoint == FinalPoint)
    {
        System.Windows.MessageBox.Show("未选择任何像素", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
        return;
    }
    GetImage();
    SaveFileDialog f = new SaveFileDialog();
    f.Filter = "位图格式(*.bmp)|*.bmp|增强型图元文件(*.wmf)|*.wmf|可交换图像文件(*.exif)|*.exif|图形交换格式(*.gif)|*.gif|Windows图标图像格式(*.ico)|*.ico|联合图像专家组(*.jpeg)|*.jpeg|W3C可移植网络图形(*.png)|*.png|标记图像文件格式(*.tiff)|*.tiff|Windows图元文件(*.wmf)|*.wmf";
    f.FilterIndex = 6;
    f.RestoreDirectory = true;
    f.FileName = filename;
    f.Title = "保存截图";
    System.Windows.Forms.DialogResult b = f.ShowDialog();
    if (b == System.Windows.Forms.DialogResult.OK)
    {
        switch (f.FilterIndex)
        {
            case 1:
                bimage.Save(f.FileName, System.Drawing.Imaging.ImageFormat.Bmp);
                break;
            case 2:
                bimage.Save(f.FileName, System.Drawing.Imaging.ImageFormat.Emf);
                break;
            case 3:
                bimage.Save(f.FileName, System.Drawing.Imaging.ImageFormat.Exif);
                break;
            case 4:
                bimage.Save(f.FileName, System.Drawing.Imaging.ImageFormat.Gif);
                break;
            case 5:
                bimage.Save(f.FileName, System.Drawing.Imaging.ImageFormat.Icon);
                break;
            case 6:
                bimage.Save(f.FileName, System.Drawing.Imaging.ImageFormat.Jpeg);
                break;
            case 7:
                bimage.Save(f.FileName, System.Drawing.Imaging.ImageFormat.Png);
                break;
            case 8:
                bimage.Save(f.FileName, System.Drawing.Imaging.ImageFormat.Tiff);
                break;
            case 9:
                bimage.Save(f.FileName, System.Drawing.Imaging.ImageFormat.Wmf);
                break;
        }
        System.Windows.MessageBox.Show("截图已保存", "保存成功", MessageBoxButton.OK);
    }
}

private Bitmap bimage;
/// 
/// 切分并获取选中部分截图
/// 
private void GetImage()
{
    bimage = new Bitmap(Convert.ToInt32(FinalPoint.X - StartPoint.X), Convert.ToInt32(FinalPoint.Y - StartPoint.Y));
    using (Graphics g = Graphics.FromImage(bimage))
    {
        g.DrawImage(image, new System.Drawing.Rectangle(0, 0, Convert.ToInt32(FinalPoint.X - StartPoint.X), Convert.ToInt32(FinalPoint.Y - StartPoint.Y)), new System.Drawing.Rectangle(Convert.ToInt32(StartPoint.X), Convert.ToInt32(StartPoint.Y), Convert.ToInt32(FinalPoint.X - StartPoint.X), Convert.ToInt32(FinalPoint.Y - StartPoint.Y)), GraphicsUnit.Pixel);
        System.Windows.Forms.Clipboard.SetImage(bimage);
    }
}


你可能感兴趣的:(C#)