当前位置:  开发笔记 > 编程语言 > 正文

剪贴板事件C#

如何解决《剪贴板事件C#》经验,为你挑选了6个好方法。

是否有可以通过C#访问的剪贴板更改或更新事件?



1> dbkk..:

为了完整起见,这是我在生产代码中使用的控件.只需从设计器拖动并双击即可创建事件处理程序.

using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Drawing;

namespace ClipboardAssist {

// Must inherit Control, not Component, in order to have Handle
[DefaultEvent("ClipboardChanged")]
public partial class ClipboardMonitor : Control 
{
    IntPtr nextClipboardViewer;

    public ClipboardMonitor()
    {
        this.BackColor = Color.Red;
        this.Visible = false;

        nextClipboardViewer = (IntPtr)SetClipboardViewer((int)this.Handle);
    }

    /// 
    /// Clipboard contents changed.
    /// 
    public event EventHandler ClipboardChanged;

    protected override void Dispose(bool disposing)
    {
        ChangeClipboardChain(this.Handle, nextClipboardViewer);
    }

    [DllImport("User32.dll")]
    protected static extern int SetClipboardViewer(int hWndNewViewer);

    [DllImport("User32.dll", CharSet = CharSet.Auto)]
    public static extern bool ChangeClipboardChain(IntPtr hWndRemove, IntPtr hWndNewNext);

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern int SendMessage(IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam);

    protected override void WndProc(ref System.Windows.Forms.Message m)
    {
        // defined in winuser.h
        const int WM_DRAWCLIPBOARD = 0x308;
        const int WM_CHANGECBCHAIN = 0x030D;

        switch (m.Msg)
        {
            case WM_DRAWCLIPBOARD:
                OnClipboardChanged();
                SendMessage(nextClipboardViewer, m.Msg, m.WParam, m.LParam);
                break;

            case WM_CHANGECBCHAIN:
                if (m.WParam == nextClipboardViewer)
                    nextClipboardViewer = m.LParam;
                else
                    SendMessage(nextClipboardViewer, m.Msg, m.WParam, m.LParam);
                break;

            default:
                base.WndProc(ref m);
                break;
        }
    }

    void OnClipboardChanged()
    {
        try
        {
            IDataObject iData = Clipboard.GetDataObject();
            if (ClipboardChanged != null)
            {
                ClipboardChanged(this, new ClipboardChangedEventArgs(iData));
            }

        }
        catch (Exception e)
        {
            // Swallow or pop-up, not sure
            // Trace.Write(e.ToString());
            MessageBox.Show(e.ToString());
        }
    }
}

public class ClipboardChangedEventArgs : EventArgs
{
    public readonly IDataObject DataObject;

    public ClipboardChangedEventArgs(IDataObject dataObject)
    {
        DataObject = dataObject;
    }
}
}


很好!但是,您的事件调用代码不是线程安全的.您应该创建本地副本,或者使用空委托初始化事件.您还忘记了ClipboardChanged :)中的'event'关键字

2> Daniel LeChe..:

我想你将不得不使用一些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;
        // ... 
   }
}

(还有更多工作要做;沿剪贴板链传递内容并取消注册视图,但是你可以从文章中获得)



3> Colin Dabrit..:

我在WPF中遇到了这个挑战,最终使用了下面描述的方法.对于Windows表单,在这个答案的其他地方有很好的例子,例如ClipboardHelper控件.

对于WPF,我们不能覆盖WndProc,因此我们必须使用窗口中的Source显式地将其与HwndSource AddHook调用挂钩.剪贴板侦听器仍使用AddClipboardFormatListener本机互操作调用.

原生方法:

internal static class NativeMethods
{
    // See http://msdn.microsoft.com/en-us/library/ms649021%28v=vs.85%29.aspx
    public const int WM_CLIPBOARDUPDATE = 0x031D;
    public static IntPtr HWND_MESSAGE = new IntPtr(-3);

    // See http://msdn.microsoft.com/en-us/library/ms632599%28VS.85%29.aspx#message_only
    [DllImport("user32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool AddClipboardFormatListener(IntPtr hwnd);
}

剪贴板管理器类:

using System.Windows;
using System.Windows.Interop;

public class ClipboardManager
{
    public event EventHandler ClipboardChanged;

    public ClipboardManager(Window windowSource)
    {
        HwndSource source = PresentationSource.FromVisual(windowSource) as HwndSource;
        if(source == null)
        {
            throw new ArgumentException(
                "Window source MUST be initialized first, such as in the Window's OnSourceInitialized handler."
                , nameof(windowSource));
        }

        source.AddHook(WndProc);

        // get window handle for interop
        IntPtr windowHandle = new WindowInteropHelper(windowSource).Handle;

        // register for clipboard events
        NativeMethods.AddClipboardFormatListener(windowHandle);
    }

    private void OnClipboardChanged()
    {
        ClipboardChanged?.Invoke(this, EventArgs.Empty);
    }

    private static readonly IntPtr WndProcSuccess = IntPtr.Zero;

    private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
    {
        if (msg == NativeMethods.WM_CLIPBOARDUPDATE)
        {
            OnClipboardChanged();
            handled = true;
        }

        return WndProcSuccess;
    }
}

通过在OnSourceInitialized或更高版本中添加事件(例如Window.Loaded事件或在操作期间),可以在WPF窗口中使用它.(当我们有足够的信息来使用本机钩子时):

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    protected override void OnSourceInitialized(EventArgs e)
    {
        base.OnSourceInitialized(e);

        // Initialize the clipboard now that we have a window soruce to use
        var windowClipboardManager = new ClipboardManager(this);
        windowClipboardManager.ClipboardChanged += ClipboardChanged;
    }

    private void ClipboardChanged(object sender, EventArgs e)
    {
        // Handle your clipboard update here, debug logging example:
        if (Clipboard.ContainsText())
        {
            Debug.WriteLine(Clipboard.GetText());
        }
    }
}

我在Path of Exile项目分析器项目中使用此方法,因为当您按Ctrl-C时游戏通过剪贴板公开项目信息.

https://github.com/ColinDabritz/PoeItemAnalyzer

我希望这有助于WPF剪贴板更改处理的人!



4> 小智..:

好的,这是一篇旧帖子,但我们发现一个解决方案与目前的答案相比看起来非常简单.我们正在使用WPF,如果剪贴板包含文本,我们希望自己的自定义命令(在ContextMenu中)启用和禁用.已有ApplicationCommands.Cut,Copy和Paste,这些命令可以正确响应剪贴板更改.所以我们刚刚添加了以下EventHandler.

ApplicationCommands.Paste.CanExecuteChanged += new EventHandler(Paste_CanExecuteChanged);

private void Paste_CanExecuteChanged(object sender, EventArgs e) {
  ourVariable= Clipboard.ContainsText();
}

我们实际上是通过这种方式在我们自己的Command上控制CanExecute.为我们需要的东西工作,也许它会帮助其他人.



5> AzzamAziz..:

有多种方法可以做到这一点,但这是我最喜欢的,也适合我.我已经创建了一个类库,以便其他人可以添加项目并包含DLL然后只需调用它并在其应用程序中的任何地方使用它.

这个答案的帮助下做这一个.

    创建类库项目并将其命名为ClipboardHelper.

    用ClipboardMonitor替换Class1名称.

    将以下代码添加到其中.

    添加System.Windows.Forms参考.

代码下的更多步骤.

using System;
using System.Windows.Forms;
using System.Threading;
using System.Runtime.InteropServices;

namespace ClipboardHelper
{
    public static class ClipboardMonitor
    {
        public delegate void OnClipboardChangeEventHandler(ClipboardFormat format, object data);
        public static event OnClipboardChangeEventHandler OnClipboardChange;

        public static void Start()
        {
            ClipboardWatcher.Start();
            ClipboardWatcher.OnClipboardChange += (ClipboardFormat format, object data) =>
            {
                if (OnClipboardChange != null)
                    OnClipboardChange(format, data);
            };
        }

        public static void Stop()
        {
            OnClipboardChange = null;
            ClipboardWatcher.Stop();
        }

        class ClipboardWatcher : Form
        {
            // static instance of this form
            private static ClipboardWatcher mInstance;

            // needed to dispose this form
            static IntPtr nextClipboardViewer;

            public delegate void OnClipboardChangeEventHandler(ClipboardFormat format, object data);
            public static event OnClipboardChangeEventHandler OnClipboardChange;

            // start listening
            public static void Start()
            {
                // we can only have one instance if this class
                if (mInstance != null)
                    return;

                var t = new Thread(new ParameterizedThreadStart(x => Application.Run(new ClipboardWatcher())));
                t.SetApartmentState(ApartmentState.STA); // give the [STAThread] attribute
                t.Start();
            }

            // stop listening (dispose form)
            public static void Stop()
            {
                mInstance.Invoke(new MethodInvoker(() =>
                {
                    ChangeClipboardChain(mInstance.Handle, nextClipboardViewer);
                }));
                mInstance.Invoke(new MethodInvoker(mInstance.Close));

                mInstance.Dispose();

                mInstance = null;
            }

            // on load: (hide this window)
            protected override void SetVisibleCore(bool value)
            {
                CreateHandle();

                mInstance = this;

                nextClipboardViewer = SetClipboardViewer(mInstance.Handle);

                base.SetVisibleCore(false);
            }

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

            [DllImport("User32.dll", CharSet = CharSet.Auto)]
            private static extern bool ChangeClipboardChain(IntPtr hWndRemove, IntPtr hWndNewNext);

            [DllImport("user32.dll", CharSet = CharSet.Auto)]
            private static extern int SendMessage(IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam);

            // defined in winuser.h
            const int WM_DRAWCLIPBOARD = 0x308;
            const int WM_CHANGECBCHAIN = 0x030D;

            protected override void WndProc(ref Message m)
            {
                switch (m.Msg)
                {
                    case WM_DRAWCLIPBOARD:
                        ClipChanged();
                        SendMessage(nextClipboardViewer, m.Msg, m.WParam, m.LParam);
                        break;

                    case WM_CHANGECBCHAIN:
                        if (m.WParam == nextClipboardViewer)
                            nextClipboardViewer = m.LParam;
                        else
                            SendMessage(nextClipboardViewer, m.Msg, m.WParam, m.LParam);
                        break;

                    default:
                        base.WndProc(ref m);
                        break;
                }
            }

            static readonly string[] formats = Enum.GetNames(typeof(ClipboardFormat));

            private void ClipChanged()
            {
                IDataObject iData = Clipboard.GetDataObject();

                ClipboardFormat? format = null;

                foreach (var f in formats)
                {
                    if (iData.GetDataPresent(f))
                    {
                        format = (ClipboardFormat)Enum.Parse(typeof(ClipboardFormat), f);
                        break;
                    }
                }

                object data = iData.GetData(format.ToString());

                if (data == null || format == null)
                    return;

                if (OnClipboardChange != null)
                    OnClipboardChange((ClipboardFormat)format, data);
            }
        }
    }

    public enum ClipboardFormat : byte
    {
        /// Specifies the standard ANSI text format. This static field is read-only.
        /// 
        /// 1
        Text,
        /// Specifies the standard Windows Unicode text format. This static field
        /// is read-only.
        /// 1
        UnicodeText,
        /// Specifies the Windows device-independent bitmap (DIB) format. This static
        /// field is read-only.
        /// 1
        Dib,
        /// Specifies a Windows bitmap format. This static field is read-only.
        /// 1
        Bitmap,
        /// Specifies the Windows enhanced metafile format. This static field is
        /// read-only.
        /// 1
        EnhancedMetafile,
        /// Specifies the Windows metafile format, which Windows Forms does not
        /// directly use. This static field is read-only.
        /// 1
        MetafilePict,
        /// Specifies the Windows symbolic link format, which Windows Forms does
        /// not directly use. This static field is read-only.
        /// 1
        SymbolicLink,
        /// Specifies the Windows Data Interchange Format (DIF), which Windows Forms
        /// does not directly use. This static field is read-only.
        /// 1
        Dif,
        /// Specifies the Tagged Image File Format (TIFF), which Windows Forms does
        /// not directly use. This static field is read-only.
        /// 1
        Tiff,
        /// Specifies the standard Windows original equipment manufacturer (OEM)
        /// text format. This static field is read-only.
        /// 1
        OemText,
        /// Specifies the Windows palette format. This static field is read-only.
        /// 
        /// 1
        Palette,
        /// Specifies the Windows pen data format, which consists of pen strokes
        /// for handwriting software, Windows Forms does not use this format. This static
        /// field is read-only.
        /// 1
        PenData,
        /// Specifies the Resource Interchange File Format (RIFF) audio format,
        /// which Windows Forms does not directly use. This static field is read-only.
        /// 1
        Riff,
        /// Specifies the wave audio format, which Windows Forms does not directly
        /// use. This static field is read-only.
        /// 1
        WaveAudio,
        /// Specifies the Windows file drop format, which Windows Forms does not
        /// directly use. This static field is read-only.
        /// 1
        FileDrop,
        /// Specifies the Windows culture format, which Windows Forms does not directly
        /// use. This static field is read-only.
        /// 1
        Locale,
        /// Specifies text consisting of HTML data. This static field is read-only.
        /// 
        /// 1
        Html,
        /// Specifies text consisting of Rich Text Format (RTF) data. This static
        /// field is read-only.
        /// 1
        Rtf,
        /// Specifies a comma-separated value (CSV) format, which is a common interchange
        /// format used by spreadsheets. This format is not used directly by Windows Forms.
        /// This static field is read-only.
        /// 1
        CommaSeparatedValue,
        /// Specifies the Windows Forms string class format, which Windows Forms
        /// uses to store string objects. This static field is read-only.
        /// 1
        StringFormat,
        /// Specifies a format that encapsulates any type of Windows Forms object.
        /// This static field is read-only.
        /// 1
        Serializable,
    }
}

    在您的其他项目中右键单击解决方案并添加 - >退出项目 - > ClipboardHelper.csproj

    在您的项目上转到并右键单击引用 - >添加引用 - >解决方案 - >选择ClipboardHelper.

    在使用ClipboardHelper的项目类型的类文件中.

    您现在可以键入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...
            }
    }
    



6> SamFisher83..:

我相信早期解决方案之一不会检查dispose方法的null:

using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Drawing;

namespace ClipboardAssist {

// Must inherit Control, not Component, in order to have Handle
[DefaultEvent("ClipboardChanged")]
public partial class ClipboardMonitor : Control 
{
    IntPtr nextClipboardViewer;

    public ClipboardMonitor()
    {
        this.BackColor = Color.Red;
        this.Visible = false;

        nextClipboardViewer = (IntPtr)SetClipboardViewer((int)this.Handle);
    }

    /// 
    /// Clipboard contents changed.
    /// 
    public event EventHandler ClipboardChanged;

    protected override void Dispose(bool disposing)
    {
        if(nextClipboardViewer != null)
            ChangeClipboardChain(this.Handle, nextClipboardViewer);
    }

    [DllImport("User32.dll")]
    protected static extern int SetClipboardViewer(int hWndNewViewer);

    [DllImport("User32.dll", CharSet = CharSet.Auto)]
    public static extern bool ChangeClipboardChain(IntPtr hWndRemove, IntPtr hWndNewNext);

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern int SendMessage(IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam);

    protected override void WndProc(ref System.Windows.Forms.Message m)
    {
        // defined in winuser.h
        const int WM_DRAWCLIPBOARD = 0x308;
        const int WM_CHANGECBCHAIN = 0x030D;

        switch (m.Msg)
        {
            case WM_DRAWCLIPBOARD:
                OnClipboardChanged();
                SendMessage(nextClipboardViewer, m.Msg, m.WParam, m.LParam);
                break;

            case WM_CHANGECBCHAIN:
                if (m.WParam == nextClipboardViewer)
                    nextClipboardViewer = m.LParam;
                else
                    SendMessage(nextClipboardViewer, m.Msg, m.WParam, m.LParam);
                break;

            default:
                base.WndProc(ref m);
                break;
        }
    }

    void OnClipboardChanged()
    {
        try
        {
            IDataObject iData = Clipboard.GetDataObject();
            if (ClipboardChanged != null)
            {
                ClipboardChanged(this, new ClipboardChangedEventArgs(iData));
            }

        }
        catch (Exception e)
        {
            // Swallow or pop-up, not sure
            // Trace.Write(e.ToString());
            MessageBox.Show(e.ToString());
        }
    }
}

    public class ClipboardChangedEventArgs : EventArgs
    {
        public readonly IDataObject DataObject;

        public ClipboardChangedEventArgs(IDataObject dataObject)
        {
            DataObject = dataObject;
        }
    }
}

推荐阅读
跟我搞对象吧
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有