剪贴板事件C#
是否有剪贴板更改或更新的事件,我可以通过C#访问?
我想你将不得不使用一些p / invoke:
[DllImport("User32.dll", CharSet=CharSet.Auto)] public static extern IntPtr SetClipboardViewer(IntPtr hWndNewViewer);
看到这篇文章如何在c#中设置剪贴板监视器
基本上你注册你的应用程序作为剪贴板查看器使用
_ClipboardViewerNext = SetClipboardViewer(this.Handle);
然后你会收到WM_DRAWCLIPBOARD
消息,你可以通过覆盖WndProc
来处理:
protected override void WndProc(ref Message m) { switch ((Win32.Msgs)m.Msg) { case Win32.Msgs.WM_DRAWCLIPBOARD: // Handle clipboard changed break; // ... } }
(还有更多的事情要做,沿着剪贴板链传递东西,取消注册你的视图,但你可以从文章中获得 )
为了完整性,这里是我在生产代码中使用的控件。 只需从devise人员拖动,然后双击创build事件处理程序。
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); } /// <summary> /// Clipboard contents changed. /// </summary> public event EventHandler<ClipboardChangedEventArgs> 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; } } }
我在WPF中遇到了这个挑战,最终使用了下面描述的方法。 对于Windows窗体,在这个答案的其他地方有很好的例子,比如ClipboardHelper控件。
对于WPF我们不能重写WndProc,所以我们必须使用来自窗口的Source来显式挂接HwndSource的AddHook调用。 剪贴板侦听器仍然使用AddClipboardFormatListener本地互操作调用。
原生方法:
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); }
剪贴板pipe理器类:
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; } }
这通过在OnSourceInitialized或更高版本(如Window.Loaded事件)中添加事件或在操作期间在WPF窗口中使用。 (当我们有足够的信息来使用本地钩子):
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()); } } }
我在“stream亡之路”项目分析器项目中使用这种方法,因为当您按Ctrl-C时,游戏会通过剪贴板公开项目信息。
https://github.com/ColinDabritz/PoeItemAnalyzer
我希望这可以帮助WPF剪贴板更改处理的人!
好,所以这是一个旧的职位,但我们发现一个解决scheme,似乎很简单相比,目前的答案。 我们正在使用WPF,如果剪贴板包含文本,我们希望自己的自定义命令(在ContextMenu中)启用和禁用。 已经有一个ApplicationCommands.Cut,Copy和Paste,这些命令正确地响应剪贴板的变化。 所以我们只是添加了以下的EventHandler。
ApplicationCommands.Paste.CanExecuteChanged += new EventHandler(Paste_CanExecuteChanged); private void Paste_CanExecuteChanged(object sender, EventArgs e) { ourVariable= Clipboard.ContainsText(); }
我们实际上是通过我们自己的Command来控制CanExecute。 为我们所需要的工作,也许会帮助其他人。
我相信以前的解决scheme之一不检查dispose方法上的空值:
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); } /// <summary> /// Clipboard contents changed. /// </summary> public event EventHandler<ClipboardChangedEventArgs> 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; } } }
有多种方式做这个,但这是我最喜欢的,为我工作。 我创build了一个类库,以便其他人可以添加项目,并包含DLL,然后只需调用它,并在应用程序中的任何位置使用它。
这个答案是在这个帮助下完成的。
- 创build类库项目并将其命名为ClipboardHelper。
- 用ClipboardMonitorreplaceClass1名称。
- 将下面的代码添加到它。
- 添加System.Windows.Forms参考。
代码下的更多步骤。
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 { /// <summary>Specifies the standard ANSI text format. This static field is read-only. /// </summary> /// <filterpriority>1</filterpriority> Text, /// <summary>Specifies the standard Windows Unicode text format. This static field /// is read-only.</summary> /// <filterpriority>1</filterpriority> UnicodeText, /// <summary>Specifies the Windows device-independent bitmap (DIB) format. This static /// field is read-only.</summary> /// <filterpriority>1</filterpriority> Dib, /// <summary>Specifies a Windows bitmap format. This static field is read-only.</summary> /// <filterpriority>1</filterpriority> Bitmap, /// <summary>Specifies the Windows enhanced metafile format. This static field is /// read-only.</summary> /// <filterpriority>1</filterpriority> EnhancedMetafile, /// <summary>Specifies the Windows metafile format, which Windows Forms does not /// directly use. This static field is read-only.</summary> /// <filterpriority>1</filterpriority> MetafilePict, /// <summary>Specifies the Windows symbolic link format, which Windows Forms does /// not directly use. This static field is read-only.</summary> /// <filterpriority>1</filterpriority> SymbolicLink, /// <summary>Specifies the Windows Data Interchange Format (DIF), which Windows Forms /// does not directly use. This static field is read-only.</summary> /// <filterpriority>1</filterpriority> Dif, /// <summary>Specifies the Tagged Image File Format (TIFF), which Windows Forms does /// not directly use. This static field is read-only.</summary> /// <filterpriority>1</filterpriority> Tiff, /// <summary>Specifies the standard Windows original equipment manufacturer (OEM) /// text format. This static field is read-only.</summary> /// <filterpriority>1</filterpriority> OemText, /// <summary>Specifies the Windows palette format. This static field is read-only. /// </summary> /// <filterpriority>1</filterpriority> Palette, /// <summary>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.</summary> /// <filterpriority>1</filterpriority> PenData, /// <summary>Specifies the Resource Interchange File Format (RIFF) audio format, /// which Windows Forms does not directly use. This static field is read-only.</summary> /// <filterpriority>1</filterpriority> Riff, /// <summary>Specifies the wave audio format, which Windows Forms does not directly /// use. This static field is read-only.</summary> /// <filterpriority>1</filterpriority> WaveAudio, /// <summary>Specifies the Windows file drop format, which Windows Forms does not /// directly use. This static field is read-only.</summary> /// <filterpriority>1</filterpriority> FileDrop, /// <summary>Specifies the Windows culture format, which Windows Forms does not directly /// use. This static field is read-only.</summary> /// <filterpriority>1</filterpriority> Locale, /// <summary>Specifies text consisting of HTML data. This static field is read-only. /// </summary> /// <filterpriority>1</filterpriority> Html, /// <summary>Specifies text consisting of Rich Text Format (RTF) data. This static /// field is read-only.</summary> /// <filterpriority>1</filterpriority> Rtf, /// <summary>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.</summary> /// <filterpriority>1</filterpriority> CommaSeparatedValue, /// <summary>Specifies the Windows Forms string class format, which Windows Forms /// uses to store string objects. This static field is read-only.</summary> /// <filterpriority>1</filterpriority> StringFormat, /// <summary>Specifies a format that encapsulates any type of Windows Forms object. /// This static field is read-only.</summary> /// <filterpriority>1</filterpriority> Serializable, } }
- 在你的其他项目中,右键点击解决scheme并添加 – >退出项目 – > ClipboardHelper.csproj
- 在你的项目上,右键点击References – > Add Reference – > Solution – > Select ClipboardHelper。
- 在使用ClipboardHelper的项目types的类文件中。
-
您现在可以键入ClipboardMonitor.Start或.Stop或.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... } }
这里是使用AddClipboardFormatListener
一个很好的例子。
为了做到这一点,我们需要AddClipboardFormatListener
和RemoveClipboardFormatListener
。
/// <summary> /// Places the given window in the system-maintained clipboard format listener list. /// </summary> [DllImport("user32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] static extern bool AddClipboardFormatListener(IntPtr hwnd); /// <summary> /// Removes the given window from the system-maintained clipboard format listener list. /// </summary> [DllImport("user32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] static extern bool RemoveClipboardFormatListener(IntPtr hwnd); /// <summary> /// Sent when the contents of the clipboard have changed. /// </summary> private const int WM_CLIPBOARDUPDATE = 0x031D;
然后,我们需要通过调用AddClipboardFormatListener
方法,将窗口的句柄作为参数,将我们的窗口添加到剪贴板格式监听器列表中。 将下面的代码放在主窗口窗体构造函数或其任何加载事件中。
AddClipboardFormatListener(this.Handle); // Add our window to the clipboard's format listener list.
重写WndProc
方法,以便在发送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 } } }
最后,确保在closures表单之前从剪贴板格式侦听器列表中删除主窗口。
private void Form1_FormClosing(object sender, FormClosingEventArgs e) { RemoveClipboardFormatListener(this.Handle); // Remove our window from the clipboard's format listener list. }