亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

C#?wpf實(shí)現(xiàn)截屏框熱鍵截屏的示例代碼

 更新時(shí)間:2023年09月07日 09:05:27   作者:CodeOfCC  
這篇文章主要為大家詳細(xì)介紹了C#?wpf實(shí)現(xiàn)截屏框熱鍵截屏的相關(guān)知識(shí),文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,有需要的小伙伴可以學(xué)習(xí)一下

前言

在《C# wpf 使用DockPanel實(shí)現(xiàn)截屏框》中我們實(shí)現(xiàn)了一個(gè)截屏框,接下來就要實(shí)現(xiàn)相應(yīng)的截屏功能了。獲取截屏區(qū)域然后使用GDI+截屏,在這里不少的細(xì)節(jié)需要處理,比如響應(yīng)熱鍵彈出截屏界面、點(diǎn)擊拖出截屏框、截屏區(qū)域任意反向拖動(dòng)、處理不同dpi下的坐標(biāo)位置等等。

一、實(shí)現(xiàn)步驟

1、響應(yīng)熱鍵

我們直接使用win32 api的RegisterHotKey和UnregisterHotKey即可。在Window的SourceInitialized事件中注冊(cè)熱鍵,如下是注冊(cè)alt+d為熱鍵的示例代碼

[System.Runtime.InteropServices.DllImport("user32")]
private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint controlKey, uint virtualKey);
[System.Runtime.InteropServices.DllImport("user32")]
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);

HotKey是對(duì)RegisterHotKey、UnregisterHotKey做了封裝的對(duì)象,網(wǎng)上可以搜到此處略。

 private void Window_SourceInitialized(object sender, EventArgs e)
 {
     //注冊(cè)alt+d熱鍵,0x44為d,其他虛擬鍵值請(qǐng)查看:https://learn.microsoft.com/zh-tw/windows/win32/inputdev/virtual-key-codes
     HotKey k = new HotKey(this, HotKey.KeyFlags.MOD_ALT, 0x44);
     k.OnHotKey += K_OnHotKey;
     Visibility = Visibility.Collapsed;
 }

2、截屏顯示

(1)獲取屏幕區(qū)域

我們需要使用win32 api獲取屏幕區(qū)域,采用wpf的方法取得的屏幕分辨率是基于dpi的,就算是用PointToScreen進(jìn)行轉(zhuǎn)換,在程序運(yùn)行過程中改了系統(tǒng)dpi后依然會(huì)不準(zhǔn)確,所以需要直接取得屏幕的實(shí)際像素分辨率,用于gdi+截屏。

  const int DESKTOPVERTRES = 117;
        const int DESKTOPHORZRES = 118;
        [DllImport("gdi32.dll")]
        static extern int GetDeviceCaps(
   IntPtr hdc, // handle to DC  
   int nIndex // index of capability  
   );
        [DllImport("user32.dll")]
        static extern IntPtr GetDC(IntPtr ptr);
        [DllImport("user32.dll", EntryPoint = "ReleaseDC")]
        static extern IntPtr ReleaseDC(IntPtr hWnd, IntPtr hDc);
        /// <summary>  
        /// 獲取真實(shí)設(shè)置的桌面分辨率大小  
        /// </summary>  
        static Size DESKTOP
        {
            get
            {
                IntPtr hdc = GetDC(IntPtr.Zero);
                Size size = new Size();
                size.Width = GetDeviceCaps(hdc, DESKTOPHORZRES);
                size.Height = GetDeviceCaps(hdc, DESKTOPVERTRES);
                ReleaseDC(IntPtr.Zero, hdc);
                return size;
            }
        }

(2)截取并顯示

利用上面步驟獲取到的截屏區(qū)域,結(jié)合《C# wpf 使用GDI+實(shí)現(xiàn)截屏》里的簡(jiǎn)單截屏即完成。取得Bitmap對(duì)象后,參考我的另一篇文章《C# wpf Bitmap轉(zhuǎn)換成WriteableBitmap(BitmapSource)的方法》將其轉(zhuǎn)換為轉(zhuǎn)換成wpf對(duì)象,然后通過ImageBrush賦值為控件的Background即可以顯示在控件上。

//截屏并顯示到窗口
void Snapshot()
{
    //獲取桌面實(shí)際分辨率,可以解決程序運(yùn)行后修改dpi,截圖區(qū)域不正常的問題
    var leftTop = new Point(0, 0);
    var rightBottom = new Point(DESKTOP.Width, DESKTOP.Height);
    var bitmap = Snapshot((int)leftTop.X, (int)leftTop.Y, (int)(rightBottom.X - leftTop.X), (int)(rightBottom.Y - leftTop.Y));
    var bmp = BitmapToWriteableBitmap(bitmap);
    bitmap.Dispose();
    //顯示到窗口
    grdGlobal.Background = new ImageBrush(bmp);
}

3、自動(dòng)捕獲窗口

qq和微信的截屏都有自動(dòng)捕獲窗口功能,我們也可以自己實(shí)現(xiàn)這種功能。

(1)獲取系統(tǒng)所有窗口

通過win32 api可以枚舉系統(tǒng)所有窗口,我們需要將所有窗口的位置大小記錄下來,網(wǎng)上可以找到WindowList相關(guān)代碼此處略。

//獲取桌面所有窗口
_windows = WindowList.GetAllWindows();
IntPtr hwnd = new WindowInteropHelper(this).Handle;
//去除不可見窗口以及自己
_windows.RemoveAll((ele) => { return !ele.isVisible || ele.Handle == hwnd; });

(2)根據(jù)鼠標(biāo)位置搜索窗口

//窗口是以z順序排列的查找到第一個(gè)匹配的窗口即可
var screenPoint = grdGlobal.PointToScreen(point);
foreach (var window in _windows)
{
    if (window.rect.Contains(screenPoint))
    //獲取在鼠標(biāo)所在區(qū)域的窗口
    {
        try
        {
            if (window.rect.Right > window.rect.Left && window.rect.Bottom > window.rect.Top)
            //
            {
                var topLeft = grdGlobal.PointFromScreen(window.rect.TopLeft);
                var bottomRight = grdGlobal.PointFromScreen(window.rect.BottomRight);
                Thickness thickness = new Thickness(topLeft.X, topLeft.Y, grdGlobal.ActualWidth - bottomRight.X, grdGlobal.ActualHeight - bottomRight.Y);
                 //修正邊界
                if (thickness.Left < 0) thickness.Left = 0;
                if (thickness.Top < 0) thickness.Top = 0;
                if (thickness.Right < 0) thickness.Right = 0;
                if (thickness.Bottom < 0) thickness.Bottom = 0;
                //將截屏框顯示在窗口位置
                leftPanel.Width = thickness.Left;
                topPanel.Height = thickness.Top;
                rightPanel.Width = thickness.Right;
                bottomPanel.Height = thickness.Bottom;
                break;
            }
        }
        catch { }
    }
}

(3)效果預(yù)覽

4、點(diǎn)擊拖出截屏框

出現(xiàn)截屏界面之后,參考qq或微信的實(shí)現(xiàn),第一次點(diǎn)擊是可以拖出截屏框框選的。如果是采樣繪制的方法很簡(jiǎn)單,直接繪制矩形就可以了。但是基于控件要實(shí)現(xiàn)這個(gè)功能需要一定的技巧,在《C# wpf 使用DockPanel實(shí)現(xiàn)截屏框》的基礎(chǔ)上實(shí)現(xiàn)這個(gè)功能。

(1)移動(dòng)到點(diǎn)擊位置

在鼠標(biāo)按下事件或移動(dòng)實(shí)現(xiàn)中

//將截屏框移動(dòng)到點(diǎn)擊位置
leftPanel.Width = p.X;
topPanel.Height = p.Y;
rightPanel.Width = grdGlobal.ActualWidth - p.X;
bottomPanel.Height = grdGlobal.ActualHeight - p.Y;

(2)模擬按下事件

接著上面的代碼,thumb為右下角拖動(dòng)點(diǎn)。

//手動(dòng)觸發(fā)截屏框滑塊拖動(dòng)事件
MouseButtonEventArgs downEvent = new MouseButtonEventArgs(Mouse.PrimaryDevice, Environment.TickCount, MouseButton.Left)
{ RoutedEvent = FrameworkElement.MouseLeftButtonDownEvent };
thumb.RaiseEvent(downEvent);

(3)修正偏移

由于是模擬的點(diǎn)擊事件,可能會(huì)出現(xiàn)鼠標(biāo)不在Thumb上的情況,此時(shí)需要對(duì)thumb位置進(jìn)行修正,在Thumb的DragStarted事件中記錄偏移。

//滑塊需要的偏移量
Point? _thumbOffset;
var thumb = sender as FrameworkElement;
if (!new Rect(0, 0, thumb.ActualWidth, thumb.ActualHeight).Contains(new Point(e.HorizontalOffset, e.VerticalOffset)))
//鼠標(biāo)起始位置超出了控件范圍,則記錄中心點(diǎn)偏移在拖動(dòng)時(shí)修正
{
    _thumbOffset = new Point(e.HorizontalOffset - thumb.ActualWidth / 2, e.VerticalOffset - thumb.ActualHeight / 2);
}

在Thumb的DragDelta事件中添加修正邏輯

var horizontalChange = e.HorizontalChange;
var verticalChange = e.VerticalChange;
if (_thumbOffset != null)
//修正偏移
{
    horizontalChange += _thumbOffset.Value.X;
    verticalChange += _thumbOffset.Value.Y;
}

(4)效果預(yù)覽

5、反向拖動(dòng)

這一步不是必須的,但是有的話操作體驗(yàn)會(huì)更好,比如qq和微信的截圖就支持反向拖動(dòng)。如果我們使用gdi或gdi+繪制截屏框則天然支持反向拖動(dòng),因?yàn)镽ECT的大小可以為負(fù)數(shù)。但是基于控件則有一定的難度了,由于控件寬高不能為負(fù)數(shù),我們需要實(shí)現(xiàn)事件轉(zhuǎn)移機(jī)制,依然是在《C# wpf 使用DockPanel實(shí)現(xiàn)截屏框》的基礎(chǔ)上實(shí)現(xiàn)這個(gè)功能。

(1)判斷邊界

原本《C# wpf 使用DockPanel實(shí)現(xiàn)截屏框》的邏輯的Thumb到了邊界就不進(jìn)行任何操作了,現(xiàn)在要拓展為到達(dá)邊界則進(jìn)行事件轉(zhuǎn)移。橫向的Thumb

if (width >= 0)
{
    leftPanel.Width = left >= 0 ? left : 0;
    rightPanel.Width = right >= 0 ? right : 0;
}
else{
//此處將事件轉(zhuǎn)移到反方向的Thumb
}

縱向的Thumb

if (height >= 0)
{
    topPanel.Height = top >= 0 ? top : 0;
    bottomPanel.Height = bottom >= 0 ? bottom : 0;
}
else
{
//此處將事件轉(zhuǎn)移到反方向的Thumb
}

(2)事件轉(zhuǎn)移

//當(dāng)前的Thumb觸發(fā)鼠標(biāo)彈起事件,結(jié)束拖動(dòng)
MouseButtonEventArgs upEvent = new MouseButtonEventArgs(Mouse.PrimaryDevice, Environment.TickCount, MouseButton.Left)
{ RoutedEvent = FrameworkElement.MouseLeftButtonUpEvent };
thumb.RaiseEvent(upEvent);
//反方向的Thumb觸發(fā)鼠標(biāo)按下事件,開始拖動(dòng)
MouseButtonEventArgs downEvent = new MouseButtonEventArgs(Mouse.PrimaryDevice, Environment.TickCount, MouseButton.Left)
{ RoutedEvent = FrameworkElement.MouseLeftButtonDownEvent };
t.RaiseEvent(downEvent);

(3)修正邊界

完成上述兩步之后已經(jīng)可以做到反向拖動(dòng)了,但是會(huì)有個(gè)問題,當(dāng)多動(dòng)過快的時(shí)截屏框的位置會(huì)發(fā)生移動(dòng),要解決這個(gè)問題則需要在事件轉(zhuǎn)移時(shí)修正邊界位置,即使兩條邊重合。橫向的Thumb

if (thumb.HorizontalAlignment == HorizontalAlignment.Left)
//從左到右轉(zhuǎn)移的修正
{
    leftPanel.Width = grdGlobal.ActualWidth - rightPanel.Width;
}
else
//從右到左轉(zhuǎn)移的修正
{
    rightPanel.Width = grdGlobal.ActualWidth - leftPanel.Width;
}

縱向的Thumb

 if (thumb.VerticalAlignment == VerticalAlignment.Top)
 //從上到下轉(zhuǎn)移的修正
 {
     topPanel.Height = grdGlobal.ActualHeight - bottomPanel.Height;
 }
 else
 //從下到上轉(zhuǎn)移的修正
 {
     bottomPanel.Height = grdGlobal.ActualHeight - topPanel.Height;
 }

(4)效果預(yù)覽

6、截取圖片

由于前面截取是整個(gè)桌面的圖像,保存時(shí)需要根據(jù)截屏框截取畫面,我們使用WriteableBitmap對(duì)象就可以實(shí)現(xiàn)。

//獲取截屏框的圖片
WriteableBitmap GetClipImage()
{
    var bursh = grdGlobal.Background as ImageBrush;
    if (bursh != null)
    {
        //裁剪
        //全屏圖片
        var screenWb = bursh.ImageSource as WriteableBitmap;
        //獲取截取區(qū)域
        var leftTop = clipRect.PointToScreen(new Point(0, 0));
        var rightBottom = clipRect.PointToScreen(new Point(clipRect.ActualWidth, clipRect.ActualHeight));
        var rect = new Int32Rect((int)leftTop.X, (int)leftTop.Y, (int)(rightBottom.X - leftTop.X), (int)(rightBottom.Y - leftTop.Y));
        //創(chuàng)建截取圖片對(duì)象
        var wb = new WriteableBitmap(rect.Width, rect.Height, 0, 0, screenWb.Format, null);
        //寫入截取區(qū)域數(shù)據(jù)
        wb.WritePixels(rect, screenWb.BackBuffer, screenWb.PixelHeight * screenWb.BackBufferStride, screenWb.BackBufferStride, 0, 0);
        return wb;
    }
    return null;
}

7、設(shè)置粘貼板

直接使用Clipboard.SetImage即可,參數(shù)類型為BitmapSource,是WriteableBitmap的基類。

 Clipboard.SetImage(GetClipImage());

二、關(guān)于dpi

1、適配不同dpi

有處理dpi不同的情況,在任意dpi下都能正常截圖。

2、不支持dpi實(shí)時(shí)修改

(1)現(xiàn)象

程序啟動(dòng)后實(shí)時(shí)修改dpi,截屏顯示的畫面會(huì)模糊,主要原因是不同api之間的dpi計(jì)算不統(tǒng)一。系統(tǒng)dpi實(shí)時(shí)修改后wpf界面會(huì)響應(yīng)oloaded自動(dòng)調(diào)整大小,但部分程序內(nèi)部的dpi(比如getWindowRect)是不會(huì)變化的,尤其是渲染圖片依然按照程序啟動(dòng)時(shí)的dpi去計(jì)算,所以會(huì)進(jìn)行縮放,顯示的畫面必然模糊。

這里舉一個(gè)具體的例子流程如下:

win11 分辨率1920x1080

1、初始系統(tǒng)dpi為120(1.25倍)

2、程序啟動(dòng)

3、程序dpi為120

5、全屏窗口大小1536x864,通過winapi獲取則是1920x1080,截屏1920x1080顯示,截屏畫面無損

6、系統(tǒng)dpi設(shè)置為96(1倍)

7、此時(shí)程序dpi為120

8、全屏窗口大小1920x1080,通過winapi獲取則是2400x1350,截屏1920x1080顯示,截屏畫面模糊。

按像素點(diǎn)繪制,畫面顯示在左上角無法充滿窗口。

(2)嘗試的解決方案

筆者采樣了多種方式嘗試解決

1、提前縮放圖片再顯示。

2、參考微軟解決dpi問題的方法。

3、使用gdi+的graphics直接通過hdc以像素點(diǎn)為單位繪制。

4、使用gdi的bitblt進(jìn)行hdc拷貝。

以上方法都沒效果畫面依然模糊。

3、建議

需要支持dpi實(shí)時(shí)改變,可以將截圖功能作為單獨(dú)的程序,響應(yīng)熱鍵后再啟動(dòng)。

三、效果預(yù)覽

1、截屏粘貼到qq

2、截屏保存到文件

總結(jié)

本文介紹了wpf截屏框熱鍵截屏的方法。需要實(shí)現(xiàn)的功能還是比較多的,而且有些功能難度也不小,幾經(jīng)嘗試才找到合適的實(shí)現(xiàn)方法,至于實(shí)時(shí)改變dpi的模糊的問題,這個(gè)目前的結(jié)論是無法解決,這并不是wpf的局限,用c++ mfc也不行,除非存在一個(gè)設(shè)置程序全局dpi的winapi接口筆者沒有發(fā)現(xiàn)。所以這個(gè)問題目前只能通過獨(dú)立程序啟動(dòng)解決。但是總的來說實(shí)現(xiàn)的效果是很不錯(cuò)的,尤其是反向拖動(dòng),通過事件轉(zhuǎn)移的方式實(shí)現(xiàn),界面操作還是很流暢。

到此這篇關(guān)于C# wpf實(shí)現(xiàn)截屏框熱鍵截屏的示例代碼的文章就介紹到這了,更多相關(guān)C# wpf截屏內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論