使.NET 截图工具兼容多个显示器

发布于 2024-09-28 22:18:01 字数 4649 浏览 7 评论 0原文

此帖子中提供了另一种截图工具解决方案:.NET 等效的截图工具

现在有必要使其适用于选定的屏幕(在多显示器系统上)。

代码已进行相应修改:

Public Class SnippingTool


    Private Shared _Screen As Screen

    Private Shared BitmapSize As Size

    Private Shared Graph As Graphics


    Public Shared Function Snip(ByVal screen As Screen) As Image

        _Screen = screen

        Dim bmp As New Bitmap(screen.Bounds.Width, screen.Bounds.Height, System.Drawing.Imaging.PixelFormat.Format32bppPArgb)

        Dim gr As Graphics = Graphics.FromImage(bmp)

        Graph = gr


        gr.SmoothingMode = Drawing2D.SmoothingMode.None '###

        BitmapSize = bmp.Size


        Using snipper = New SnippingTool(bmp)

            snipper.Location = New Point(screen.Bounds.Left, screen.Bounds.Top)

            If snipper.ShowDialog() = DialogResult.OK Then
                Return snipper.Image
            End If

        End Using

        Return Nothing


    End Function



    Public Sub New(ByVal screenShot As Image)
        InitializeComponent()
        Me.BackgroundImage = screenShot
        Me.ShowInTaskbar = False
        Me.FormBorderStyle = FormBorderStyle.None


        'Me.WindowState = FormWindowState.Maximized

        Me.DoubleBuffered = True
    End Sub
    Public Property Image() As Image
        Get
            Return m_Image
        End Get
        Set(ByVal value As Image)
            m_Image = Value
        End Set
    End Property
    Private m_Image As Image


    Private rcSelect As New Rectangle()
    Private pntStart As Point

    Protected Overrides Sub OnMouseDown(ByVal e As MouseEventArgs)
        ' Start the snip on mouse down
        If e.Button <> MouseButtons.Left Then
            Return
        End If
        pntStart = e.Location
        rcSelect = New Rectangle(e.Location, New Size(0, 0))
        Me.Invalidate()
    End Sub
    Protected Overrides Sub OnMouseMove(ByVal e As MouseEventArgs)
        ' Modify the selection on mouse move
        If e.Button <> MouseButtons.Left Then
            Return
        End If
        Dim x1 As Integer = Math.Min(e.X, pntStart.X)
        Dim y1 As Integer = Math.Min(e.Y, pntStart.Y)
        Dim x2 As Integer = Math.Max(e.X, pntStart.X)
        Dim y2 As Integer = Math.Max(e.Y, pntStart.Y)
        rcSelect = New Rectangle(x1, y1, x2 - x1, y2 - y1)
        Me.Invalidate()
    End Sub


    Protected Overrides Sub OnMouseUp(ByVal e As MouseEventArgs)
        ' Complete the snip on mouse-up
        If rcSelect.Width <= 0 OrElse rcSelect.Height <= 0 Then
            Return
        End If
        Image = New Bitmap(rcSelect.Width, rcSelect.Height)
        Using gr As Graphics = Graphics.FromImage(Image)
            gr.DrawImage(Me.BackgroundImage, New Rectangle(0, 0, Image.Width, Image.Height), rcSelect, GraphicsUnit.Pixel)
        End Using
        DialogResult = DialogResult.OK
    End Sub
    Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)
        ' Draw the current selection
        Using br As Brush = New SolidBrush(Color.FromArgb(120, Color.White))
            Dim x1 As Integer = rcSelect.X
            Dim x2 As Integer = rcSelect.X + rcSelect.Width
            Dim y1 As Integer = rcSelect.Y
            Dim y2 As Integer = rcSelect.Y + rcSelect.Height
            e.Graphics.FillRectangle(br, New Rectangle(0, 0, x1, Me.Height))
            e.Graphics.FillRectangle(br, New Rectangle(x2, 0, Me.Width - x2, Me.Height))
            e.Graphics.FillRectangle(br, New Rectangle(x1, 0, x2 - x1, y1))
            e.Graphics.FillRectangle(br, New Rectangle(x1, y2, x2 - x1, Me.Height - y2))
        End Using
        Using pen As New Pen(Color.Red, 3)
            e.Graphics.DrawRectangle(pen, rcSelect)
        End Using
    End Sub
    Protected Overrides Function ProcessCmdKey(ByRef msg As Message, ByVal keyData As Keys) As Boolean
        ' Allow canceling the snip with the Escape key
        If keyData = Keys.Escape Then
            Me.DialogResult = DialogResult.Cancel
        End If
        Return MyBase.ProcessCmdKey(msg, keyData)
    End Function

    Protected Overrides Sub OnLoad(ByVal e As System.EventArgs)
        MyBase.OnLoad(e)
        Me.Size = New Size(_Screen.Bounds.Width, _Screen.Bounds.Height)
        Dim area = _Screen.WorkingArea
        Graph.CopyFromScreen(area.X, area.Y, area.Y, area.Y, BitmapSize)
    End Sub

End Class

但它拒绝按预期工作。无论“截图”功能中的“屏幕”参数如何,截图不会出现在所选屏幕上,而是出现在第一个屏幕上。 如何使其正常工作?

更新:最新的截图版本出现在正确的屏幕上,但空白。

更新X2:上面的代码已更新以反映最新版本,现在可以正常工作。

An alternative snipping tool solution was provided in this posting: .NET Equivalent of Snipping Tool

Now it's necessary to make it work for selected screens (on multi-monitor systems).

The code has been modified accordingly:

Public Class SnippingTool


    Private Shared _Screen As Screen

    Private Shared BitmapSize As Size

    Private Shared Graph As Graphics


    Public Shared Function Snip(ByVal screen As Screen) As Image

        _Screen = screen

        Dim bmp As New Bitmap(screen.Bounds.Width, screen.Bounds.Height, System.Drawing.Imaging.PixelFormat.Format32bppPArgb)

        Dim gr As Graphics = Graphics.FromImage(bmp)

        Graph = gr


        gr.SmoothingMode = Drawing2D.SmoothingMode.None '###

        BitmapSize = bmp.Size


        Using snipper = New SnippingTool(bmp)

            snipper.Location = New Point(screen.Bounds.Left, screen.Bounds.Top)

            If snipper.ShowDialog() = DialogResult.OK Then
                Return snipper.Image
            End If

        End Using

        Return Nothing


    End Function



    Public Sub New(ByVal screenShot As Image)
        InitializeComponent()
        Me.BackgroundImage = screenShot
        Me.ShowInTaskbar = False
        Me.FormBorderStyle = FormBorderStyle.None


        'Me.WindowState = FormWindowState.Maximized

        Me.DoubleBuffered = True
    End Sub
    Public Property Image() As Image
        Get
            Return m_Image
        End Get
        Set(ByVal value As Image)
            m_Image = Value
        End Set
    End Property
    Private m_Image As Image


    Private rcSelect As New Rectangle()
    Private pntStart As Point

    Protected Overrides Sub OnMouseDown(ByVal e As MouseEventArgs)
        ' Start the snip on mouse down
        If e.Button <> MouseButtons.Left Then
            Return
        End If
        pntStart = e.Location
        rcSelect = New Rectangle(e.Location, New Size(0, 0))
        Me.Invalidate()
    End Sub
    Protected Overrides Sub OnMouseMove(ByVal e As MouseEventArgs)
        ' Modify the selection on mouse move
        If e.Button <> MouseButtons.Left Then
            Return
        End If
        Dim x1 As Integer = Math.Min(e.X, pntStart.X)
        Dim y1 As Integer = Math.Min(e.Y, pntStart.Y)
        Dim x2 As Integer = Math.Max(e.X, pntStart.X)
        Dim y2 As Integer = Math.Max(e.Y, pntStart.Y)
        rcSelect = New Rectangle(x1, y1, x2 - x1, y2 - y1)
        Me.Invalidate()
    End Sub


    Protected Overrides Sub OnMouseUp(ByVal e As MouseEventArgs)
        ' Complete the snip on mouse-up
        If rcSelect.Width <= 0 OrElse rcSelect.Height <= 0 Then
            Return
        End If
        Image = New Bitmap(rcSelect.Width, rcSelect.Height)
        Using gr As Graphics = Graphics.FromImage(Image)
            gr.DrawImage(Me.BackgroundImage, New Rectangle(0, 0, Image.Width, Image.Height), rcSelect, GraphicsUnit.Pixel)
        End Using
        DialogResult = DialogResult.OK
    End Sub
    Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)
        ' Draw the current selection
        Using br As Brush = New SolidBrush(Color.FromArgb(120, Color.White))
            Dim x1 As Integer = rcSelect.X
            Dim x2 As Integer = rcSelect.X + rcSelect.Width
            Dim y1 As Integer = rcSelect.Y
            Dim y2 As Integer = rcSelect.Y + rcSelect.Height
            e.Graphics.FillRectangle(br, New Rectangle(0, 0, x1, Me.Height))
            e.Graphics.FillRectangle(br, New Rectangle(x2, 0, Me.Width - x2, Me.Height))
            e.Graphics.FillRectangle(br, New Rectangle(x1, 0, x2 - x1, y1))
            e.Graphics.FillRectangle(br, New Rectangle(x1, y2, x2 - x1, Me.Height - y2))
        End Using
        Using pen As New Pen(Color.Red, 3)
            e.Graphics.DrawRectangle(pen, rcSelect)
        End Using
    End Sub
    Protected Overrides Function ProcessCmdKey(ByRef msg As Message, ByVal keyData As Keys) As Boolean
        ' Allow canceling the snip with the Escape key
        If keyData = Keys.Escape Then
            Me.DialogResult = DialogResult.Cancel
        End If
        Return MyBase.ProcessCmdKey(msg, keyData)
    End Function

    Protected Overrides Sub OnLoad(ByVal e As System.EventArgs)
        MyBase.OnLoad(e)
        Me.Size = New Size(_Screen.Bounds.Width, _Screen.Bounds.Height)
        Dim area = _Screen.WorkingArea
        Graph.CopyFromScreen(area.X, area.Y, area.Y, area.Y, BitmapSize)
    End Sub

End Class

But it refuses to work as expected. The snipper doesn't appear on the selected screen, instead it appears on the first one, regardless of the "screen" parameter in "Snip" function.
How to make it work correctly?

UPDATE: The latest snipper version appears on correct screen, but blank.

UPDATE X2 : The code above has been updated to reflect the latest version, which now works properly.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

抽个烟儿 2024-10-05 22:18:01

LoveDotNet,我相信您的源代码有一个小问题,以下行:

Graph.CopyFromScreen(area.X,area.Y,area.Y,area.Y,BitmapSize)

应该是:

Graph.CopyFromScreen(area.X, area.Y, 0, 0, BitmapSize)

另外,对于任何想要使用此代码的人来说,这是一个快速提示,您可以像下面这样调用它:

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    Dim img As Image = SnippingTool.Snip(Screen.AllScreens(0)) 'Set to (1) for secondary monitor'
End Sub

另外,当当您创建 SnippingTool 表单时,请务必将 StartPosition 属性更改为 Manual

大编辑:

我做了一些工作来同时支持多个显示器,这不需要开发人员选择要使用的显示器(这克隆了“截图工具”)。

基本上,我会遍历所有屏幕来查找最小的 XY 坐标,以及最大的 RightBottom< /code>,这让我们可以评估“虚拟监视器”的完整大小:

我已经使用我的配置对其进行了测试,配置为:

Primary 1280x800

Secondary 1280x1024 w/ -224 X offset< /code>

在此处输入图像描述

代码:

'SnippingTool 代码:将其放入新表单中(将启动属性设置为手动)'
公共类截图工具


私人共享_屏幕作为屏幕

私有共享 BitmapSize 作为大小

私有共享图作为图形

私有结构 MultiScreenSize
    Dim minX 作为整数
    将 minY 变暗为整数
    Dim maxRight 作为整数
    将 maxBottom 变暗为整数
末端结构


私有共享函数 FindMultiScreenSize() As MultiScreenSize

    Dim minX As Integer = Screen.AllScreens(0).Bounds.X
    Dim minY As Integer = Screen.AllScreens(0).Bounds.Y

    Dim maxRight As Integer = Screen.AllScreens(0).Bounds.Right
    Dim maxBottom As Integer = Screen.AllScreens(0).Bounds.Bottom

    对于每个 aScreen 作为 Screen In Screen.AllScreens
        如果 aScreen.Bounds.X < minX 然后
            minX = aScreen.Bounds.X
        结束如果

        如果 aScreen.Bounds.Y <分钟Y 那么
            minY = aScreen.Bounds.Y
        结束如果

        如果 aScreen.Bounds.Right >最大此时
            maxRight = aScreen.Bounds.Right
        结束如果

        如果 aScreen.Bounds.Bottom >那么最大底部
            maxBottom = aScreen.Bounds.Bottom
        结束如果
    下一个
    将 m_MultiScreenSize 调暗为 MultiScreenSize
    使用 m_MultiScreenSize
        .minX = minX
        .minY = minY
        .maxBottom = 最大底部
        .maxRight = 最大Right
    结束于
    返回m_MultiScreenSize
结束功能
公共共享函数 Snip() As Image
    Dim m_MultiScreenSize As MultiScreenSize = FindMultiScreenSize()

    调暗 bmp 作为新位图(m_MultiScreenSize.maxRight - m_MultiScreenSize.minX, _
                          m_MultiScreenSize.maxBottom - m_MultiScreenSize.minY, _
                          System.Drawing.Imaging.PixelFormat.Format32bppPArgb)
    Dim gr As Graphics = Graphics.FromImage(bmp)

    图=gr


    gr.SmoothingMode = Drawing2D.SmoothingMode.None 
    位图大小 = bmp.Size


    使用snipper = New SnippingTool(bmp)

        snipper.Location = 新点(m_MultiScreenSize.minX, m_MultiScreenSize.minY)

        如果 snipper.ShowDialog() = DialogResult.OK 那么
            返回狙击手.图片
        结束如果

    结束使用

    不返回任何内容


结束功能



公共子新(ByVal screenShot As Image)
    初始化组件()
    Me.BackgroundImage = 屏幕截图
    Me.ShowInTaskbar = False
    Me.FormBorderStyle = FormBorderStyle.None

    Me.DoubleBuffered = True
结束子
公共属性 Image() 作为图像
    得到
        返回m_Image
    结束获取
    设置(ByVal 值作为图像)
        m_Image = 值
    结束组
结束财产
私有 m_Image 作为图像


私有 rcSelect 作为新矩形()
私有 pntStart 作为点

受保护的覆盖 Sub OnMouseDown(ByVal e As MouseEventArgs)
    '按下鼠标开始剪辑'
    如果 e.Button <> MouseButtons.Left 然后
        返回
    结束如果
    pntStart = e.Location
    rcSelect = 新矩形(e.位置, 新大小(0, 0))
    Me.Invalidate()
结束子
受保护的覆盖 Sub OnMouseMove(ByVal e As MouseEventArgs)
    '修改鼠标移动时的选择'
    如果 e.Button <> MouseButtons.Left 然后
        返回
    结束如果
    Dim x1 As Integer = Math.Min(eX, pntStart.X)
    Dim y1 As Integer = Math.Min(eY, pntStart.Y)
    Dim x2 As Integer = Math.Max(eX, pntStart.X)
    Dim y2 As Integer = Math.Max(eY, pntStart.Y)
    rcSelect = 新矩形(x1, y1, x2 - x1, y2 - y1)
    Me.Invalidate()
结束子


受保护的覆盖 Sub OnMouseUp(ByVal e As MouseEventArgs)
    ' 完成鼠标悬停的截图'
    如果 rcSelect.Width <= 0 或者 rcSelect.Height <= 0 则
        返回
    结束如果
    图像 = 新位图(rcSelect.Width, rcSelect.Height)
    使用 gr 作为图形 = Graphics.FromImage(Image)
        gr.DrawImage(Me.BackgroundImage, 新矩形(0, 0, Image.Width, Image.Height), _
                     rcSelect、GraphicsUnit.Pixel)
    结束使用
    对话框结果=对话框结果.确定
结束子
受保护的覆盖 Sub OnPaint(ByVal e As PaintEventArgs)
    '绘制当前选择'
    使用 br 作为画笔 = New SolidBrush(Color.FromArgb(120, Color.White))
        Dim x1 As Integer = rcSelect.X
        Dim x2 As Integer = rcSelect.X + rcSelect.Width
        Dim y1 As Integer = rcSelect.Y
        Dim y2 As Integer = rcSelect.Y + rcSelect.Height
        e.Graphics.FillRectangle(br, 新矩形(0, 0, x1, Me.Height))
        e.Graphics.FillRectangle(br, 新矩形(x2, 0, Me.Width - x2, Me.Height))
        e.Graphics.FillRectangle(br, 新矩形(x1, 0, x2 - x1, y1))
        e.Graphics.FillRectangle(br, 新矩形(x1, y2, x2 - x1, Me.Height - y2))
    结束使用
    使用笔作为新笔(颜色.红色,3)
        e.Graphics.DrawRectangle(pen, rcSelect)
    结束使用
结束子
受保护的覆盖函数 ProcessCmdKey(ByRef msg As Message, ByVal keyData As Keys) As Boolean
    '允许使用 Escape 键取消剪辑'
    如果 keyData = Keys.Escape 那么
        Me.DialogResult = DialogResult.Cancel
    结束如果
    返回 MyBase.ProcessCmdKey(msg, keyData)
结束功能

受保护的覆盖 Sub OnLoad(ByVal e As System.EventArgs)
    MyBase.OnLoad(e)
    Dim m_MultiScreenSize As MultiScreenSize = FindMultiScreenSize()
    Me.Size = 新尺寸(m_MultiScreenSize.maxRight - m_MultiScreenSize.minX, _
                       m_MultiScreenSize.maxBottom - m_MultiScreenSize.minY)

    Graph.CopyFromScreen(m_MultiScreenSize.minX, m_MultiScreenSize.minY, 0, 0, BitmapSize)
结束子
 结束课程

你可以这样称呼它:

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    Dim img As Image = SnippingTool.Snip()
    img.Save("C:\ScreenShot.png")
End Sub

LoveDotNet, I believe there is a small problem with your source code, the following line:

Graph.CopyFromScreen(area.X, area.Y, area.Y, area.Y, BitmapSize)

Should be:

Graph.CopyFromScreen(area.X, area.Y, 0, 0, BitmapSize)

Also, just a quick tip to anyone who wants to use this code you can invoke it like the following:

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    Dim img As Image = SnippingTool.Snip(Screen.AllScreens(0)) 'Set to (1) for secondary monitor'
End Sub

Also, when you create your SnippingTool form, be sure to change the StartPosition property to Manual.

Big Edit:

I did some work to support multiple monitors at once, which doesn't require the developer to select which monitor to use (this clones the "Snipping Tool" a little closer).

Basically I'm iterating through all of the screens to find the minimum X and Y coordinates, and the largest Right and Bottom, this lets us evaluate the full size of the "Virtual Monitor":

I've tested it with my configuration which is:

Primary 1280x800

Secondary 1280x1024 w/ -224 X offset

enter image description here

Code:

'SnippingTool Code: Place this in a new form (set the StartUp Property to Manual)'
Public Class SnippingTool


Private Shared _Screen As Screen

Private Shared BitmapSize As Size

Private Shared Graph As Graphics

Private Structure MultiScreenSize
    Dim minX As Integer
    Dim minY As Integer
    Dim maxRight As Integer
    Dim maxBottom As Integer
End Structure


Private Shared Function FindMultiScreenSize() As MultiScreenSize

    Dim minX As Integer = Screen.AllScreens(0).Bounds.X
    Dim minY As Integer = Screen.AllScreens(0).Bounds.Y

    Dim maxRight As Integer = Screen.AllScreens(0).Bounds.Right
    Dim maxBottom As Integer = Screen.AllScreens(0).Bounds.Bottom

    For Each aScreen As Screen In Screen.AllScreens
        If aScreen.Bounds.X < minX Then
            minX = aScreen.Bounds.X
        End If

        If aScreen.Bounds.Y < minY Then
            minY = aScreen.Bounds.Y
        End If

        If aScreen.Bounds.Right > maxRight Then
            maxRight = aScreen.Bounds.Right
        End If

        If aScreen.Bounds.Bottom > maxBottom Then
            maxBottom = aScreen.Bounds.Bottom
        End If
    Next
    Dim m_MultiScreenSize As MultiScreenSize
    With m_MultiScreenSize
        .minX = minX
        .minY = minY
        .maxBottom = maxBottom
        .maxRight = maxRight
    End With
    Return m_MultiScreenSize
End Function
Public Shared Function Snip() As Image
    Dim m_MultiScreenSize As MultiScreenSize = FindMultiScreenSize()

    Dim bmp As New Bitmap(m_MultiScreenSize.maxRight - m_MultiScreenSize.minX, _
                          m_MultiScreenSize.maxBottom - m_MultiScreenSize.minY, _
                          System.Drawing.Imaging.PixelFormat.Format32bppPArgb)
    Dim gr As Graphics = Graphics.FromImage(bmp)

    Graph = gr


    gr.SmoothingMode = Drawing2D.SmoothingMode.None 
    BitmapSize = bmp.Size


    Using snipper = New SnippingTool(bmp)

        snipper.Location = New Point(m_MultiScreenSize.minX, m_MultiScreenSize.minY)

        If snipper.ShowDialog() = DialogResult.OK Then
            Return snipper.Image
        End If

    End Using

    Return Nothing


End Function



Public Sub New(ByVal screenShot As Image)
    InitializeComponent()
    Me.BackgroundImage = screenShot
    Me.ShowInTaskbar = False
    Me.FormBorderStyle = FormBorderStyle.None

    Me.DoubleBuffered = True
End Sub
Public Property Image() As Image
    Get
        Return m_Image
    End Get
    Set(ByVal value As Image)
        m_Image = Value
    End Set
End Property
Private m_Image As Image


Private rcSelect As New Rectangle()
Private pntStart As Point

Protected Overrides Sub OnMouseDown(ByVal e As MouseEventArgs)
    ' Start the snip on mouse down'
    If e.Button <> MouseButtons.Left Then
        Return
    End If
    pntStart = e.Location
    rcSelect = New Rectangle(e.Location, New Size(0, 0))
    Me.Invalidate()
End Sub
Protected Overrides Sub OnMouseMove(ByVal e As MouseEventArgs)
    ' Modify the selection on mouse move'
    If e.Button <> MouseButtons.Left Then
        Return
    End If
    Dim x1 As Integer = Math.Min(e.X, pntStart.X)
    Dim y1 As Integer = Math.Min(e.Y, pntStart.Y)
    Dim x2 As Integer = Math.Max(e.X, pntStart.X)
    Dim y2 As Integer = Math.Max(e.Y, pntStart.Y)
    rcSelect = New Rectangle(x1, y1, x2 - x1, y2 - y1)
    Me.Invalidate()
End Sub


Protected Overrides Sub OnMouseUp(ByVal e As MouseEventArgs)
    ' Complete the snip on mouse-up'
    If rcSelect.Width <= 0 OrElse rcSelect.Height <= 0 Then
        Return
    End If
    Image = New Bitmap(rcSelect.Width, rcSelect.Height)
    Using gr As Graphics = Graphics.FromImage(Image)
        gr.DrawImage(Me.BackgroundImage, New Rectangle(0, 0, Image.Width, Image.Height), _
                     rcSelect, GraphicsUnit.Pixel)
    End Using
    DialogResult = DialogResult.OK
End Sub
Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)
    ' Draw the current selection'
    Using br As Brush = New SolidBrush(Color.FromArgb(120, Color.White))
        Dim x1 As Integer = rcSelect.X
        Dim x2 As Integer = rcSelect.X + rcSelect.Width
        Dim y1 As Integer = rcSelect.Y
        Dim y2 As Integer = rcSelect.Y + rcSelect.Height
        e.Graphics.FillRectangle(br, New Rectangle(0, 0, x1, Me.Height))
        e.Graphics.FillRectangle(br, New Rectangle(x2, 0, Me.Width - x2, Me.Height))
        e.Graphics.FillRectangle(br, New Rectangle(x1, 0, x2 - x1, y1))
        e.Graphics.FillRectangle(br, New Rectangle(x1, y2, x2 - x1, Me.Height - y2))
    End Using
    Using pen As New Pen(Color.Red, 3)
        e.Graphics.DrawRectangle(pen, rcSelect)
    End Using
End Sub
Protected Overrides Function ProcessCmdKey(ByRef msg As Message, ByVal keyData As Keys) As Boolean
    ' Allow canceling the snip with the Escape key'
    If keyData = Keys.Escape Then
        Me.DialogResult = DialogResult.Cancel
    End If
    Return MyBase.ProcessCmdKey(msg, keyData)
End Function

Protected Overrides Sub OnLoad(ByVal e As System.EventArgs)
    MyBase.OnLoad(e)
    Dim m_MultiScreenSize As MultiScreenSize = FindMultiScreenSize()
    Me.Size = New Size(m_MultiScreenSize.maxRight - m_MultiScreenSize.minX, _
                       m_MultiScreenSize.maxBottom - m_MultiScreenSize.minY)

    Graph.CopyFromScreen(m_MultiScreenSize.minX, m_MultiScreenSize.minY, 0, 0, BitmapSize)
End Sub
 End Class

And you can call it like this:

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    Dim img As Image = SnippingTool.Snip()
    img.Save("C:\ScreenShot.png")
End Sub
喜你已久 2024-10-05 22:18:01

您需要设置 StartPosition在设置 SnippingTool 的位置之前将表单设置为Manual,否则无论如何它都会被放置在主屏幕上。在构造函数或 using 语句中执行此操作,它应该可以解决您的问题。

StartPosition = FormStartPosition.Manual;

You need to set the StartPosition of the form to Manual before setting the Location of the SnippingTool, otherwise it gets placed on the primary screen no matter what. Do this either in the constructor or in your using statement and it should fix your issue.

StartPosition = FormStartPosition.Manual;
感受沵的脚步 2024-10-05 22:18:01

我没看到你做错什么。我无法测试这个,现在没有设置。 Bounds 有点棘手,它后面有一堆代码确保表单不能在屏幕外显示。作为替代方案,您可以设置 Location 属性并重写 SnippingTool 中的 OnLoad() 以设置 WindowState 属性。

I don't see you doing anything wrong. I can't test this, don't have the setup right now. Bounds is a bit tricky, there's a bunch of code behind it that ensures that the form can't be displayed off-screen. As an alternative, you could set the Location property instead and override OnLoad() in SnippingTool to set the WindowState property.

一世旳自豪 2024-10-05 22:18:01

我已经创建了帮助程序类来捕获控件所在的特定屏幕上的选定区域。它适用于多个屏幕。

Displays

这个想法取自多个在线资源,这些资源基本上冻结屏幕并放入 PictureBox .NET 控件中。

这是代码:

public class CaptureScreen : IDisposable
{
    readonly Control control;
    readonly Pen penSelectedAreaScreenShot;

    Form frmPictureBox = null;
    PictureBox pictureBoxScreenShot = null;
    Point selectedScreenShotStartPoint;
    Size selectedScreenShotSize;
    bool isMouseDownSelectedScreenShot = false;

    public event Action<object, Bitmap> SelectedScreenAreaCaptured;

    public event Action<object, Exception> ScreenCaptureFailed;

    public CaptureScreen(Control control)
    {
        if (control == null)
        {
            throw new ArgumentNullException("control");
        }

        this.control = control;

        this.penSelectedAreaScreenShot = new Pen(Color.Red, 1);
        this.penSelectedAreaScreenShot.DashStyle = DashStyle.Dot;
    }

    public void BeginStart()
    {
        #region Setup the Picture Box for ScreenShot

        if (this.frmPictureBox != null)
        {
            this.frmPictureBox.Close();
            this.frmPictureBox.Dispose();
            this.frmPictureBox = null;
        }

        if (this.pictureBoxScreenShot != null)
        {
            this.pictureBoxScreenShot.Dispose();
            this.pictureBoxScreenShot = null;
        }

        this.frmPictureBox = new Form
        {
            BackColor = Color.Black,
            Cursor = Cursors.Cross,
            FormBorderStyle = FormBorderStyle.None,
            StartPosition = FormStartPosition.CenterParent,
            TopLevel = true,
            TopMost = true,
            Top = 0,
            Left = 0,
            WindowState = FormWindowState.Maximized,
            KeyPreview = true
        };

        this.pictureBoxScreenShot = new PictureBox
        {
            Location = new Point(0, 0),
            SizeMode = PictureBoxSizeMode.Zoom
        };
        this.frmPictureBox.Controls.Add(this.pictureBoxScreenShot);

        #endregion

        #region Capture the Screen

        Bitmap screenShotBitmap = null;
        Graphics graphics = null;
        MemoryStream stream = null;
        try
        {
            Screen currentScreen = Screen.FromControl(this.control);

            screenShotBitmap = new Bitmap(currentScreen.Bounds.Width, currentScreen.Bounds.Height);

            graphics = Graphics.FromImage(screenShotBitmap as Image);
            graphics.CopyFromScreen(currentScreen.Bounds.X, currentScreen.Bounds.Y, 0, 0, screenShotBitmap.Size);

            stream = new MemoryStream();
            screenShotBitmap.Save(stream, ImageFormat.Png);

            this.pictureBoxScreenShot.Size = screenShotBitmap.Size;
            this.pictureBoxScreenShot.Image = Image.FromStream(stream);
        }
        catch (Exception ex)
        {
            if (this.ScreenCaptureFailed != null)
            {
                this.ScreenCaptureFailed(this, ex);
            }
        }
        finally
        {
            if (stream != null)
            {
                stream.Close();
                stream.Dispose();
                stream = null;
            }

            if (graphics != null)
            {
                graphics.Dispose();
                graphics = null;
            }

            if (screenShotBitmap != null)
            {
                screenShotBitmap.Dispose();
                screenShotBitmap = null;
            }
        }

        #endregion

        this.frmPictureBox.KeyDown += this.frmPictureBox_KeyDown;

        this.pictureBoxScreenShot.MouseMove += this.pictureBoxScreenShot_MouseMove;
        this.pictureBoxScreenShot.MouseDown += this.pictureBoxScreenShot_MouseDown;
        this.pictureBoxScreenShot.MouseUp += this.pictureBoxScreenShot_MouseUp;

        this.frmPictureBox.Show(this.control);
    }

    public void Exit()
    {
        if (this.frmPictureBox != null)
        {
            this.frmPictureBox.Close();
            this.frmPictureBox.Dispose();
            this.frmPictureBox = null;
        }

        if (this.pictureBoxScreenShot != null)
        {
            this.pictureBoxScreenShot.Dispose();
            this.pictureBoxScreenShot = null;
        }

        this.isMouseDownSelectedScreenShot = false;
    }

    [DebuggerStepThrough]
    void frmPictureBox_KeyDown(object sender, KeyEventArgs e)
    {
        if (e.KeyCode == Keys.Escape)
        {
            this.Exit();
        }
    }

    void pictureBoxScreenShot_MouseMove(object sender, MouseEventArgs e)
    {
        if (this.pictureBoxScreenShot.Image == null)
        {
            this.Exit();
            return;
        }

        if (this.isMouseDownSelectedScreenShot)
        {
            this.pictureBoxScreenShot.Refresh();

            this.selectedScreenShotSize = new Size(
                e.X - this.selectedScreenShotStartPoint.X,
                e.Y - this.selectedScreenShotStartPoint.Y);

            // Draw the selected area rectangle.
            this.pictureBoxScreenShot.CreateGraphics().DrawRectangle(this.penSelectedAreaScreenShot,
                this.selectedScreenShotStartPoint.X, this.selectedScreenShotStartPoint.Y,
                this.selectedScreenShotSize.Width, this.selectedScreenShotSize.Height);
        }
    }

    void pictureBoxScreenShot_MouseDown(object sender, MouseEventArgs e)
    {
        if (!this.isMouseDownSelectedScreenShot)
        {
            if (e.Button == MouseButtons.Left)
            {
                this.selectedScreenShotStartPoint = new Point(e.X, e.Y);
            }

            this.pictureBoxScreenShot.Refresh();

            this.isMouseDownSelectedScreenShot = true;
        }
    }

    void pictureBoxScreenShot_MouseUp(object sender, MouseEventArgs e)
    {
        if (this.pictureBoxScreenShot.Image == null)
        {
            this.Exit();
            return;
        }

        isMouseDownSelectedScreenShot = false;

        this.frmPictureBox.Hide();

        // Check whether there is something get selected.
        if (this.selectedScreenShotSize.Width > 0 && this.selectedScreenShotSize.Height > 0)
        {
            Bitmap selectedAreaBitmap = null;
            Graphics graphics = null;
            Bitmap screenShotBitmap = null;

            try
            {
                selectedAreaBitmap = new Bitmap(this.selectedScreenShotSize.Width, this.selectedScreenShotSize.Height);

                graphics = Graphics.FromImage(selectedAreaBitmap);

                graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
                graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
                graphics.CompositingQuality = CompositingQuality.HighQuality;

                screenShotBitmap = new Bitmap(this.pictureBoxScreenShot.Image, this.pictureBoxScreenShot.Size);
                graphics.DrawImage(screenShotBitmap, 0, 0, new Rectangle(this.selectedScreenShotStartPoint, this.selectedScreenShotSize), GraphicsUnit.Pixel);

                if (this.SelectedScreenAreaCaptured != null)
                {
                    this.SelectedScreenAreaCaptured(this, selectedAreaBitmap);
                }
                else
                {
                    Clipboard.SetImage(selectedAreaBitmap);
                    MessageBox.Show(this.control, "Selected Screen is copied to Clipboard.");
                }
            }
            catch (Exception ex)
            {
                if (this.ScreenCaptureFailed != null)
                {
                    this.ScreenCaptureFailed(this, ex);
                }
            }
            finally
            {
                if (screenShotBitmap != null)
                {
                    screenShotBitmap.Dispose();
                    screenShotBitmap = null;
                }

                if (graphics != null)
                {
                    graphics.Dispose();
                    graphics = null;
                }

                if (selectedAreaBitmap != null)
                {
                    selectedAreaBitmap.Dispose();
                    selectedAreaBitmap = null;
                }
            }
        }

        this.Exit();
    }

    #region IDisposable Member

    public void Dispose()
    {
        try
        {
            this.Exit();
        }
        catch { }
    }

    #endregion

}

您可以像这样使用它:

   CaptureScreen captureScreen = new CaptureScreen(this);

   // If event not implement, then by default it will copied to clipboard.
   //captureScreen.SelectedScreenAreaCaptured += delegate(object am_sender, Bitmap am_selectedAreaBitmap)
   //{
        //string destFilename = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), String.Format("Capture_{0}.png", DateTime.Now.ToString("yyyyMMddHHmmss")));
        //am_selectedAreaBitmap.Save(destFilename, System.Drawing.Imaging.ImageFormat.Png);                
   //};

   // Implements this to handle the exception that occurs during screen capture.
   //captureScreen.ScreenCaptureFailed += delegate(object am_sender, Exception am_ex)
   //{
        //MessageBox.Show(this, am_ex.Message, "Unable to Capture the Screen", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
   //};

   captureScreen.BeginStart();

I have create the helper class to capture the selected area on specific screen where control reside. It works on multiple screens.

Displays

The idea is taken from multiple online resources that basically Freeze the Screen and put into PictureBox .NET control.

Here's the codes:

public class CaptureScreen : IDisposable
{
    readonly Control control;
    readonly Pen penSelectedAreaScreenShot;

    Form frmPictureBox = null;
    PictureBox pictureBoxScreenShot = null;
    Point selectedScreenShotStartPoint;
    Size selectedScreenShotSize;
    bool isMouseDownSelectedScreenShot = false;

    public event Action<object, Bitmap> SelectedScreenAreaCaptured;

    public event Action<object, Exception> ScreenCaptureFailed;

    public CaptureScreen(Control control)
    {
        if (control == null)
        {
            throw new ArgumentNullException("control");
        }

        this.control = control;

        this.penSelectedAreaScreenShot = new Pen(Color.Red, 1);
        this.penSelectedAreaScreenShot.DashStyle = DashStyle.Dot;
    }

    public void BeginStart()
    {
        #region Setup the Picture Box for ScreenShot

        if (this.frmPictureBox != null)
        {
            this.frmPictureBox.Close();
            this.frmPictureBox.Dispose();
            this.frmPictureBox = null;
        }

        if (this.pictureBoxScreenShot != null)
        {
            this.pictureBoxScreenShot.Dispose();
            this.pictureBoxScreenShot = null;
        }

        this.frmPictureBox = new Form
        {
            BackColor = Color.Black,
            Cursor = Cursors.Cross,
            FormBorderStyle = FormBorderStyle.None,
            StartPosition = FormStartPosition.CenterParent,
            TopLevel = true,
            TopMost = true,
            Top = 0,
            Left = 0,
            WindowState = FormWindowState.Maximized,
            KeyPreview = true
        };

        this.pictureBoxScreenShot = new PictureBox
        {
            Location = new Point(0, 0),
            SizeMode = PictureBoxSizeMode.Zoom
        };
        this.frmPictureBox.Controls.Add(this.pictureBoxScreenShot);

        #endregion

        #region Capture the Screen

        Bitmap screenShotBitmap = null;
        Graphics graphics = null;
        MemoryStream stream = null;
        try
        {
            Screen currentScreen = Screen.FromControl(this.control);

            screenShotBitmap = new Bitmap(currentScreen.Bounds.Width, currentScreen.Bounds.Height);

            graphics = Graphics.FromImage(screenShotBitmap as Image);
            graphics.CopyFromScreen(currentScreen.Bounds.X, currentScreen.Bounds.Y, 0, 0, screenShotBitmap.Size);

            stream = new MemoryStream();
            screenShotBitmap.Save(stream, ImageFormat.Png);

            this.pictureBoxScreenShot.Size = screenShotBitmap.Size;
            this.pictureBoxScreenShot.Image = Image.FromStream(stream);
        }
        catch (Exception ex)
        {
            if (this.ScreenCaptureFailed != null)
            {
                this.ScreenCaptureFailed(this, ex);
            }
        }
        finally
        {
            if (stream != null)
            {
                stream.Close();
                stream.Dispose();
                stream = null;
            }

            if (graphics != null)
            {
                graphics.Dispose();
                graphics = null;
            }

            if (screenShotBitmap != null)
            {
                screenShotBitmap.Dispose();
                screenShotBitmap = null;
            }
        }

        #endregion

        this.frmPictureBox.KeyDown += this.frmPictureBox_KeyDown;

        this.pictureBoxScreenShot.MouseMove += this.pictureBoxScreenShot_MouseMove;
        this.pictureBoxScreenShot.MouseDown += this.pictureBoxScreenShot_MouseDown;
        this.pictureBoxScreenShot.MouseUp += this.pictureBoxScreenShot_MouseUp;

        this.frmPictureBox.Show(this.control);
    }

    public void Exit()
    {
        if (this.frmPictureBox != null)
        {
            this.frmPictureBox.Close();
            this.frmPictureBox.Dispose();
            this.frmPictureBox = null;
        }

        if (this.pictureBoxScreenShot != null)
        {
            this.pictureBoxScreenShot.Dispose();
            this.pictureBoxScreenShot = null;
        }

        this.isMouseDownSelectedScreenShot = false;
    }

    [DebuggerStepThrough]
    void frmPictureBox_KeyDown(object sender, KeyEventArgs e)
    {
        if (e.KeyCode == Keys.Escape)
        {
            this.Exit();
        }
    }

    void pictureBoxScreenShot_MouseMove(object sender, MouseEventArgs e)
    {
        if (this.pictureBoxScreenShot.Image == null)
        {
            this.Exit();
            return;
        }

        if (this.isMouseDownSelectedScreenShot)
        {
            this.pictureBoxScreenShot.Refresh();

            this.selectedScreenShotSize = new Size(
                e.X - this.selectedScreenShotStartPoint.X,
                e.Y - this.selectedScreenShotStartPoint.Y);

            // Draw the selected area rectangle.
            this.pictureBoxScreenShot.CreateGraphics().DrawRectangle(this.penSelectedAreaScreenShot,
                this.selectedScreenShotStartPoint.X, this.selectedScreenShotStartPoint.Y,
                this.selectedScreenShotSize.Width, this.selectedScreenShotSize.Height);
        }
    }

    void pictureBoxScreenShot_MouseDown(object sender, MouseEventArgs e)
    {
        if (!this.isMouseDownSelectedScreenShot)
        {
            if (e.Button == MouseButtons.Left)
            {
                this.selectedScreenShotStartPoint = new Point(e.X, e.Y);
            }

            this.pictureBoxScreenShot.Refresh();

            this.isMouseDownSelectedScreenShot = true;
        }
    }

    void pictureBoxScreenShot_MouseUp(object sender, MouseEventArgs e)
    {
        if (this.pictureBoxScreenShot.Image == null)
        {
            this.Exit();
            return;
        }

        isMouseDownSelectedScreenShot = false;

        this.frmPictureBox.Hide();

        // Check whether there is something get selected.
        if (this.selectedScreenShotSize.Width > 0 && this.selectedScreenShotSize.Height > 0)
        {
            Bitmap selectedAreaBitmap = null;
            Graphics graphics = null;
            Bitmap screenShotBitmap = null;

            try
            {
                selectedAreaBitmap = new Bitmap(this.selectedScreenShotSize.Width, this.selectedScreenShotSize.Height);

                graphics = Graphics.FromImage(selectedAreaBitmap);

                graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
                graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
                graphics.CompositingQuality = CompositingQuality.HighQuality;

                screenShotBitmap = new Bitmap(this.pictureBoxScreenShot.Image, this.pictureBoxScreenShot.Size);
                graphics.DrawImage(screenShotBitmap, 0, 0, new Rectangle(this.selectedScreenShotStartPoint, this.selectedScreenShotSize), GraphicsUnit.Pixel);

                if (this.SelectedScreenAreaCaptured != null)
                {
                    this.SelectedScreenAreaCaptured(this, selectedAreaBitmap);
                }
                else
                {
                    Clipboard.SetImage(selectedAreaBitmap);
                    MessageBox.Show(this.control, "Selected Screen is copied to Clipboard.");
                }
            }
            catch (Exception ex)
            {
                if (this.ScreenCaptureFailed != null)
                {
                    this.ScreenCaptureFailed(this, ex);
                }
            }
            finally
            {
                if (screenShotBitmap != null)
                {
                    screenShotBitmap.Dispose();
                    screenShotBitmap = null;
                }

                if (graphics != null)
                {
                    graphics.Dispose();
                    graphics = null;
                }

                if (selectedAreaBitmap != null)
                {
                    selectedAreaBitmap.Dispose();
                    selectedAreaBitmap = null;
                }
            }
        }

        this.Exit();
    }

    #region IDisposable Member

    public void Dispose()
    {
        try
        {
            this.Exit();
        }
        catch { }
    }

    #endregion

}

And you can use it like this:

   CaptureScreen captureScreen = new CaptureScreen(this);

   // If event not implement, then by default it will copied to clipboard.
   //captureScreen.SelectedScreenAreaCaptured += delegate(object am_sender, Bitmap am_selectedAreaBitmap)
   //{
        //string destFilename = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), String.Format("Capture_{0}.png", DateTime.Now.ToString("yyyyMMddHHmmss")));
        //am_selectedAreaBitmap.Save(destFilename, System.Drawing.Imaging.ImageFormat.Png);                
   //};

   // Implements this to handle the exception that occurs during screen capture.
   //captureScreen.ScreenCaptureFailed += delegate(object am_sender, Exception am_ex)
   //{
        //MessageBox.Show(this, am_ex.Message, "Unable to Capture the Screen", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
   //};

   captureScreen.BeginStart();
葬﹪忆之殇 2024-10-05 22:18:01

解决方案是使用 Screen.WorkingArea 属性而不是 Screen.Bounds
第二个选项使用 Graphics.CopyFromScreen 会产生不正确的结果。
上面的代码片段已更新为全功能版本。

The solution is to use Screen.WorkingArea property rather than Screen.Bounds.
The second option yields incorrect results with Graphics.CopyFromScreen.
Code snippet above has been updated with fully functional version.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文