此帖子中提供了另一种剪切工具解决方案:.NET Equivalent of Snipping Tool
现在有必要让它适用于选定的屏幕(在多监视器系统上)。
代码已相应修改:
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
但它拒绝按预期工作。无论“剪切”功能中的“屏幕”参数如何,剪切器都不会出现在所选屏幕上,而是显示在第一个屏幕上。 如何让它正常工作?
更新:最新的片段版本显示在正确的屏幕上,但为空白。
UPDATE X2 :上面的代码已更新,以反映最新版本,现在可以正常使用。
答案 0 :(得分:3)
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
。
大编辑:
我做了一些工作来同时支持多个监视器,这不需要开发人员选择使用哪个监视器(这会将“Snipping Tool”克隆得更近)。
基本上我正在遍历所有屏幕以找到最小X
和Y
坐标,以及最大的Right
和Bottom
,这让我们可以评估全尺寸的“虚拟监视器”:
我用我的配置测试了它:
Primary 1280x800
Secondary 1280x1024 w/ -224 X offset
代码:
'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
你可以这样称呼它:
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
答案 1 :(得分:2)
在设置SnippingTool的位置之前,您需要将表单的StartPosition设置为Manual
,否则无论如何都会将其置于主屏幕上。在构造函数或using语句中执行此操作,它应该可以解决您的问题。
StartPosition = FormStartPosition.Manual;
答案 2 :(得分:1)
我没有看到你做错了什么。我无法测试,现在没有设置。界限有点棘手,它背后有一堆代码可以确保表格无法在屏幕外显示。作为替代方法,您可以改为设置Location属性并覆盖SnippingTool中的OnLoad()以设置WindowState属性。
答案 3 :(得分:1)
我创建了辅助类来捕获控件所在的特定屏幕上的选定区域。它适用于多个屏幕。
这个想法取自多个在线资源,基本上冻结屏幕并放入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();
答案 4 :(得分:0)
解决方案是使用Screen.WorkingArea
属性而不是Screen.Bounds
。
第二个选项会导致Graphics.CopyFromScreen
的结果不正确。
上面的代码段已更新为功能完整的版本。