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程序設計的內容。

/// <summary>
/// 窗體建立完成時調用
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Window_Loaded(object sender, RoutedEventArgs e)
{
    Handle = new WindowInteropHelper(this).Handle;  //獲取窗口句柄
    RunHotKey();  //註冊並監聽HotKey
}

/// <summary>
/// 添加快捷鍵監聽
/// </summary>
private void RunHotKey()
{
    RegisterHotKey();  //註冊截圖快捷鍵
    HwndSource source = HwndSource.FromHwnd(Handle);
    if (source != null)
        source.AddHook(WndProc);  //添加Hook,監聽窗口事件
}

/// <summary>
/// 註冊快捷鍵
/// </summary>
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);
    }
}

/// <summary>
/// 重寫WndProc函數,類型爲虛保護,響應窗體消息事件
/// </summary>
/// <param name="hwnd"></param>
/// <param name="msg">消息內容</param>
/// <param name="wParam"></param>
/// <param name="lParam"></param>
/// <param name="handled">是否相應完成</param>
/// <returns></returns>
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截圖時,一開始會有一個灰色的遮罩,然後鼠標按下後移動,建立選區,選區中遮罩消失並可以拖動選區。

爲了實現這一功能,我們在設置遮罩時可以考慮如下方法:


首先拷貝當前屏幕作爲底層。定義4個遮罩層,當鼠標按下後,捕捉鼠標移動的位置,實時調整遮罩區的大小和位置。

選取就是沒有遮罩而露出底層的部分。


3. 初始化遮罩

我製作的時候遮罩和底層都使用的是Canvas。

初始化時,將整個屏幕拷貝到底層,添加黑色遮罩。

/// <summary>
/// 獲取本機分辨率
/// </summary>
private void GetScreenSize()
{
    Width = SystemParameters.PrimaryScreenWidth;
    Height = SystemParameters.PrimaryScreenHeight;
}

/// <summary>
/// 初始化截圖,截取把整個屏幕並顯示
/// </summary>
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.選區框的製作

設計如下圖所示的選取框的方法:

 
鼠標按下時記錄起始位置,鼠標放開記錄終止位置。根據這兩個位置可以確定一個矩形,即上圖中的選區。矩形有4個頂點和邊的4箇中點處分別畫小矩形(System.Windows.Shapes.Rectangle)。記錄矩形的中間位置。設定鼠標移動到特定位置時光標的圖案。


6. 保存圖片

根據選區位置,直接切割底層顯示的原始圖片,獲取截圖,然後保存。這一部分沒什麼說的,直接看代碼。

/// <summary>
/// 保存圖片
/// </summary>
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;
/// <summary>
/// 切分並獲取選中部分截圖
/// </summary>
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);
    }
}


發佈了56 篇原創文章 · 獲贊 44 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章