Có sự kiện cập nhật hoặc thay đổi khay nhớ tạm nào mà tôi có thể truy cập thông qua C # không?
Câu trả lời:
Tôi nghĩ bạn sẽ phải sử dụng một số p / gọi:
[DllImport("User32.dll", CharSet=CharSet.Auto)]
public static extern IntPtr SetClipboardViewer(IntPtr hWndNewViewer);
Xem bài viết này về cách thiết lập màn hình khay nhớ tạm trong c #
Về cơ bản, bạn đăng ký ứng dụng của mình làm trình xem khay nhớ tạm bằng
_ClipboardViewerNext = SetClipboardViewer(this.Handle);
và sau đó bạn sẽ nhận được WM_DRAWCLIPBOARD
thông báo mà bạn có thể xử lý bằng cách ghi đè WndProc
:
protected override void WndProc(ref Message m)
{
switch ((Win32.Msgs)m.Msg)
{
case Win32.Msgs.WM_DRAWCLIPBOARD:
// Handle clipboard changed
break;
// ...
}
}
(Còn nhiều việc phải làm; chuyển mọi thứ dọc theo chuỗi khay nhớ tạm và hủy đăng ký chế độ xem của bạn, nhưng bạn có thể lấy điều đó từ bài viết )
ClipboardChanged
sự kiện của nó và phát hiện các định dạng dữ liệu khác nhau khi chúng được cắt / sao chép.
Để hoàn thiện, đây là kiểm soát tôi đang sử dụng trong mã sản xuất. Chỉ cần kéo từ trình thiết kế và nhấp đúp để tạo trình xử lý sự kiện.
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Drawing;
namespace ClipboardAssist {
// Must inherit Control, not Component, in order to have Handle
[DefaultEvent("ClipboardChanged")]
public partial class ClipboardMonitor : Control
{
IntPtr nextClipboardViewer;
public ClipboardMonitor()
{
this.BackColor = Color.Red;
this.Visible = false;
nextClipboardViewer = (IntPtr)SetClipboardViewer((int)this.Handle);
}
/// <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
sự kiện của nó và phát hiện các định dạng dữ liệu khác nhau khi chúng được cắt / sao chép.
Tôi đã gặp thử thách này trong WPF và kết thúc bằng cách sử dụng phương pháp được mô tả bên dưới. Đối với các biểu mẫu cửa sổ, có những ví dụ tuyệt vời ở nơi khác trong câu trả lời này, chẳng hạn như điều khiển ClipboardHelper.
Đối với WPF, chúng ta không thể ghi đè WndProc, vì vậy chúng ta phải kết nối nó một cách rõ ràng với lệnh gọi HwndSource AddHook bằng cách sử dụng Nguồn từ một cửa sổ. Trình nghe khay nhớ tạm vẫn sử dụng lệnh gọi tương tác gốc AddClipboardFormatListener.
Phương pháp gốc:
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);
}
Lớp quản lý bảng tạm:
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;
}
}
Điều này được sử dụng trong cửa sổ WPF bằng cách thêm sự kiện trong OnSourceInitialized trở lên, chẳng hạn như sự kiện Window.Loaded hoặc trong quá trình hoạt động. (khi chúng ta có đủ thông tin để sử dụng các hook gốc):
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());
}
}
}
Tôi đang sử dụng phương pháp này trong một dự án phân tích vật phẩm Path of Exile, vì trò chơi hiển thị thông tin vật phẩm qua khay nhớ tạm khi bạn nhấn Ctrl-C.
https://github.com/ColinDabritz/PoeItemAnalyzer
Tôi hy vọng điều này sẽ giúp ai đó xử lý thay đổi khay nhớ tạm WPF!
ClipboardChanged?.Invoke
nhìn thấy Sử dụng New Null Conditional Operator trong C # 6 , phần kịch bản khác
Ok, đây là một bài cũ nhưng chúng tôi đã tìm ra một giải pháp có vẻ rất đơn giản so với bộ đáp án hiện tại. Chúng tôi đang sử dụng WPF và chúng tôi muốn bật và tắt các Lệnh tùy chỉnh của riêng mình (trong ContextMenu) nếu Clipboard chứa văn bản. Đã có một ApplicationCommands.Cut, Copy và Paste và các lệnh này phản hồi chính xác với việc thay đổi khay nhớ tạm. Vì vậy, chúng tôi vừa thêm EventHandler sau.
ApplicationCommands.Paste.CanExecuteChanged += new EventHandler(Paste_CanExecuteChanged);
private void Paste_CanExecuteChanged(object sender, EventArgs e) {
ourVariable= Clipboard.ContainsText();
}
Chúng tôi thực sự đang điều khiển CanExecute trên Command của riêng mình theo cách này. Làm việc cho những gì chúng tôi cần và có thể nó sẽ giúp ích cho những người khác.
Có nhiều cách để làm điều này nhưng đây là cách tôi yêu thích và phù hợp với tôi. Tôi đã tạo một thư viện lớp để những người khác có thể thêm dự án và bao gồm DLL, sau đó chỉ cần gọi nó và sử dụng nó ở bất cứ đâu họ muốn trong ứng dụng của họ.
Câu trả lời này đã được thực hiện với sự trợ giúp của cái này .
Các bước khác dưới mã.
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,
}
}
Bây giờ bạn có thể nhập ClipboardMonitor.Start hoặc .Stop hoặc .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...
}
}
Tôi tin rằng một trong những giải pháp trước đó không kiểm tra giá trị rỗng trên phương pháp xử lý:
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()
trong phương thức vứt bỏ.
SharpClipboard như một thư viện có thể có nhiều lợi ích hơn vì nó đóng gói các tính năng giống nhau vào một thư viện thành phần tốt. Sau đó, bạn có thể truy cập ClipboardChanged
sự kiện của nó và phát hiện các định dạng dữ liệu khác nhau khi chúng được cắt / sao chép.
Bạn có thể chọn các định dạng dữ liệu khác nhau mà bạn muốn theo dõi:
var clipboard = new SharpClipboard();
clipboard.ObservableFormats.Texts = true;
clipboard.ObservableFormats.Files = true;
clipboard.ObservableFormats.Images = true;
clipboard.ObservableFormats.Others = true;
Đây là một ví dụ sử dụng ClipboardChanged
sự kiện của nó :
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...
}
}
Bạn cũng có thể tìm hiểu ứng dụng đã xảy ra sự kiện cắt / sao chép cùng với các chi tiết của nó:
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);
}
Ngoài ra còn có các sự kiện khác như MonitorChanged
sự kiện sẽ lắng nghe bất cứ khi nào tính năng giám sát khay nhớ tạm bị tắt, có nghĩa là bạn có thể bật hoặc tắt giám sát khay nhớ tạm trong thời gian chạy.
Ngoài tất cả những điều này, vì nó là một thành phần, bạn có thể sử dụng nó trong Chế độ xem nhà thiết kế bằng cách kéo và thả nó vào Biểu mẫu Windows, giúp mọi người cực kỳ dễ dàng tùy chỉnh các tùy chọn của nó và làm việc với các sự kiện có sẵn của nó.
SharpClipboard dường như là lựa chọn tốt nhất cho các tình huống giám sát clipboard trong .NET.
[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;
}
}