2010-06-26 20 views

risposta

37

L'effetto strumento di snipping non è difficile da implementare in Windows Form. Aggiungi un nuovo modulo al tuo progetto e chiamalo "SnippingTool". Rendere il look codice come questo:

using System; 
using System.Drawing; 
using System.Drawing.Drawing2D; 
using System.Windows.Forms; 

namespace WindowsFormsApplication1 { 
    public partial class SnippingTool : Form { 
     public static Image Snip() { 
      var rc = Screen.PrimaryScreen.Bounds; 
      using (Bitmap bmp = new Bitmap(rc.Width, rc.Height, System.Drawing.Imaging.PixelFormat.Format32bppPArgb)) { 
       using (Graphics gr = Graphics.FromImage(bmp)) 
        gr.CopyFromScreen(0, 0, 0, 0, bmp.Size); 
       using (var snipper = new SnippingTool(bmp)) { 
        if (snipper.ShowDialog() == DialogResult.OK) { 
         return snipper.Image; 
        } 
       } 
       return null; 
      } 
     } 

     public SnippingTool(Image screenShot) { 
      InitializeComponent(); 
      this.BackgroundImage = screenShot; 
      this.ShowInTaskbar = false; 
      this.FormBorderStyle = FormBorderStyle.None; 
      this.WindowState = FormWindowState.Maximized; 
      this.DoubleBuffered = true; 
     } 
     public Image Image { get; set; } 

     private Rectangle rcSelect = new Rectangle(); 
     private Point pntStart; 

     protected override void OnMouseDown(MouseEventArgs e) { 
      // Start the snip on mouse down 
      if (e.Button != MouseButtons.Left) return; 
      pntStart = e.Location; 
      rcSelect = new Rectangle(e.Location, new Size(0, 0)); 
      this.Invalidate(); 
     } 
     protected override void OnMouseMove(MouseEventArgs e) { 
      // Modify the selection on mouse move 
      if (e.Button != MouseButtons.Left) return; 
      int x1 = Math.Min(e.X, pntStart.X); 
      int y1 = Math.Min(e.Y, pntStart.Y); 
      int x2 = Math.Max(e.X, pntStart.X); 
      int y2 = Math.Max(e.Y, pntStart.Y); 
      rcSelect = new Rectangle(x1, y1, x2 - x1, y2 - y1); 
      this.Invalidate(); 
     } 
     protected override void OnMouseUp(MouseEventArgs e) { 
      // Complete the snip on mouse-up 
      if (rcSelect.Width <= 0 || rcSelect.Height <= 0) return; 
      Image = new Bitmap(rcSelect.Width, rcSelect.Height); 
      using (Graphics gr = Graphics.FromImage(Image)) { 
       gr.DrawImage(this.BackgroundImage, new Rectangle(0, 0, Image.Width, Image.Height), 
        rcSelect, GraphicsUnit.Pixel); 
      } 
      DialogResult = DialogResult.OK; 
     } 
     protected override void OnPaint(PaintEventArgs e) { 
      // Draw the current selection 
      using (Brush br = new SolidBrush(Color.FromArgb(120, Color.White))) { 
       int x1 = rcSelect.X; int x2 = rcSelect.X + rcSelect.Width; 
       int y1 = rcSelect.Y; int y2 = rcSelect.Y + rcSelect.Height; 
       e.Graphics.FillRectangle(br, new Rectangle(0, 0, x1, this.Height)); 
       e.Graphics.FillRectangle(br, new Rectangle(x2, 0, this.Width - x2, this.Height)); 
       e.Graphics.FillRectangle(br, new Rectangle(x1, 0, x2 - x1, y1)); 
       e.Graphics.FillRectangle(br, new Rectangle(x1, y2, x2 - x1, this.Height - y2)); 
      } 
      using (Pen pen = new Pen(Color.Red, 3)) { 
       e.Graphics.DrawRectangle(pen, rcSelect); 
      } 
     } 
     protected override bool ProcessCmdKey(ref Message msg, Keys keyData) { 
      // Allow canceling the snip with the Escape key 
      if (keyData == Keys.Escape) this.DialogResult = DialogResult.Cancel; 
      return base.ProcessCmdKey(ref msg, keyData); 
     } 
    } 
} 

Usage:

var bmp = SnippingTool.Snip(); 
if (bmp != null) { 
    // Do something with the bitmap 
    //... 
} 
+0

Fantastico !! Grazie mille! – SharpAffair

+1

Hans, potresti aiutare a rendere questo strumento compatibile con i monitor secondari? Ho provato ad aggiungere un parametro di schermo e ad aggiustare la posizione di snipper in base ad esso, ma ottenendo risultati inaspettati. – SharpAffair

+6

Cosa ti aspetti che faccia con "risultati inaspettati"? Inizia una nuova domanda e * davvero * documenta il tuo problema. –

2

Fa uno screenshot a schermo intero, quindi (probabilmente) lo copia, applica l'effetto traslucido & lo visualizza. Quando si fa clic sul trascinamento, è possibile sovrapporre la regione corrispondente dall'acquisizione originale.

È possibile ottenere uno screenshot utilizzando CopyFromScreen() o utilizzando il GDI API.

+0

Grazie per una grande risposta. Ciò ha senso. Forse sai come può essere applicato all'effetto "sbiancamento" questo bitmap? – SharpAffair

7

Questa è una versione modificata @ versione di Hans che è compatibile con più monitor e funziona bene con il ridimensionamento DPI (testato su Windows 7 e Windows 10).

public sealed partial class SnippingTool : Form 
{ 
    public static event EventHandler Cancel; 
    public static event EventHandler AreaSelected; 
    public static Image Image { get; set; } 

    private static SnippingTool[] _forms; 
    private Rectangle _rectSelection; 
    private Point _pointStart; 

    public SnippingTool(Image screenShot, int x, int y, int width, int height) 
    { 
     InitializeComponent(); 
     BackgroundImage = screenShot; 
     BackgroundImageLayout = ImageLayout.Stretch; 
     ShowInTaskbar = false; 
     FormBorderStyle = FormBorderStyle.None; 
     StartPosition = FormStartPosition.Manual; 
     SetBounds(x, y, width, height); 
     WindowState = FormWindowState.Maximized; 
     DoubleBuffered = true; 
     Cursor = Cursors.Cross; 
     TopMost = true; 
    } 

    private void OnCancel(EventArgs e) 
    { 
     Cancel?.Invoke(this, e); 
    } 

    private void OnAreaSelected(EventArgs e) 
    { 
     AreaSelected?.Invoke(this, e); 
    } 

    private void CloseForms() 
    { 
     for (int i = 0; i < _forms.Length; i++) 
     { 
      _forms[i].Dispose(); 
     } 
    } 

    public static void Snip() 
    { 
     var screens = ScreenHelper.GetMonitorsInfo(); 
     _forms = new SnippingTool[screens.Count]; 
     for (int i = 0; i < screens.Count; i++) 
     { 
      int hRes = screens[i].HorizontalResolution; 
      int vRes = screens[i].VerticalResolution; 
      int top = screens[i].MonitorArea.Top; 
      int left = screens[i].MonitorArea.Left; 
      var bmp = new Bitmap(hRes, vRes, PixelFormat.Format32bppPArgb); 
      using (var g = Graphics.FromImage(bmp)) 
      { 
       g.CopyFromScreen(left, top, 0, 0, bmp.Size); 
      } 
      _forms[i] = new SnippingTool(bmp, left, top, hRes, vRes); 
      _forms[i].Show(); 
     } 
    } 

    #region Overrides 
    protected override void OnMouseDown(MouseEventArgs e) 
    { 
     // Start the snip on mouse down 
     if (e.Button != MouseButtons.Left) 
     { 
      return; 
     } 
     _pointStart = e.Location; 
     _rectSelection = new Rectangle(e.Location, new Size(0, 0)); 
     Invalidate(); 
    } 

    protected override void OnMouseMove(MouseEventArgs e) 
    { 
     // Modify the selection on mouse move 
     if (e.Button != MouseButtons.Left) 
     { 
      return; 
     } 
     int x1 = Math.Min(e.X, _pointStart.X); 
     int y1 = Math.Min(e.Y, _pointStart.Y); 
     int x2 = Math.Max(e.X, _pointStart.X); 
     int y2 = Math.Max(e.Y, _pointStart.Y); 
     _rectSelection = new Rectangle(x1, y1, x2 - x1, y2 - y1); 
     Invalidate(); 
    } 

    protected override void OnMouseUp(MouseEventArgs e) 
    { 
     // Complete the snip on mouse-up 
     if (_rectSelection.Width <= 0 || _rectSelection.Height <= 0) 
     { 
      CloseForms(); 
      OnCancel(new EventArgs()); 
      return; 
     } 
     Image = new Bitmap(_rectSelection.Width, _rectSelection.Height); 
     var hScale = BackgroundImage.Width/(double)Width; 
     var vScale = BackgroundImage.Height/(double)Height; 
     using (Graphics gr = Graphics.FromImage(Image)) 
     { 

      gr.DrawImage(BackgroundImage, 
       new Rectangle(0, 0, Image.Width, Image.Height), 
       new Rectangle((int)(_rectSelection.X * hScale), (int)(_rectSelection.Y * vScale), (int)(_rectSelection.Width * hScale), (int)(_rectSelection.Height * vScale)), 
       GraphicsUnit.Pixel); 
     } 
     CloseForms(); 
     OnAreaSelected(new EventArgs()); 
    } 

    protected override void OnPaint(PaintEventArgs e) 
    { 
     // Draw the current selection 
     using (Brush br = new SolidBrush(Color.FromArgb(120, Color.White))) 
     { 
      int x1 = _rectSelection.X; 
      int x2 = _rectSelection.X + _rectSelection.Width; 
      int y1 = _rectSelection.Y; 
      int y2 = _rectSelection.Y + _rectSelection.Height; 
      e.Graphics.FillRectangle(br, new Rectangle(0, 0, x1, Height)); 
      e.Graphics.FillRectangle(br, new Rectangle(x2, 0, Width - x2, Height)); 
      e.Graphics.FillRectangle(br, new Rectangle(x1, 0, x2 - x1, y1)); 
      e.Graphics.FillRectangle(br, new Rectangle(x1, y2, x2 - x1, Height - y2)); 
     } 
     using (Pen pen = new Pen(Color.Red, 2)) 
     { 
      e.Graphics.DrawRectangle(pen, _rectSelection); 
     } 
    } 

    protected override bool ProcessCmdKey(ref Message msg, Keys keyData) 
    { 
     // Allow canceling the snip with the Escape key 
     if (keyData == Keys.Escape) 
     { 
      Image = null; 
      CloseForms(); 
      OnCancel(new EventArgs()); 
     } 
     return base.ProcessCmdKey(ref msg, keyData); 
    } 
    #endregion 
} 

Usage:

SnippingTool.AreaSelected += OnAreaSelected; 
SnippingTool.Snip(); 

private static void OnAreaSelected(object sender, EventArgs e) 
{ 
    var bmp = SnippingTool.Image; 
    // Do something with the bitmap 
    //... 
} 

Nota hai bisogno di una classe di supporto per ottenere la risoluzione effettiva del monitor e evitare problemi con il ridimensionamento DPI. Questo è il codice:

public class DeviceInfo 
{ 
    public string DeviceName { get; set; } 
    public int VerticalResolution { get; set; } 
    public int HorizontalResolution { get; set; } 
    public Rectangle MonitorArea { get; set; } 
} 
public static class ScreenHelper 
{ 
    private const int DektopVertRes = 117; 
    private const int DesktopHorzRes = 118; 
    [StructLayout(LayoutKind.Sequential)] 
    internal struct Rect 
    { 
     public int left; 
     public int top; 
     public int right; 
     public int bottom; 
    } 
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] 
    internal struct MONITORINFOEX 
    { 
     public int Size; 
     public Rect Monitor; 
     public Rect WorkArea; 
     public uint Flags; 
     [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] 
     public string DeviceName; 
    } 
    private delegate bool MonitorEnumDelegate(IntPtr hMonitor, IntPtr hdcMonitor, ref Rect lprcMonitor, IntPtr dwData); 
    [DllImport("user32.dll")] 
    private static extern bool EnumDisplayMonitors(IntPtr hdc, IntPtr lprcClip, MonitorEnumDelegate lpfnEnum, IntPtr dwData); 
    [DllImport("gdi32.dll")] 
    private static extern IntPtr CreateDC(string lpszDriver, string lpszDevice, string lpszOutput, IntPtr lpInitData); 
    [DllImport("user32.dll", CharSet = CharSet.Unicode)] 
    private static extern bool GetMonitorInfo(IntPtr hMonitor, ref MONITORINFOEX lpmi); 
    [DllImport("User32.dll")] 
    private static extern int ReleaseDC(IntPtr hwnd, IntPtr dc); 
    [DllImport("gdi32.dll")] 
    private static extern int GetDeviceCaps(IntPtr hdc, int nIndex); 

    private static List<DeviceInfo> _result; 

    public static List<DeviceInfo> GetMonitorsInfo() 
    { 
     _result = new List<DeviceInfo>(); 
     EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, MonitorEnum, IntPtr.Zero); 
     return _result; 
    } 

    private static bool MonitorEnum(IntPtr hMonitor, IntPtr hdcMonitor, ref Rect lprcMonitor, IntPtr dwData) 
    { 
     var mi = new MONITORINFOEX(); 
     mi.Size = Marshal.SizeOf(typeof(MONITORINFOEX)); 
     bool success = GetMonitorInfo(hMonitor, ref mi); 
     if (success) 
     { 
      var dc = CreateDC(mi.DeviceName, mi.DeviceName, null, IntPtr.Zero); 
      var di = new DeviceInfo 
      { 
       DeviceName = mi.DeviceName, 
       MonitorArea = new Rectangle(mi.Monitor.left, mi.Monitor.top, mi.Monitor.right-mi.Monitor.right, mi.Monitor.bottom-mi.Monitor.top), 
       VerticalResolution = GetDeviceCaps(dc, DektopVertRes), 
       HorizontalResolution = GetDeviceCaps(dc, DesktopHorzRes) 
      }; 
      ReleaseDC(IntPtr.Zero, dc); 
      _result.Add(di); 
     } 
     return true; 
    } 
} 

Ecco l'completi source code

+0

Funziona, ma in alta Dpi mentre snipping l'immagine è sfocata e se si salva il l'immagine è troppo piccola, ha provato a risolverlo ma non ci è riuscito. @ thepirat000 – Arvand

+0

@Arvand Qual è la tua configurazione DPI? – thepirat000

+0

3840x2160 con dimensioni del testo impostate al 250% [display 4K] – Arvand

Problemi correlati