Evento del portapapeles C #

¿Hay algún evento modificado o actualizado en el portapapeles al que pueda acceder a través de C #?

Creo que deberás usar un poco de p / invoke:

[DllImport("User32.dll", CharSet=CharSet.Auto)] public static extern IntPtr SetClipboardViewer(IntPtr hWndNewViewer); 

Vea este artículo sobre cómo configurar un monitor de portapapeles en c #

Básicamente registras tu aplicación como un visor de portapapeles usando

 _ClipboardViewerNext = SetClipboardViewer(this.Handle); 

y luego recibirá el mensaje WM_DRAWCLIPBOARD , que puede manejar anulando WndProc :

 protected override void WndProc(ref Message m) { switch ((Win32.Msgs)m.Msg) { case Win32.Msgs.WM_DRAWCLIPBOARD: // Handle clipboard changed break; // ... } } 

(Hay mucho más por hacer: pasar cosas a lo largo de la cadena del portapapeles y anular el registro de su vista, pero puede obtenerlo del artículo )

Para completar, aquí está el control que estoy usando en el código de producción. Simplemente arrastre desde el diseñador y haga doble clic para crear el controlador de eventos.

 using System; using System.ComponentModel; using System.Runtime.InteropServices; using System.Windows.Forms; using System.Drawing; namespace ClipboardAssist { // Must inherit Control, not Component, in order to have Handle [DefaultEvent("ClipboardChanged")] public partial class ClipboardMonitor : Control { IntPtr nextClipboardViewer; public ClipboardMonitor() { this.BackColor = Color.Red; this.Visible = false; nextClipboardViewer = (IntPtr)SetClipboardViewer((int)this.Handle); } ///  /// Clipboard contents changed. ///  public event EventHandler ClipboardChanged; protected override void Dispose(bool disposing) { ChangeClipboardChain(this.Handle, nextClipboardViewer); } [DllImport("User32.dll")] protected static extern int SetClipboardViewer(int hWndNewViewer); [DllImport("User32.dll", CharSet = CharSet.Auto)] public static extern bool ChangeClipboardChain(IntPtr hWndRemove, IntPtr hWndNewNext); [DllImport("user32.dll", CharSet = CharSet.Auto)] public static extern int SendMessage(IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam); protected override void WndProc(ref System.Windows.Forms.Message m) { // defined in winuser.h const int WM_DRAWCLIPBOARD = 0x308; const int WM_CHANGECBCHAIN = 0x030D; switch (m.Msg) { case WM_DRAWCLIPBOARD: OnClipboardChanged(); SendMessage(nextClipboardViewer, m.Msg, m.WParam, m.LParam); break; case WM_CHANGECBCHAIN: if (m.WParam == nextClipboardViewer) nextClipboardViewer = m.LParam; else SendMessage(nextClipboardViewer, m.Msg, m.WParam, m.LParam); break; default: base.WndProc(ref m); break; } } void OnClipboardChanged() { try { IDataObject iData = Clipboard.GetDataObject(); if (ClipboardChanged != null) { ClipboardChanged(this, new ClipboardChangedEventArgs(iData)); } } catch (Exception e) { // Swallow or pop-up, not sure // Trace.Write(e.ToString()); MessageBox.Show(e.ToString()); } } } public class ClipboardChangedEventArgs : EventArgs { public readonly IDataObject DataObject; public ClipboardChangedEventArgs(IDataObject dataObject) { DataObject = dataObject; } } } 

Tuve este desafío en WPF y terminé usando el enfoque que se describe a continuación. Para los formularios de Windows hay ejemplos excelentes en otras partes de esta respuesta, como el control ClipboardHelper.

Para WPF no podemos anular WndProc, por lo que debemos conectarlo explícitamente con una llamada HwndSource AddHook utilizando Source desde una ventana. El oyente del portapapeles sigue utilizando la llamada de interoperación nativa AddClipboardFormatListener.

Métodos nativos:

 internal static class NativeMethods { // See http://msdn.microsoft.com/en-us/library/ms649021%28v=vs.85%29.aspx public const int WM_CLIPBOARDUPDATE = 0x031D; public static IntPtr HWND_MESSAGE = new IntPtr(-3); // See http://msdn.microsoft.com/en-us/library/ms632599%28VS.85%29.aspx#message_only [DllImport("user32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool AddClipboardFormatListener(IntPtr hwnd); } 

Clase de administrador de portapapeles:

 using System.Windows; using System.Windows.Interop; public class ClipboardManager { public event EventHandler ClipboardChanged; public ClipboardManager(Window windowSource) { HwndSource source = PresentationSource.FromVisual(windowSource) as HwndSource; if(source == null) { throw new ArgumentException( "Window source MUST be initialized first, such as in the Window's OnSourceInitialized handler." , nameof(windowSource)); } source.AddHook(WndProc); // get window handle for interop IntPtr windowHandle = new WindowInteropHelper(windowSource).Handle; // register for clipboard events NativeMethods.AddClipboardFormatListener(windowHandle); } private void OnClipboardChanged() { ClipboardChanged?.Invoke(this, EventArgs.Empty); } private static readonly IntPtr WndProcSuccess = IntPtr.Zero; private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { if (msg == NativeMethods.WM_CLIPBOARDUPDATE) { OnClipboardChanged(); handled = true; } return WndProcSuccess; } } 

Esto se usa en una ventana de WPF agregando el evento en OnSourceInitialized o posterior, como el evento Window.Loaded o durante la operación. (cuando tenemos suficiente información para usar los ganchos nativos):

 public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } protected override void OnSourceInitialized(EventArgs e) { base.OnSourceInitialized(e); // Initialize the clipboard now that we have a window soruce to use var windowClipboardManager = new ClipboardManager(this); windowClipboardManager.ClipboardChanged += ClipboardChanged; } private void ClipboardChanged(object sender, EventArgs e) { // Handle your clipboard update here, debug logging example: if (Clipboard.ContainsText()) { Debug.WriteLine(Clipboard.GetText()); } } } 

Estoy usando este enfoque en un proyecto analizador de ítems de Path of Exile, ya que el juego expone la información del ítem a través del portapapeles cuando presionas Ctrl-C.

https://github.com/ColinDabritz/PoeItemAnalyzer

¡Espero que esto ayude a alguien con el manejo del cambio del portapapeles de WPF!

Ok, esta es una publicación anterior, pero encontramos una solución que parece muy simple en comparación con el conjunto actual de respuestas. Estamos utilizando WPF y queríamos tener nuestros propios Comandos personalizados (en un ContextMenu) activar y desactivar si el Portapapeles contiene texto. Ya hay una ApplicationCommands.Cut, Copy y Paste, y estos comandos responden correctamente al cambio del portapapeles. Así que acabamos de agregar el siguiente EventHandler.

 ApplicationCommands.Paste.CanExecuteChanged += new EventHandler(Paste_CanExecuteChanged); private void Paste_CanExecuteChanged(object sender, EventArgs e) { ourVariable= Clipboard.ContainsText(); } 

De hecho, estamos controlando CanExecute en nuestro propio comando de esta manera. Funciona para lo que necesitábamos y tal vez ayudará a otros por ahí.

Hay varias maneras de hacerlo, pero este es mi favorito y funciona para mí. Creé una biblioteca de clases para que otros puedan agregar el proyecto e incluir la DLL y luego simplemente invocarlo y usarlo donde quieran dentro de sus aplicaciones.

Esta respuesta fue hecha con la ayuda de este .

  1. Cree un proyecto de biblioteca de clases y asígnele el nombre ClipboardHelper.
  2. Reemplace el nombre Class1 con ClipboardMonitor.
  3. Agregue el código a continuación en él.
  4. Agregue referencia de System.Windows.Forms.

Más pasos debajo del código.

 using System; using System.Windows.Forms; using System.Threading; using System.Runtime.InteropServices; namespace ClipboardHelper { public static class ClipboardMonitor { public delegate void OnClipboardChangeEventHandler(ClipboardFormat format, object data); public static event OnClipboardChangeEventHandler OnClipboardChange; public static void Start() { ClipboardWatcher.Start(); ClipboardWatcher.OnClipboardChange += (ClipboardFormat format, object data) => { if (OnClipboardChange != null) OnClipboardChange(format, data); }; } public static void Stop() { OnClipboardChange = null; ClipboardWatcher.Stop(); } class ClipboardWatcher : Form { // static instance of this form private static ClipboardWatcher mInstance; // needed to dispose this form static IntPtr nextClipboardViewer; public delegate void OnClipboardChangeEventHandler(ClipboardFormat format, object data); public static event OnClipboardChangeEventHandler OnClipboardChange; // start listening public static void Start() { // we can only have one instance if this class if (mInstance != null) return; var t = new Thread(new ParameterizedThreadStart(x => Application.Run(new ClipboardWatcher()))); t.SetApartmentState(ApartmentState.STA); // give the [STAThread] attribute t.Start(); } // stop listening (dispose form) public static void Stop() { mInstance.Invoke(new MethodInvoker(() => { ChangeClipboardChain(mInstance.Handle, nextClipboardViewer); })); mInstance.Invoke(new MethodInvoker(mInstance.Close)); mInstance.Dispose(); mInstance = null; } // on load: (hide this window) protected override void SetVisibleCore(bool value) { CreateHandle(); mInstance = this; nextClipboardViewer = SetClipboardViewer(mInstance.Handle); base.SetVisibleCore(false); } [DllImport("User32.dll", CharSet = CharSet.Auto)] private static extern IntPtr SetClipboardViewer(IntPtr hWndNewViewer); [DllImport("User32.dll", CharSet = CharSet.Auto)] private static extern bool ChangeClipboardChain(IntPtr hWndRemove, IntPtr hWndNewNext); [DllImport("user32.dll", CharSet = CharSet.Auto)] private static extern int SendMessage(IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam); // defined in winuser.h const int WM_DRAWCLIPBOARD = 0x308; const int WM_CHANGECBCHAIN = 0x030D; protected override void WndProc(ref Message m) { switch (m.Msg) { case WM_DRAWCLIPBOARD: ClipChanged(); SendMessage(nextClipboardViewer, m.Msg, m.WParam, m.LParam); break; case WM_CHANGECBCHAIN: if (m.WParam == nextClipboardViewer) nextClipboardViewer = m.LParam; else SendMessage(nextClipboardViewer, m.Msg, m.WParam, m.LParam); break; default: base.WndProc(ref m); break; } } static readonly string[] formats = Enum.GetNames(typeof(ClipboardFormat)); private void ClipChanged() { IDataObject iData = Clipboard.GetDataObject(); ClipboardFormat? format = null; foreach (var f in formats) { if (iData.GetDataPresent(f)) { format = (ClipboardFormat)Enum.Parse(typeof(ClipboardFormat), f); break; } } object data = iData.GetData(format.ToString()); if (data == null || format == null) return; if (OnClipboardChange != null) OnClipboardChange((ClipboardFormat)format, data); } } } public enum ClipboardFormat : byte { /// Specifies the standard ANSI text format. This static field is read-only. ///  /// 1 Text, /// Specifies the standard Windows Unicode text format. This static field /// is read-only. /// 1 UnicodeText, /// Specifies the Windows device-independent bitmap (DIB) format. This static /// field is read-only. /// 1 Dib, /// Specifies a Windows bitmap format. This static field is read-only. /// 1 Bitmap, /// Specifies the Windows enhanced metafile format. This static field is /// read-only. /// 1 EnhancedMetafile, /// Specifies the Windows metafile format, which Windows Forms does not /// directly use. This static field is read-only. /// 1 MetafilePict, /// Specifies the Windows symbolic link format, which Windows Forms does /// not directly use. This static field is read-only. /// 1 SymbolicLink, /// Specifies the Windows Data Interchange Format (DIF), which Windows Forms /// does not directly use. This static field is read-only. /// 1 Dif, /// Specifies the Tagged Image File Format (TIFF), which Windows Forms does /// not directly use. This static field is read-only. /// 1 Tiff, /// Specifies the standard Windows original equipment manufacturer (OEM) /// text format. This static field is read-only. /// 1 OemText, /// Specifies the Windows palette format. This static field is read-only. ///  /// 1 Palette, /// Specifies the Windows pen data format, which consists of pen strokes /// for handwriting software, Windows Forms does not use this format. This static /// field is read-only. /// 1 PenData, /// Specifies the Resource Interchange File Format (RIFF) audio format, /// which Windows Forms does not directly use. This static field is read-only. /// 1 Riff, /// Specifies the wave audio format, which Windows Forms does not directly /// use. This static field is read-only. /// 1 WaveAudio, /// Specifies the Windows file drop format, which Windows Forms does not /// directly use. This static field is read-only. /// 1 FileDrop, /// Specifies the Windows culture format, which Windows Forms does not directly /// use. This static field is read-only. /// 1 Locale, /// Specifies text consisting of HTML data. This static field is read-only. ///  /// 1 Html, /// Specifies text consisting of Rich Text Format (RTF) data. This static /// field is read-only. /// 1 Rtf, /// Specifies a comma-separated value (CSV) format, which is a common interchange /// format used by spreadsheets. This format is not used directly by Windows Forms. /// This static field is read-only. /// 1 CommaSeparatedValue, /// Specifies the Windows Forms string class format, which Windows Forms /// uses to store string objects. This static field is read-only. /// 1 StringFormat, /// Specifies a format that encapsulates any type of Windows Forms object. /// This static field is read-only. /// 1 Serializable, } } 
  1. En sus otros proyectos, haga clic derecho en la solución y Agregar -> Salir del proyecto -> ClipboardHelper.csproj
  2. En su proyecto, vaya a y haga clic con el botón derecho en Referencias -> Agregar referencia -> Solución -> Seleccionar PortapapelesHelper.
  3. En su archivo de clase del tipo de proyecto usando ClipboardHelper.
  4. Ahora puede escribir ClipboardMonitor.Start o .Stop o .OnClipboardChanged

     using ClipboardHelper; namespace Something.Something.DarkSide { public class MainWindow { public MainWindow() { InitializeComponent(); Loaded += MainWindow_Loaded; } void MainWindow_Loaded(object sender, RoutedEventArgs e) { ClipboardMonitor.OnClipboardChange += ClipboardMonitor_OnClipboardChange; ClipboardMonitor.Start(); } private void ClipboardMonitor_OnClipboardChange(ClipboardFormat format, object data) { // Do Something... } } 

Creo que una de las soluciones anteriores no verifica un nulo en el método de eliminación:

 using System; using System.ComponentModel; using System.Runtime.InteropServices; using System.Windows.Forms; using System.Drawing; namespace ClipboardAssist { // Must inherit Control, not Component, in order to have Handle [DefaultEvent("ClipboardChanged")] public partial class ClipboardMonitor : Control { IntPtr nextClipboardViewer; public ClipboardMonitor() { this.BackColor = Color.Red; this.Visible = false; nextClipboardViewer = (IntPtr)SetClipboardViewer((int)this.Handle); } ///  /// Clipboard contents changed. ///  public event EventHandler ClipboardChanged; protected override void Dispose(bool disposing) { if(nextClipboardViewer != null) ChangeClipboardChain(this.Handle, nextClipboardViewer); } [DllImport("User32.dll")] protected static extern int SetClipboardViewer(int hWndNewViewer); [DllImport("User32.dll", CharSet = CharSet.Auto)] public static extern bool ChangeClipboardChain(IntPtr hWndRemove, IntPtr hWndNewNext); [DllImport("user32.dll", CharSet = CharSet.Auto)] public static extern int SendMessage(IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam); protected override void WndProc(ref System.Windows.Forms.Message m) { // defined in winuser.h const int WM_DRAWCLIPBOARD = 0x308; const int WM_CHANGECBCHAIN = 0x030D; switch (m.Msg) { case WM_DRAWCLIPBOARD: OnClipboardChanged(); SendMessage(nextClipboardViewer, m.Msg, m.WParam, m.LParam); break; case WM_CHANGECBCHAIN: if (m.WParam == nextClipboardViewer) nextClipboardViewer = m.LParam; else SendMessage(nextClipboardViewer, m.Msg, m.WParam, m.LParam); break; default: base.WndProc(ref m); break; } } void OnClipboardChanged() { try { IDataObject iData = Clipboard.GetDataObject(); if (ClipboardChanged != null) { ClipboardChanged(this, new ClipboardChangedEventArgs(iData)); } } catch (Exception e) { // Swallow or pop-up, not sure // Trace.Write(e.ToString()); MessageBox.Show(e.ToString()); } } } public class ClipboardChangedEventArgs : EventArgs { public readonly IDataObject DataObject; public ClipboardChangedEventArgs(IDataObject dataObject) { DataObject = dataObject; } } } 

aquí hay un buen ejemplo de AddClipboardFormatListener usar AddClipboardFormatListener .

Para hacer eso necesitamos pinvoke AddClipboardFormatListener y RemoveClipboardFormatListener .

 ///  /// Places the given window in the system-maintained clipboard format listener list. ///  [DllImport("user32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] static extern bool AddClipboardFormatListener(IntPtr hwnd); ///  /// Removes the given window from the system-maintained clipboard format listener list. ///  [DllImport("user32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] static extern bool RemoveClipboardFormatListener(IntPtr hwnd); ///  /// Sent when the contents of the clipboard have changed. ///  private const int WM_CLIPBOARDUPDATE = 0x031D; 

Luego, necesitamos agregar nuestra ventana a la lista de escuchas del formato del portapapeles llamando al método AddClipboardFormatListener con el AddClipboardFormatListener nuestra ventana como parámetro. Coloque el siguiente código en su constructor de formulario de ventana principal o cualquiera de sus eventos de carga.

 AddClipboardFormatListener(this.Handle); // Add our window to the clipboard's format listener list. 

Reemplace el método WndProc para que podamos capturar cuando se envía WM_CLIPBOARDUPDATE .

 protected override void WndProc(ref Message m) { base.WndProc(ref m); if (m.Msg == WM_CLIPBOARDUPDATE) { IDataObject iData = Clipboard.GetDataObject(); // Clipboard's data. /* Depending on the clipboard's current data format we can process the data differently. * Feel free to add more checks if you want to process more formats. */ if (iData.GetDataPresent(DataFormats.Text)) { string text = (string)iData.GetData(DataFormats.Text); // do something with it } else if (iData.GetDataPresent(DataFormats.Bitmap)) { Bitmap image = (Bitmap)iData.GetData(DataFormats.Bitmap); // do something with it } } } 

Y finalmente asegúrese de eliminar su ventana principal de la lista de escuchas del formato del portapapeles antes de cerrar su formulario.

 private void Form1_FormClosing(object sender, FormClosingEventArgs e) { RemoveClipboardFormatListener(this.Handle); // Remove our window from the clipboard's format listener list. }