а затем вы получите WM_DRAWCLIPBOARDсообщение, которое вы можете обработать, переопределив WndProc:
protectedoverridevoidWndProc(ref Message m)
{
switch ((Win32.Msgs)m.Msg)
{
case Win32.Msgs.WM_DRAWCLIPBOARD:
// Handle clipboard changedbreak;
// ...
}
}
(Еще многое предстоит сделать; передача вещей по цепочке буфера обмена и отмена регистрации вашего представления, но вы можете получить это из статьи )
Он работает только с первой открытой формой ... скажем, если у меня есть MyForm1 и myForm2, поэтому я открываю myForm1, затем MyForm2, событие ClipboardChanged будет возникать только в MyForm1. Я имею в виду, в приложении MDI ...
serhio 05
Ссылка мертвая. Любая резервная копия, о которой вы знаете? +1 тем не менее.
Патрик Хофман
1
Для ленивых: установите таймер с отсчетом 1 мс. Затем с каждым тиком проверяйте, изменилось ли содержимое буфера обмена ur. Эти хуки вызывают на моем компьютере предупреждения о вирусах и троянах.
C4d
1
Он передает все окна MSG в форму и затрудняет отладку кода
Точно так же SharpClipboard как библиотека может принести больше пользы, поскольку объединяет те же функции в одну прекрасную библиотеку компонентов. Затем вы можете получить доступ к его ClipboardChangedсобытию и обнаружить различные форматы данных, когда они вырезаны / скопированы.
Вилли Кимура
78
Для полноты, вот элемент управления, который я использую в производственном коде. Просто перетащите из дизайнера и дважды щелкните, чтобы создать обработчик событий.
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;
}
}
}
Прекрасная работа! Однако код вызова события не является потокобезопасным. Вы должны либо создать локальную копию, либо инициировать событие с пустым делегатом. Вы также забыли ключевое слово 'event' в определении ClipboardChanged :)
Охад Шнайдер,
1
@ohadsc Спасибо за исправления. Насколько мне известно, WndProc вызывается в потоке пользовательского интерфейса. Поскольку класс является производным от Control, клиенты также должны вызывать его в потоке пользовательского интерфейса.
dbkk
Он работает только в первой открытой форме ... скажем, если у меня есть MyForm1 и myForm2, поэтому я открываю myForm1, затем MyForm2, событие ClipboardChanged будет вызвано только в MyForm1 ... Я имею в виду, в приложении MDI ...
serhio 05
Каким-то образом ваш вызов SetClipboardViewer устанавливает код ошибки Win32 1400: «Недопустимый дескриптор окна». Но все равно работает. Мне это кажется немного странным.
метакруг
1
SharpClipboard как библиотека может принести больше пользы, поскольку он инкапсулирует те же функции в одну прекрасную библиотеку компонентов. Затем вы можете получить доступ к его ClipboardChangedсобытию и обнаружить различные форматы данных, когда они вырезаны / скопированы.
Вилли Кимура
26
У меня была эта проблема в WPF, и в итоге я использовал подход, описанный ниже. Для форм Windows в этом ответе есть отличные примеры, такие как элемент управления ClipboardHelper.
Для WPF мы не можем переопределить WndProc, поэтому мы должны явно привязать его к вызову HwndSource AddHook с использованием источника из окна. Слушатель буфера обмена по-прежнему использует собственный вызов взаимодействия AddClipboardFormatListener.
Родные методы:
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);
}
Класс диспетчера буфера обмена:
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;
}
}
Это используется в окне WPF путем добавления события в OnSourceInitialized или более поздней версии, например событие Window.Loaded, или во время работы. (когда у нас достаточно информации для использования собственных хуков):
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());
}
}
}
Я использую этот подход в проекте анализатора предметов Path of Exile, поскольку игра предоставляет информацию об предмете через буфер обмена, когда вы нажимаете Ctrl-C.
Хорошо, это старый пост, но мы нашли решение, которое кажется очень простым по сравнению с текущим набором ответов. Мы используем WPF и хотели, чтобы наши собственные настраиваемые команды (в ContextMenu) включались и отключались, если буфер обмена содержит текст. Уже существует ApplicationCommands.Cut, Copy и Paste, и эти команды правильно реагируют на изменение буфера обмена. Итак, мы просто добавили следующий обработчик событий.
Отличное решение, потому что это так просто ... Спасибо!
okieh
1
Это фантастическое решение конкретной проблемы включения или отключения команды вставки. К сожалению, он не распространяется на конкретный сценарий «текст изменен» и не срабатывает, например, при копировании нескольких разных строк текста.
Колин Дабриц 08
11
Есть несколько способов сделать это, но это мой любимый и работает для меня. Я создал библиотеку классов, чтобы другие могли добавить проект и включить DLL, а затем просто вызвать ее и использовать в своих приложениях, где захотят.
Создайте проект библиотеки классов и назовите его ClipboardHelper.
Замените имя Class1 на ClipboardMonitor.
Добавьте в него приведенный ниже код.
Добавьте ссылку на System.Windows.Forms.
Больше шагов под кодом.
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,
}
}
В других ваших проектах щелкните правой кнопкой мыши решение и выберите Добавить -> Выход из проекта -> ClipboardHelper.csproj
В своем проекте перейдите и щелкните правой кнопкой мыши Ссылки -> Добавить ссылку -> Решение -> Выберите ClipboardHelper.
В вашем файле класса типа проекта с помощью ClipboardHelper.
Теперь вы можете ввести ClipboardMonitor.Start или .Stop или .OnClipboardChanged.
SharpClipboard в качестве библиотеки может принести больше пользы, поскольку он объединяет те же функции в одну прекрасную библиотеку компонентов. Затем вы можете получить доступ к егоClipboardChanged событию и обнаружить различные форматы данных, когда они вырезаны / скопированы.
Вы можете выбрать различные форматы данных, которые хотите отслеживать:
var clipboard = new SharpClipboard();
clipboard.ObservableFormats.Texts = true;
clipboard.ObservableFormats.Files = true;
clipboard.ObservableFormats.Images = true;
clipboard.ObservableFormats.Others = true;
Вот пример использования его ClipboardChangedсобытия:
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...
}
}
Вы также можете узнать приложение, в котором произошло событие вырезания / копирования, вместе с его деталями:
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);
}
Есть также другие события, такие как MonitorChangedсобытие, которое прослушивается всякий раз, когда мониторинг буфера обмена отключен, что означает, что вы можете включить или отключить мониторинг буфера обмена во время выполнения.
В дополнение ко всему этому, поскольку это компонент, вы можете использовать его в представлении конструктора , перетаскивая его в форму Windows Forms, что упрощает настройку параметров и работу со встроенными событиями для всех.
SharpClipboard кажется лучшим вариантом для сценариев мониторинга буфера обмена в .NET.
Он никогда не равен нулю, потому что его устанавливает конструктор. Единственное, что я бы сделал иначе, это вызов base.Dispose()метода dispose.
jedmao
Так или иначе. Для целей проверки, как вы указали, вы должны использовать IntPtr.Zero для NULL (обратите внимание, что это не эквивалентно NULL C #) stackoverflow.com/questions/1456861/…
Уолтер
1
ChangeClipboardChain выполняется всегда на выходе во всех образцах MSDN
Вальтер
Цель состоит в том, чтобы удалить себя из цепочки просмотра буфера обмена
Ответы:
Думаю, вам придется использовать 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; // ... } }
(Еще многое предстоит сделать; передача вещей по цепочке буфера обмена и отмена регистрации вашего представления, но вы можете получить это из статьи )
источник
ClipboardChanged
событию и обнаружить различные форматы данных, когда они вырезаны / скопированы.Для полноты, вот элемент управления, который я использую в производственном коде. Просто перетащите из дизайнера и дважды щелкните, чтобы создать обработчик событий.
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; } } }
источник
ClipboardChanged
событию и обнаружить различные форматы данных, когда они вырезаны / скопированы.У меня была эта проблема в WPF, и в итоге я использовал подход, описанный ниже. Для форм Windows в этом ответе есть отличные примеры, такие как элемент управления ClipboardHelper.
Для WPF мы не можем переопределить WndProc, поэтому мы должны явно привязать его к вызову 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); }
Класс диспетчера буфера обмена:
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; } }
Это используется в окне WPF путем добавления события в OnSourceInitialized или более поздней версии, например событие Window.Loaded, или во время работы. (когда у нас достаточно информации для использования собственных хуков):
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()); } } }
Я использую этот подход в проекте анализатора предметов Path of Exile, поскольку игра предоставляет информацию об предмете через буфер обмена, когда вы нажимаете Ctrl-C.
https://github.com/ColinDabritz/PoeItemAnalyzer
Надеюсь, это поможет кому-то с обработкой изменений буфера обмена WPF!
источник
ClipboardChanged?.Invoke
увидеть Использование нового оператора сХорошо, это старый пост, но мы нашли решение, которое кажется очень простым по сравнению с текущим набором ответов. Мы используем WPF и хотели, чтобы наши собственные настраиваемые команды (в ContextMenu) включались и отключались, если буфер обмена содержит текст. Уже существует ApplicationCommands.Cut, Copy и Paste, и эти команды правильно реагируют на изменение буфера обмена. Итак, мы просто добавили следующий обработчик событий.
ApplicationCommands.Paste.CanExecuteChanged += new EventHandler(Paste_CanExecuteChanged); private void Paste_CanExecuteChanged(object sender, EventArgs e) { ourVariable= Clipboard.ContainsText(); }
Таким образом, мы фактически контролируем CanExecute в нашей собственной команде. Работает для того, что нам нужно, и, возможно, это поможет другим.
источник
Есть несколько способов сделать это, но это мой любимый и работает для меня. Я создал библиотеку классов, чтобы другие могли добавить проект и включить DLL, а затем просто вызвать ее и использовать в своих приложениях, где захотят.
Этот ответ был дан с помощью этого .
Больше шагов под кодом.
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, } }
Теперь вы можете ввести 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... } }
источник
SharpClipboard в качестве библиотеки может принести больше пользы, поскольку он объединяет те же функции в одну прекрасную библиотеку компонентов. Затем вы можете получить доступ к его
ClipboardChanged
событию и обнаружить различные форматы данных, когда они вырезаны / скопированы.Вы можете выбрать различные форматы данных, которые хотите отслеживать:
var clipboard = new SharpClipboard(); clipboard.ObservableFormats.Texts = true; clipboard.ObservableFormats.Files = true; clipboard.ObservableFormats.Images = true; clipboard.ObservableFormats.Others = true;
Вот пример использования его
ClipboardChanged
события: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... } }
Вы также можете узнать приложение, в котором произошло событие вырезания / копирования, вместе с его деталями:
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); }
Есть также другие события, такие как
MonitorChanged
событие, которое прослушивается всякий раз, когда мониторинг буфера обмена отключен, что означает, что вы можете включить или отключить мониторинг буфера обмена во время выполнения.В дополнение ко всему этому, поскольку это компонент, вы можете использовать его в представлении конструктора , перетаскивая его в форму Windows Forms, что упрощает настройку параметров и работу со встроенными событиями для всех.
SharpClipboard кажется лучшим вариантом для сценариев мониторинга буфера обмена в .NET.
источник
Я считаю, что одно из более ранних решений не проверяет наличие нуля в методе удаления:
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; } } }
источник
base.Dispose()
метода dispose.[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; } }
источник