Zdarzenie schowka C #


90

Czy istnieje zmieniony lub zaktualizowany schowek, do którego mogę uzyskać dostęp za pośrednictwem C #?


co z klasą Control? Gdzie to jest?

Jest częścią WinForms.
Contango,

Odpowiedzi:


73

Myślę, że będziesz musiał użyć p / invoke:

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

Zobacz ten artykuł na temat konfigurowania monitora schowka w języku C #

Zasadniczo rejestrujesz swoją aplikację jako przeglądarkę schowka za pomocą

_ClipboardViewerNext = SetClipboardViewer(this.Handle);

a następnie otrzymasz WM_DRAWCLIPBOARDwiadomość, którą możesz obsłużyć, zastępując WndProc:

protected override void WndProc(ref Message m)
{
    switch ((Win32.Msgs)m.Msg)
    {
        case Win32.Msgs.WM_DRAWCLIPBOARD:
        // Handle clipboard changed
        break;
        // ... 
   }
}

(Jest więcej do zrobienia; przekazywanie rzeczy wzdłuż łańcucha schowka i wyrejestrowywanie widoku, ale możesz to uzyskać z artykułu )


Działa tylko na pierwszym otwartym formularzu ... powiedzmy, że mam MyForm1 i myForm2, więc otwieram myForm1, a następnie MyForm2, zdarzenie ClipboardChanged zostanie zgłoszone tylko w MyForm1. To znaczy w aplikacji MDI ...
serhio

Link nie działa. Jakaś kopia zapasowa, o której jesteś świadomy? Mimo to +1.
Patrick Hofman,

1
Dla leniwych: ustaw zegar, który tyka co 1 ms. Następnie przy każdym ticku sprawdzaj, czy zawartość twojego schowka uległa zmianie. Te haki podnoszą alarmy o wirusach i trojanach na moim komputerze.
C4d,

1
Przekazuje wszystkie pliki MSG systemu Windows do formularza i utrudnia debugowanie kodu

Podobnie, SharpClipboard jako biblioteka może przynieść więcej korzyści, ponieważ zawiera te same funkcje w jednej doskonałej bibliotece komponentów. Możesz wtedy uzyskać dostęp do jego ClipboardChangedzdarzenia i wykryć różne formaty danych podczas ich wycinania / kopiowania.
Willy Kimura,

78

Aby uzyskać kompletność, oto formant, którego używam w kodzie produkcyjnym. Po prostu przeciągnij z projektanta i kliknij dwukrotnie, aby utworzyć procedurę obsługi zdarzeń.

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;
    }
}
}

2
Dobra robota! Twój kod wywołujący wydarzenie nie jest jednak bezpieczny dla wątków. Należy utworzyć kopię lokalną lub zainicjować zdarzenie z pustym delegatem. Zapomniałeś również słowa kluczowego „wydarzenie” w definicji Schowka Zmieniono :)
Ohad Schneider

1
@ohadsc Dziękuję za poprawki. O ile wiem, WndProc jest wywoływany w wątku interfejsu użytkownika. Ponieważ klasa pochodzi od Control, klienci powinni wywoływać ją również w wątku interfejsu użytkownika.
dbkk

Działa tylko na pierwszym otwartym formularzu ... powiedz, czy mam MyForm1 i myForm2, więc otwieram myForm1, potem MyForm2, zdarzenie ClipboardChanged zostanie wywołane tylko w MyForm1 ... to znaczy w aplikacji MDI ...
serhio

W jakiś sposób wywołanie SetClipboardViewer ustawia kod błędu Win32 1400: „Nieprawidłowy uchwyt okna.”. Ale nadal działa. Wydaje mi się to trochę dziwne.
metacircle

1
SharpClipboard jako biblioteka może przynosić więcej korzyści, ponieważ zawiera te same funkcje w jednej doskonałej bibliotece komponentów. Możesz wtedy uzyskać dostęp do jego ClipboardChangedzdarzenia i wykryć różne formaty danych podczas ich wycinania / kopiowania.
Willy Kimura

26

Miałem to wyzwanie w WPF i ostatecznie skorzystałem z podejścia opisanego poniżej. W przypadku formularzy systemu Windows istnieją doskonałe przykłady w innych miejscach w tej odpowiedzi, takie jak kontrolka ClipboardHelper.

W przypadku WPF nie możemy przesłonić WndProc, więc musimy go jawnie podłączyć za pomocą wywołania HwndSource AddHook przy użyciu źródła z okna. Odbiornik schowka nadal używa natywnego wywołania międzyoperacyjnego AddClipboardFormatListener.

Metody natywne:

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);
}

Klasa menedżera schowka:

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;
    }
}

Jest to używane w oknie WPF przez dodanie zdarzenia w OnSourceInitialized lub nowszym, takim jak zdarzenie Window.Loaded lub podczas operacji. (kiedy mamy wystarczająco dużo informacji, aby użyć natywnych hooków):

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());
        }
    }
}

Używam tego podejścia w projekcie analizatora przedmiotów Path of Exile, ponieważ gra ujawnia informacje o przedmiocie za pośrednictwem schowka po naciśnięciu Ctrl-C.

https://github.com/ColinDabritz/PoeItemAnalyzer

Mam nadzieję, że to pomoże komuś z obsługą zmiany schowka WPF!


1
Jeśli ktoś nie wie, co to znaczy ClipboardChanged?.Invokezobaczyć Korzystanie z nowego zerowego operatora warunkowego w C # 6 , sekcja Inne scenariusze
marbel82

11

Ok, więc to jest stary post, ale znaleźliśmy rozwiązanie, które wydaje się bardzo proste w porównaniu z obecnym zestawem odpowiedzi. Używamy WPF i chcieliśmy mieć własne niestandardowe polecenia (w menu kontekstowym) włączające i wyłączające, jeśli schowek zawiera tekst. Istnieje już ApplicationCommands. Wytnij, Kopiuj i Wklej, a te polecenia poprawnie reagują na zmianę schowka. Więc właśnie dodaliśmy następujący EventHandler.

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

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

W ten sposób kontrolujemy CanExecute w naszym własnym poleceniu. Działa na to, czego potrzebowaliśmy i może pomoże innym.


Świetne rozwiązanie, bo to takie proste ... Dzięki!
okieh

1
To fantastyczne rozwiązanie konkretnego problemu związanego z włączaniem lub wyłączaniem polecenia wklejania. Niestety nie obejmuje on konkretnego scenariusza „zmieniono tekst” i nie będzie uruchamiany na przykład podczas kopiowania tekstu w wielu różnych wierszach.
Colin Dabritz

11

Jest na to wiele sposobów, ale ten jest moim ulubionym i działa dla mnie. Stworzyłem bibliotekę klas, aby inni mogli dodać projekt i dołączyć bibliotekę DLL, a następnie po prostu ją wywołać i używać w dowolnym miejscu w swoich aplikacjach.

Ta odpowiedź została udzielona za pomocą tego .

  1. Utwórz projekt biblioteki klas i nadaj mu nazwę ClipboardHelper.
  2. Zastąp nazwę Class1 nazwą ClipboardMonitor.
  3. Dodaj do niego poniższy kod.
  4. Dodaj odniesienie do System.Windows.Forms.

Więcej kroków pod kodem.

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,
    }
}
  1. W innych projektach kliknij prawym przyciskiem myszy rozwiązanie i Dodaj -> Wyjście z projektu -> ClipboardHelper.csproj
  2. W projekcie przejdź do i kliknij prawym przyciskiem myszy References -> Add Reference -> Solution -> Select ClipboardHelper.
  3. W pliku klasy typu projektu przy użyciu ClipboardHelper.
  4. Możesz teraz wpisać ClipboardMonitor.Start lub .Stop lub .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

Uważam, że jedno z wcześniejszych rozwiązań nie sprawdza wartości null w metodzie dispose:

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

namespace ClipboardAssist {

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

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

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

    /// <summary>
    /// Clipboard contents changed.
    /// </summary>
    public event EventHandler<ClipboardChangedEventArgs> ClipboardChanged;

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

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

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

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

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

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

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

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

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

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

    public class ClipboardChangedEventArgs : EventArgs
    {
        public readonly IDataObject DataObject;

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

Nigdy nie jest null, ponieważ konstruktor go ustawia. Jedyną rzeczą, którą zrobiłbym inaczej, jest wywołanie base.Dispose()metody dispose.
jedmao

Tak czy inaczej. Do celów weryfikacji, takich jak te, które wymieniłeś, powinieneś użyć IntPtr.Zero dla NULL (zauważ, że nie jest to równoważne z C # null) stackoverflow.com/questions/1456861/ ...
walter

1
ChangeClipboardChain jest wykonywany zawsze przy wyjściu we wszystkich próbkach MSDN
Walter

Celem jest usunięcie się z łańcucha przeglądarek schowka
walter

6

SharpClipboard jako biblioteka może przynosić więcej korzyści, ponieważ zawiera te same funkcje w jednej doskonałej bibliotece komponentów. Możesz wtedy uzyskać dostęp do jego ClipboardChangedzdarzenia i wykryć różne formaty danych podczas ich wycinania / kopiowania.

Możesz wybrać różne formaty danych, które chcesz monitorować:

var clipboard = new SharpClipboard();

clipboard.ObservableFormats.Texts = true;
clipboard.ObservableFormats.Files = true;
clipboard.ObservableFormats.Images = true;
clipboard.ObservableFormats.Others = true;

Oto przykład wykorzystujący jego ClipboardChangedzdarzenie:

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...
    }
}

Możesz również dowiedzieć się, w jakiej aplikacji wystąpiło zdarzenie wycinania / kopiowania wraz z jej szczegółami:

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);
}

Istnieją również inne zdarzenia, takie jak MonitorChangedzdarzenie, które nasłuchuje, gdy monitorowanie schowka jest wyłączone, co oznacza, że ​​możesz włączyć lub wyłączyć monitorowanie schowka w czasie wykonywania.

Oprócz tego wszystkiego, ponieważ jest to komponent, możesz go używać w widoku projektanta , przeciągając i upuszczając go do formularza systemu Windows, dzięki czemu każdy może bardzo łatwo dostosować jego opcje i pracować z wbudowanymi zdarzeniami.

SharpClipboard wydaje się być najlepszą opcją dla scenariuszy monitorowania schowka w .NET.


0
        [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;
            }
        }
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.