使.NET 截图工具兼容多个显示器
此帖子中提供了另一种截图工具解决方案:.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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
LoveDotNet,我相信您的源代码有一个小问题,以下行:
Graph.CopyFromScreen(area.X,area.Y,area.Y,area.Y,BitmapSize)
应该是:
Graph.CopyFromScreen(area.X, area.Y, 0, 0, BitmapSize)
另外,对于任何想要使用此代码的人来说,这是一个快速提示,您可以像下面这样调用它:
另外,当当您创建 SnippingTool 表单时,请务必将
StartPosition
属性更改为Manual
。大编辑:
我做了一些工作来同时支持多个显示器,这不需要开发人员选择要使用的显示器(这克隆了“截图工具”)。
基本上,我会遍历所有屏幕来查找最小的
X
和Y
坐标,以及最大的Right
和Bottom< /code>,这让我们可以评估“虚拟监视器”的完整大小:
我已经使用我的配置对其进行了测试,配置为:
Primary 1280x800
Secondary 1280x1024 w/ -224 X offset< /code>
代码:
你可以这样称呼它:
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:
Also, when you create your SnippingTool form, be sure to change the
StartPosition
property toManual
.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
andY
coordinates, and the largestRight
andBottom
, 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
Code:
And you can call it like this:
您需要设置 StartPosition在设置 SnippingTool 的位置之前将表单设置为
Manual
,否则无论如何它都会被放置在主屏幕上。在构造函数或 using 语句中执行此操作,它应该可以解决您的问题。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.我没看到你做错什么。我无法测试这个,现在没有设置。 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.
我已经创建了帮助程序类来捕获控件所在的特定屏幕上的选定区域。它适用于多个屏幕。
这个想法取自多个在线资源,这些资源基本上冻结屏幕并放入 PictureBox .NET 控件中。
这是代码:
您可以像这样使用它:
I have create the helper class to capture the selected area on specific screen where control reside. It works on multiple screens.
The idea is taken from multiple online resources that basically Freeze the Screen and put into PictureBox .NET control.
Here's the codes:
And you can use it like this:
解决方案是使用
Screen.WorkingArea
属性而不是Screen.Bounds
。第二个选项使用
Graphics.CopyFromScreen
会产生不正确的结果。上面的代码片段已更新为全功能版本。
The solution is to use
Screen.WorkingArea
property rather thanScreen.Bounds
.The second option yields incorrect results with
Graphics.CopyFromScreen
.Code snippet above has been updated with fully functional version.