dan kemudian Anda akan menerima WM_DRAWCLIPBOARDpesan tersebut, yang dapat Anda tangani dengan mengganti WndProc:
protectedoverridevoidWndProc(ref Message m)
{
switch ((Win32.Msgs)m.Msg)
{
case Win32.Msgs.WM_DRAWCLIPBOARD:
// Handle clipboard changedbreak;
// ...
}
}
(Masih banyak yang harus dilakukan; meneruskan hal-hal di sepanjang rantai clipboard dan membatalkan pendaftaran tampilan Anda, tetapi Anda bisa mendapatkannya dari artikel )
Ini hanya berfungsi pada formulir pertama yang dibuka ... katakanlah jika saya memiliki MyForm1 dan myForm2, jadi saya membuka myForm1, lalu MyForm2, acara ClipboardChanged hanya akan dimunculkan di MyForm1. Maksud saya, dalam aplikasi MDI ...
serhio
Tautannya sudah mati. Ada cadangan yang Anda sadari? +1 tetap.
Patrick Hofman
1
Untuk orang malas: Atur pengatur waktu yang berdetak 1ms. Kemudian, dengan setiap centang, periksa apakah konten clipboard Anda berubah. Hook ini meningkatkan peringatan virus dan trojan di komputer saya.
C4d
1
Ini melewati setiap jendela MSG ke formulir dan membuatnya sangat sulit untuk men-debug kode
Demikian juga, SharpClipboard sebagai pustaka bisa lebih bermanfaat karena merangkum fitur yang sama ke dalam satu pustaka komponen yang bagus. Anda kemudian dapat mengakses ClipboardChangedacaranya dan mendeteksi berbagai format data saat dipotong / disalin.
Willy Kimura
78
Untuk kelengkapan, inilah kontrol yang saya gunakan dalam kode produksi. Cukup seret dari desainer dan klik dua kali untuk membuat pengendali acara.
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Drawing;
namespaceClipboardAssist {
// Must inherit Control, not Component, in order to have Handle
[DefaultEvent("ClipboardChanged")]
publicpartialclassClipboardMonitor : Control
{
IntPtr nextClipboardViewer;
publicClipboardMonitor()
{
this.BackColor = Color.Red;
this.Visible = false;
nextClipboardViewer = (IntPtr)SetClipboardViewer((int)this.Handle);
}
///<summary>/// Clipboard contents changed.///</summary>publicevent EventHandler<ClipboardChangedEventArgs> ClipboardChanged;
protectedoverridevoidDispose(bool disposing)
{
ChangeClipboardChain(this.Handle, nextClipboardViewer);
}
[DllImport("User32.dll")]
protectedstaticexternintSetClipboardViewer(int hWndNewViewer);
[DllImport("User32.dll", CharSet = CharSet.Auto)]
publicstaticexternboolChangeClipboardChain(IntPtr hWndRemove, IntPtr hWndNewNext);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
publicstaticexternintSendMessage(IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam);
protectedoverridevoidWndProc(ref System.Windows.Forms.Message m)
{
// defined in winuser.hconstint WM_DRAWCLIPBOARD = 0x308;
constint 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;
}
}
voidOnClipboardChanged()
{
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());
}
}
}
publicclassClipboardChangedEventArgs : EventArgs
{
publicreadonly IDataObject DataObject;
publicClipboardChangedEventArgs(IDataObject dataObject)
{
DataObject = dataObject;
}
}
}
Kerja bagus! Kode panggilan acara Anda tidak aman untuk thread. Anda harus membuat salinan lokal, atau memulai acara dengan delegasi kosong. Anda juga lupa kata kunci 'event' dalam definisi ClipboardChanged :)
Ohad Schneider
1
@ohadsc Terima kasih atas koreksinya. Sejauh yang saya tahu, WndProc dipanggil di thread UI. Karena kelas tersebut berasal dari Control, klien harus memanggilnya di thread UI juga.
dbkk
Ini hanya berfungsi pada formulir pertama yang dibuka ... katakanlah jika saya memiliki MyForm1 dan myForm2, jadi saya membuka myForm1, lalu MyForm2, acara ClipboardChanged hanya akan dimunculkan di MyForm1 ... Maksud saya, dalam aplikasi MDI ...
serhio
Entah bagaimana panggilan Anda ke SetClipboardViewer menetapkan Kode Kesalahan Win32 1400: "Pegangan jendela tidak valid.". Tapi masih berhasil. Ini agak aneh bagiku.
metacircle
1
SharpClipboard sebagai pustaka bisa lebih bermanfaat karena merangkum fitur yang sama ke dalam satu pustaka komponen yang bagus. Anda kemudian dapat mengakses ClipboardChangedacaranya dan mendeteksi berbagai format data saat dipotong / disalin.
Willy Kimura
26
Saya mendapat tantangan ini di WPF dan akhirnya menggunakan pendekatan yang dijelaskan di bawah ini. Untuk formulir windows ada contoh bagus di tempat lain dalam jawaban ini, seperti kontrol ClipboardHelper.
Untuk WPF kita tidak dapat menimpa WndProc, jadi kita harus menghubungkannya secara eksplisit dengan panggilan AddHook HwndSource menggunakan Source from a window. Pendengar clipboard masih menggunakan panggilan interop asli AddClipboardFormatListener.
Metode asli:
internalstaticclassNativeMethods
{
// See http://msdn.microsoft.com/en-us/library/ms649021%28v=vs.85%29.aspxpublicconstint WM_CLIPBOARDUPDATE = 0x031D;
publicstatic 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)]
publicstaticexternboolAddClipboardFormatListener(IntPtr hwnd);
}
Kelas Clipboard Manager:
using System.Windows;
using System.Windows.Interop;
publicclassClipboardManager
{
publicevent EventHandler ClipboardChanged;
publicClipboardManager(Window windowSource)
{
HwndSource source = PresentationSource.FromVisual(windowSource) as HwndSource;
if(source == null)
{
thrownew 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);
}
privatevoidOnClipboardChanged()
{
ClipboardChanged?.Invoke(this, EventArgs.Empty);
}
privatestaticreadonly IntPtr WndProcSuccess = IntPtr.Zero;
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, refbool handled)
{
if (msg == NativeMethods.WM_CLIPBOARDUPDATE)
{
OnClipboardChanged();
handled = true;
}
return WndProcSuccess;
}
}
Ini digunakan di jendela WPF dengan menambahkan acara di OnSourceInitialized atau yang lebih baru seperti acara Window.Loaded atau selama operasi. (ketika kami memiliki cukup informasi untuk menggunakan kait asli):
publicpartialclassMainWindow : Window
{
publicMainWindow()
{
InitializeComponent();
}
protectedoverridevoidOnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
// Initialize the clipboard now that we have a window soruce to usevar windowClipboardManager = new ClipboardManager(this);
windowClipboardManager.ClipboardChanged += ClipboardChanged;
}
privatevoidClipboardChanged(object sender, EventArgs e)
{
// Handle your clipboard update here, debug logging example:if (Clipboard.ContainsText())
{
Debug.WriteLine(Clipboard.GetText());
}
}
}
Saya menggunakan pendekatan ini dalam proyek penganalisis item Path of Exile, karena game tersebut memaparkan informasi item melalui clipboard saat Anda menekan Ctrl-C.
Oke jadi ini adalah posting lama tetapi kami menemukan solusi yang tampaknya sangat sederhana dibandingkan dengan rangkaian jawaban saat ini. Kami menggunakan WPF dan kami ingin memiliki Perintah kustom kami sendiri (dalam ContextMenu) mengaktifkan dan menonaktifkan jika Clipboard berisi teks. Sudah ada ApplicationCommands.Cut, Salin dan Tempel dan perintah ini merespons dengan benar ke clipboard berubah. Jadi kami baru saja menambahkan EventHandler berikut.
Kami sebenarnya mengontrol CanExecute atas Perintah kami sendiri dengan cara ini. Bekerja untuk apa yang kami butuhkan dan mungkin akan membantu orang lain di luar sana.
Solusi hebat, karena sangat sederhana ... Terima kasih!
okieh
1
Ini adalah solusi luar biasa untuk masalah khusus mengaktifkan atau menonaktifkan perintah tempel. Sayangnya ini tidak mencakup skenario khusus "teks berubah", dan tidak akan aktif saat menyalin beberapa baris teks yang berbeda misalnya.
Colin Dabritz
11
Ada banyak cara untuk melakukan ini, tetapi ini adalah favorit saya dan berhasil untuk saya. Saya telah membuat perpustakaan kelas sehingga orang lain dapat menambahkan proyek dan menyertakan DLL kemudian cukup memanggilnya dan menggunakannya di mana pun mereka inginkan dalam aplikasi mereka.
Buat proyek Perpustakaan Kelas dan beri nama ClipboardHelper.
Ganti nama Class1 dengan ClipboardMonitor.
Tambahkan kode di bawah ini ke dalamnya.
Tambahkan referensi System.Windows.Forms.
Lebih banyak langkah di bawah kode.
using System;
using System.Windows.Forms;
using System.Threading;
using System.Runtime.InteropServices;
namespaceClipboardHelper
{
publicstaticclassClipboardMonitor
{
publicdelegatevoidOnClipboardChangeEventHandler(ClipboardFormat format, object data);
publicstaticevent OnClipboardChangeEventHandler OnClipboardChange;
publicstaticvoidStart()
{
ClipboardWatcher.Start();
ClipboardWatcher.OnClipboardChange += (ClipboardFormat format, object data) =>
{
if (OnClipboardChange != null)
OnClipboardChange(format, data);
};
}
publicstaticvoidStop()
{
OnClipboardChange = null;
ClipboardWatcher.Stop();
}
classClipboardWatcher : Form
{
// static instance of this formprivatestatic ClipboardWatcher mInstance;
// needed to dispose this formstatic IntPtr nextClipboardViewer;
publicdelegatevoidOnClipboardChangeEventHandler(ClipboardFormat format, object data);
publicstaticevent OnClipboardChangeEventHandler OnClipboardChange;
// start listeningpublicstaticvoidStart()
{
// we can only have one instance if this classif (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)publicstaticvoidStop()
{
mInstance.Invoke(new MethodInvoker(() =>
{
ChangeClipboardChain(mInstance.Handle, nextClipboardViewer);
}));
mInstance.Invoke(new MethodInvoker(mInstance.Close));
mInstance.Dispose();
mInstance = null;
}
// on load: (hide this window)protectedoverridevoidSetVisibleCore(boolvalue)
{
CreateHandle();
mInstance = this;
nextClipboardViewer = SetClipboardViewer(mInstance.Handle);
base.SetVisibleCore(false);
}
[DllImport("User32.dll", CharSet = CharSet.Auto)]
privatestaticextern IntPtr SetClipboardViewer(IntPtr hWndNewViewer);
[DllImport("User32.dll", CharSet = CharSet.Auto)]
privatestaticexternboolChangeClipboardChain(IntPtr hWndRemove, IntPtr hWndNewNext);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
privatestaticexternintSendMessage(IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam);
// defined in winuser.hconstint WM_DRAWCLIPBOARD = 0x308;
constint WM_CHANGECBCHAIN = 0x030D;
protectedoverridevoidWndProc(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;
}
}
staticreadonlystring[] formats = Enum.GetNames(typeof(ClipboardFormat));
privatevoidClipChanged()
{
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);
}
}
}
publicenum 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,
}
}
Dalam proyek Anda yang lain, klik kanan pada solusi dan Add -> Exiting Project -> ClipboardHelper.csproj
Pada proyek Anda, buka dan klik kanan Referensi -> Tambahkan Referensi -> Solusi -> Pilih ClipboardHelper.
Di file kelas Anda dari jenis proyek menggunakan ClipboardHelper.
Anda sekarang dapat mengetik ClipboardMonitor.Start atau .Stop atau .OnClipboardChanged
Ini tidak pernah nol karena konstruktor menetapkannya. Satu-satunya hal yang akan saya lakukan secara berbeda adalah memanggil base.Dispose()metode pembuangan.
jedmao
Bagaimanapun. Untuk tujuan verifikasi seperti yang Anda daftarkan, Anda harus menggunakan IntPtr.Zero untuk NULL (perhatikan bahwa ini tidak setara dengan C # null) stackoverflow.com/questions/1456861/…
walter
1
ChangeClipboardChain dijalankan selalu saat keluar di semua sampel msdn
walter
Tujuannya adalah untuk melepaskan dirinya dari rantai penampil clipboard
walter
6
SharpClipboard sebagai pustaka bisa lebih bermanfaat karena merangkum fitur yang sama ke dalam satu pustaka komponen yang bagus. Anda kemudian dapat mengakses ClipboardChangedacaranya dan mendeteksi berbagai format data saat dipotong / disalin.
Anda dapat memilih berbagai format data yang ingin Anda pantau:
var clipboard = new SharpClipboard();
clipboard.ObservableFormats.Texts = true;
clipboard.ObservableFormats.Files = true;
clipboard.ObservableFormats.Images = true;
clipboard.ObservableFormats.Others = true;
Berikut contoh penggunaan ClipboardChangedeventnya:
privatevoidClipboardChanged(Object sender, ClipboardChangedEventArgs e)
{
// Is the content copied of text type?if (e.ContentType == SharpClipboard.ContentTypes.Text)
{
// Get the cut/copied text.
Debug.WriteLine(clipboard.ClipboardText);
}
// Is the content copied of image type?elseif (e.ContentType == SharpClipboard.ContentTypes.Image)
{
// Get the cut/copied image.
Image img = clipboard.ClipboardImage;
}
// Is the content copied of file type?elseif (e.ContentType == SharpClipboard.ContentTypes.Files)
{
// Get the cut/copied file/files.
Debug.WriteLine(clipboard.ClipboardFiles.ToArray());
// ...or use 'ClipboardFile' to get a single copied file.
Debug.WriteLine(clipboard.ClipboardFile);
}
// If the cut/copied content is complex, use 'Other'.elseif (e.ContentType == SharpClipboard.ContentTypes.Other)
{
// Do something with 'e.Content' here...
}
}
Anda juga dapat mengetahui aplikasi tempat kejadian potong / salin terjadi bersama dengan detailnya:
privatevoidClipboardChanged(Object sender, SharpClipboard.ClipboardChangedEventArgs e)
{
// Gets the application's executable name.
Debug.WriteLine(e.SourceApplication.Name);
// Gets the application's window title.
Debug.WriteLine(e.SourceApplication.Title);
// Gets the application's process ID.
Debug.WriteLine(e.SourceApplication.ID.ToString());
// Gets the application's executable path.
Debug.WriteLine(e.SourceApplication.Path);
}
Ada juga peristiwa lain seperti MonitorChangedperistiwa yang mendengarkan setiap kali pemantauan papan klip dinonaktifkan, yang berarti Anda dapat mengaktifkan atau menonaktifkan pemantauan papan klip saat waktu proses.
Selain semua ini, karena ini adalah komponen, Anda dapat menggunakannya dalam Designer View dengan menyeret-dan-melepaskannya ke Formulir Windows, membuatnya sangat mudah bagi siapa saja untuk menyesuaikan opsinya dan bekerja dengan acara bawaannya.
SharpClipboard tampaknya menjadi opsi terbaik untuk skenario pemantauan clipboard di .NET.
Jawaban:
Saya pikir Anda harus menggunakan beberapa p / invoke:
[DllImport("User32.dll", CharSet=CharSet.Auto)] public static extern IntPtr SetClipboardViewer(IntPtr hWndNewViewer);
Lihat artikel ini tentang cara mengatur monitor clipboard di c #
Pada dasarnya Anda mendaftarkan aplikasi Anda sebagai clipboard viewer menggunakan
_ClipboardViewerNext = SetClipboardViewer(this.Handle);
dan kemudian Anda akan menerima
WM_DRAWCLIPBOARD
pesan tersebut, yang dapat Anda tangani dengan menggantiWndProc
:protected override void WndProc(ref Message m) { switch ((Win32.Msgs)m.Msg) { case Win32.Msgs.WM_DRAWCLIPBOARD: // Handle clipboard changed break; // ... } }
(Masih banyak yang harus dilakukan; meneruskan hal-hal di sepanjang rantai clipboard dan membatalkan pendaftaran tampilan Anda, tetapi Anda bisa mendapatkannya dari artikel )
sumber
ClipboardChanged
acaranya dan mendeteksi berbagai format data saat dipotong / disalin.Untuk kelengkapan, inilah kontrol yang saya gunakan dalam kode produksi. Cukup seret dari desainer dan klik dua kali untuk membuat pengendali acara.
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; } } }
sumber
ClipboardChanged
acaranya dan mendeteksi berbagai format data saat dipotong / disalin.Saya mendapat tantangan ini di WPF dan akhirnya menggunakan pendekatan yang dijelaskan di bawah ini. Untuk formulir windows ada contoh bagus di tempat lain dalam jawaban ini, seperti kontrol ClipboardHelper.
Untuk WPF kita tidak dapat menimpa WndProc, jadi kita harus menghubungkannya secara eksplisit dengan panggilan AddHook HwndSource menggunakan Source from a window. Pendengar clipboard masih menggunakan panggilan interop asli AddClipboardFormatListener.
Metode asli:
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); }
Kelas Clipboard Manager:
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; } }
Ini digunakan di jendela WPF dengan menambahkan acara di OnSourceInitialized atau yang lebih baru seperti acara Window.Loaded atau selama operasi. (ketika kami memiliki cukup informasi untuk menggunakan kait asli):
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()); } } }
Saya menggunakan pendekatan ini dalam proyek penganalisis item Path of Exile, karena game tersebut memaparkan informasi item melalui clipboard saat Anda menekan Ctrl-C.
https://github.com/ColinDabritz/PoeItemAnalyzer
Saya harap ini membantu seseorang dengan penanganan perubahan clipboard WPF!
sumber
ClipboardChanged?.Invoke
melihat Menggunakan Operator Bersyarat Null Baru di C # 6 , bagian Skenario LainnyaOke jadi ini adalah posting lama tetapi kami menemukan solusi yang tampaknya sangat sederhana dibandingkan dengan rangkaian jawaban saat ini. Kami menggunakan WPF dan kami ingin memiliki Perintah kustom kami sendiri (dalam ContextMenu) mengaktifkan dan menonaktifkan jika Clipboard berisi teks. Sudah ada ApplicationCommands.Cut, Salin dan Tempel dan perintah ini merespons dengan benar ke clipboard berubah. Jadi kami baru saja menambahkan EventHandler berikut.
ApplicationCommands.Paste.CanExecuteChanged += new EventHandler(Paste_CanExecuteChanged); private void Paste_CanExecuteChanged(object sender, EventArgs e) { ourVariable= Clipboard.ContainsText(); }
Kami sebenarnya mengontrol CanExecute atas Perintah kami sendiri dengan cara ini. Bekerja untuk apa yang kami butuhkan dan mungkin akan membantu orang lain di luar sana.
sumber
Ada banyak cara untuk melakukan ini, tetapi ini adalah favorit saya dan berhasil untuk saya. Saya telah membuat perpustakaan kelas sehingga orang lain dapat menambahkan proyek dan menyertakan DLL kemudian cukup memanggilnya dan menggunakannya di mana pun mereka inginkan dalam aplikasi mereka.
Jawaban ini dibuat dengan bantuan yang satu ini .
Lebih banyak langkah di bawah kode.
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, } }
Anda sekarang dapat mengetik ClipboardMonitor.Start atau .Stop atau .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... } }
sumber
Saya percaya salah satu solusi sebelumnya tidak memeriksa nol pada metode pembuangan:
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; } } }
sumber
base.Dispose()
metode pembuangan.SharpClipboard sebagai pustaka bisa lebih bermanfaat karena merangkum fitur yang sama ke dalam satu pustaka komponen yang bagus. Anda kemudian dapat mengakses
ClipboardChanged
acaranya dan mendeteksi berbagai format data saat dipotong / disalin.Anda dapat memilih berbagai format data yang ingin Anda pantau:
var clipboard = new SharpClipboard(); clipboard.ObservableFormats.Texts = true; clipboard.ObservableFormats.Files = true; clipboard.ObservableFormats.Images = true; clipboard.ObservableFormats.Others = true;
Berikut contoh penggunaan
ClipboardChanged
eventnya:private void ClipboardChanged(Object sender, ClipboardChangedEventArgs e) { // Is the content copied of text type? if (e.ContentType == SharpClipboard.ContentTypes.Text) { // Get the cut/copied text. Debug.WriteLine(clipboard.ClipboardText); } // Is the content copied of image type? else if (e.ContentType == SharpClipboard.ContentTypes.Image) { // Get the cut/copied image. Image img = clipboard.ClipboardImage; } // Is the content copied of file type? else if (e.ContentType == SharpClipboard.ContentTypes.Files) { // Get the cut/copied file/files. Debug.WriteLine(clipboard.ClipboardFiles.ToArray()); // ...or use 'ClipboardFile' to get a single copied file. Debug.WriteLine(clipboard.ClipboardFile); } // If the cut/copied content is complex, use 'Other'. else if (e.ContentType == SharpClipboard.ContentTypes.Other) { // Do something with 'e.Content' here... } }
Anda juga dapat mengetahui aplikasi tempat kejadian potong / salin terjadi bersama dengan detailnya:
private void ClipboardChanged(Object sender, SharpClipboard.ClipboardChangedEventArgs e) { // Gets the application's executable name. Debug.WriteLine(e.SourceApplication.Name); // Gets the application's window title. Debug.WriteLine(e.SourceApplication.Title); // Gets the application's process ID. Debug.WriteLine(e.SourceApplication.ID.ToString()); // Gets the application's executable path. Debug.WriteLine(e.SourceApplication.Path); }
Ada juga peristiwa lain seperti
MonitorChanged
peristiwa yang mendengarkan setiap kali pemantauan papan klip dinonaktifkan, yang berarti Anda dapat mengaktifkan atau menonaktifkan pemantauan papan klip saat waktu proses.Selain semua ini, karena ini adalah komponen, Anda dapat menggunakannya dalam Designer View dengan menyeret-dan-melepaskannya ke Formulir Windows, membuatnya sangat mudah bagi siapa saja untuk menyesuaikan opsinya dan bekerja dengan acara bawaannya.
SharpClipboard tampaknya menjadi opsi terbaik untuk skenario pemantauan clipboard di .NET.
sumber
[DllImport("User32.dll", CharSet = CharSet.Auto)] public static extern IntPtr SetClipboardViewer(IntPtr hWndNewViewer); private IntPtr _ClipboardViewerNext; private void Form1_Load(object sender, EventArgs e) { _ClipboardViewerNext = SetClipboardViewer(this.Handle); } protected override void WndProc(ref System.Windows.Forms.Message m) { const int WM_DRAWCLIPBOARD = 0x308; switch (m.Msg) { case WM_DRAWCLIPBOARD: //Clipboard is Change //your code.............. break; default: base.WndProc(ref m); break; } }
sumber