none
Any event that triggers when any changes are made to clipboard?

    Question

  • The final outcome I want is actually detect if the clipboard contains any Image, if so then set a Image control to display that image. I am thinking I will need a way to monitor the clipboard if it contains an Image. How can I achieve this? I am thinking probably theres no such event so maybe i check the clipboard at regular intervals to see if it contains any image if so display it? Will it be very inefficient?

    I am wondering if the same mechanism that enables/disables my button when the clipboard contains an image can come into use here? I am using MVVM Foundation .

    GetImageFromClipboard
     
    =
     
    new
     
    RelayCommand
    (()
     
    =>
     
    ImageData
     
    =
     
    Clipboard
    .
    GetImage
    (),
     
    ()
     
    =>
     
    Clipboard
    .
    ContainsImage
    ()
     
    );
    

    because it enables/disables my Button bound to that correctly. Can i do something like when the CanExecute changes, If theres an image, display it?

    Friday, October 01, 2010 2:57 AM

Answers

  • I've made a class (ClipboardMonitor) in order to monitor the clipboard content in WPF.
    The class is about 160 lines of code (pretty much I think), but perhaps it helps.
    It uses the win32-api interop in order to hook up the monitor.
    This is my first "control" in WPF, so the code will not be the best at all, but it works as expected...

    using System;
    using System.Text;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Input;
    using System.Windows.Interop;
    using System.Runtime.InteropServices;
    using System.Windows.Media.Imaging;
    
    namespace WpfApplication1
    {
     public class ClipboardMonitor : Window
     {
      private HwndSource source = null;
      private IntPtr nextClipboardViewer;
      private IntPtr handle
      {
       get
       {
        return new WindowInteropHelper(this).Handle;
       }
      }
      public ClipboardMonitor()
      {
       //We have to "show" the window in order to obtain hwnd to process WndProc messages in WPF
       this.Top = -10;
       this.Left = -10;
       this.Width = 0;
       this.Height = 0;
       this.WindowStyle = WindowStyle.None;
       this.ShowInTaskbar = false;
       this.ShowActivated = false;
       this.Show();
       this.Hide();
      }
    
      #region "Dependency properties"
      public static readonly DependencyProperty ClipboardContainsImageProperty = 
       DependencyProperty.Register(
       "ClipboardContainsImage",
       typeof(bool), 
       typeof(Window),
       new FrameworkPropertyMetadata(false));
      public static readonly DependencyProperty ClipboardContainsTextProperty =
       DependencyProperty.Register(
       "ClipboardContainsText",
       typeof(bool),
       typeof(Window),
       new FrameworkPropertyMetadata(false));
      public static readonly DependencyProperty ClipboardTextProperty =
       DependencyProperty.Register(
       "ClipboardText",
       typeof(string),
       typeof(Window),
       new FrameworkPropertyMetadata(string.Empty));
      public static readonly DependencyProperty ClipboardImageProperty =
       DependencyProperty.Register(
       "ClipboardImage",
       typeof(BitmapSource),
       typeof(Window),
       new FrameworkPropertyMetadata(null));
      public bool ClipboardContainsImage
      {
       get { return (bool)GetValue(ClipboardMonitor.ClipboardContainsImageProperty); }
       set { SetValue(ClipboardMonitor.ClipboardContainsImageProperty, value); }
      }
      public bool ClipboardContainsText
      {
       get { return (bool)GetValue(ClipboardMonitor.ClipboardContainsTextProperty); }
       set { SetValue(ClipboardMonitor.ClipboardContainsTextProperty, value); }
      }
      public string ClipboardText
      {
       get { return (string)GetValue(ClipboardMonitor.ClipboardTextProperty); }
       set { SetValue(ClipboardMonitor.ClipboardTextProperty, value); }
      }
      public BitmapSource ClipboardImage
      {
       get { return (BitmapSource)GetValue(ClipboardMonitor.ClipboardImageProperty); }
       set { SetValue(ClipboardMonitor.ClipboardImageProperty, value); }
      }
      #endregion
    
      #region "Routed Events"
      public static readonly RoutedEvent ClipboardDataEvent = EventManager.RegisterRoutedEvent("ClipboardData", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(Window));
      public event RoutedEventHandler ClipboardData
      {
       add { AddHandler(ClipboardMonitor.ClipboardDataEvent, value); }
       remove { RemoveHandler(ClipboardMonitor.ClipboardDataEvent, value); }
      }
      protected virtual void OnRaiseClipboardData(ClipboardDataEventArgs e)
      {
       RaiseEvent(e);
      }
      #endregion
      
      #region "Win32 API"
      private const int WM_DRAWCLIPBOARD = 0x308;
      private const int WM_CHANGECBCHAIN = 0x030D;
      [DllImport("User32.dll")]
      private static extern int SetClipboardViewer(int 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);
      #endregion
    
      #region "overrides"
      protected override void OnSourceInitialized(EventArgs e)
      {
       base.OnSourceInitialized(e);
       nextClipboardViewer = (IntPtr)SetClipboardViewer((int)this.handle);
       source = PresentationSource.FromVisual(this) as HwndSource;
       source.AddHook(WndProc);
      }
      protected override void OnClosed(EventArgs e)
      {
       base.OnClosed(e);
       ChangeClipboardChain(this.handle, nextClipboardViewer);
       if (null != source)
        source.RemoveHook(WndProc);
      }
      #endregion
    
      #region "Clipboard data"
      private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
      {
       switch (msg)
       {
        case WM_DRAWCLIPBOARD:
         clipboardData();
         SendMessage(nextClipboardViewer, msg, wParam, lParam);
         break;
        case WM_CHANGECBCHAIN:
         if (wParam == nextClipboardViewer)
          nextClipboardViewer = lParam;
         else
          SendMessage(nextClipboardViewer, msg, wParam, lParam);
         break;
       }
       return IntPtr.Zero;
      }
      private void clipboardData()
      {
       IDataObject iData = Clipboard.GetDataObject();
       this.ClipboardContainsImage = iData.GetDataPresent(DataFormats.Bitmap);
       this.ClipboardContainsText = iData.GetDataPresent(DataFormats.Text);
       this.ClipboardImage = this.ClipboardContainsImage ? iData.GetData(DataFormats.Bitmap) as BitmapSource : null;
       this.ClipboardText = this.ClipboardContainsText ? iData.GetData(DataFormats.Text) as string : string.Empty;
       OnRaiseClipboardData(new ClipboardDataEventArgs(ClipboardMonitor.ClipboardDataEvent, iData));
      }
      #endregion
     }
    
     public class ClipboardDataEventArgs : RoutedEventArgs
     {
      public IDataObject Data { get; set; }
      public ClipboardDataEventArgs(RoutedEvent routedEvent, IDataObject data)
       : base(routedEvent)
      {
       this.Data = data;
      }
     }
    }
    
    

    Please note that the class inherits from Window. This is because we need to override the WndProc method in order to process the clipboard events.

    You can bind it to any control in .xaml thanks to the implementation of some Dependency properties (ClipboardContainsImage, ClipboardImage, ClipboardContainsText and ClipboardText).

    Here is an example to use the ClipboardMonitor class:

    <Window x:Class="WpfApplication1.Window1"
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     xmlns:local="clr-namespace:WpfApplication1"
     Title="Window1" Height="300" Width="300" >
     <Window.Resources>
      <local:ClipboardWatcher x:Key="ClipWatch" />
     </Window.Resources>
     <Grid>
      <Grid.RowDefinitions>
       <RowDefinition Height="50" />
       <RowDefinition Height="*" />
      </Grid.RowDefinitions>
      <Grid.ColumnDefinitions>
       <ColumnDefinition Width="78" />
       <ColumnDefinition Width="78" />
       <ColumnDefinition Width="*" />
      </Grid.ColumnDefinitions>
      <TextBlock Grid.Row="0" Grid.Column="0" Text="Text:" />
      <TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Source={StaticResource ClipWatch}, Path=ClipboardContainsText}" />
      <TextBlock Grid.Row="0" Grid.Column="2" Text="{Binding Source={StaticResource ClipWatch}, Path=ClipboardText}" />
      <TextBlock Grid.Row="1" Grid.Column="0" Text ="Image:" />
      <TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Source={StaticResource ClipWatch}, Path=ClipboardContainsImage}" />
      <Image Grid.Row="1" Grid.Column="2" Name="image" Stretch="Fill" Source="{Binding Source={StaticResource ClipWatch}, Path=ClipboardImage}" />
     </Grid>
    </Window>
    
    

    This extremely simple window will monitor the clipboard and show the "text" or "image" that is copied the clipboard.
    If you load the window and press PrintScreen button (screenshot to the clipboard), it will show something like:

    ClipMon

    And if you copy some text to the clipboard:

    Photobucket

     Maybe the code deserves more comments... Let me know if you want more description.

    Also note that there is a little problem; the monitor window will not let the application finish until it's closed (but is hidden!), so you must close the window to avoid this. With the following lines in the code-behind, the Closed event of the main window, also closes the Monitor window:

     public partial class Window1 : Window
     {
      public Window1()
      {
       InitializeComponent();
       this.Closed += new EventHandler((o, e) => (this.FindResource("ClipWatch") as ClipboardMonitor).Close());
      }
     }
    
    

    Hope it helps... And sorry for my english.

    Federico Colombo

    Saturday, October 02, 2010 6:43 AM

All replies

  • Hello Jiew Meng,

    I think you are already one the right correct, we can have one custom RoutedCommand and make your "Dispaly" Button.Command is set the our custom command.

    private void CanExecuteCustomCommand(object sender,   CanExecuteRoutedEventArgs e)
    {
        // target is the check result of whether clipboard has one image source.

        if(target)
        {
            e.CanExecute = true;
        }
        else
        {
            e.CanExecute = false;
        }
    }

     

    Hope this helps.


    Keep imporving.
    Yiling Lai. MVP (Visual C++ and Client App Dev)
    Friday, October 01, 2010 2:55 PM
  • I've made a class (ClipboardMonitor) in order to monitor the clipboard content in WPF.
    The class is about 160 lines of code (pretty much I think), but perhaps it helps.
    It uses the win32-api interop in order to hook up the monitor.
    This is my first "control" in WPF, so the code will not be the best at all, but it works as expected...

    using System;
    using System.Text;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Input;
    using System.Windows.Interop;
    using System.Runtime.InteropServices;
    using System.Windows.Media.Imaging;
    
    namespace WpfApplication1
    {
     public class ClipboardMonitor : Window
     {
      private HwndSource source = null;
      private IntPtr nextClipboardViewer;
      private IntPtr handle
      {
       get
       {
        return new WindowInteropHelper(this).Handle;
       }
      }
      public ClipboardMonitor()
      {
       //We have to "show" the window in order to obtain hwnd to process WndProc messages in WPF
       this.Top = -10;
       this.Left = -10;
       this.Width = 0;
       this.Height = 0;
       this.WindowStyle = WindowStyle.None;
       this.ShowInTaskbar = false;
       this.ShowActivated = false;
       this.Show();
       this.Hide();
      }
    
      #region "Dependency properties"
      public static readonly DependencyProperty ClipboardContainsImageProperty = 
       DependencyProperty.Register(
       "ClipboardContainsImage",
       typeof(bool), 
       typeof(Window),
       new FrameworkPropertyMetadata(false));
      public static readonly DependencyProperty ClipboardContainsTextProperty =
       DependencyProperty.Register(
       "ClipboardContainsText",
       typeof(bool),
       typeof(Window),
       new FrameworkPropertyMetadata(false));
      public static readonly DependencyProperty ClipboardTextProperty =
       DependencyProperty.Register(
       "ClipboardText",
       typeof(string),
       typeof(Window),
       new FrameworkPropertyMetadata(string.Empty));
      public static readonly DependencyProperty ClipboardImageProperty =
       DependencyProperty.Register(
       "ClipboardImage",
       typeof(BitmapSource),
       typeof(Window),
       new FrameworkPropertyMetadata(null));
      public bool ClipboardContainsImage
      {
       get { return (bool)GetValue(ClipboardMonitor.ClipboardContainsImageProperty); }
       set { SetValue(ClipboardMonitor.ClipboardContainsImageProperty, value); }
      }
      public bool ClipboardContainsText
      {
       get { return (bool)GetValue(ClipboardMonitor.ClipboardContainsTextProperty); }
       set { SetValue(ClipboardMonitor.ClipboardContainsTextProperty, value); }
      }
      public string ClipboardText
      {
       get { return (string)GetValue(ClipboardMonitor.ClipboardTextProperty); }
       set { SetValue(ClipboardMonitor.ClipboardTextProperty, value); }
      }
      public BitmapSource ClipboardImage
      {
       get { return (BitmapSource)GetValue(ClipboardMonitor.ClipboardImageProperty); }
       set { SetValue(ClipboardMonitor.ClipboardImageProperty, value); }
      }
      #endregion
    
      #region "Routed Events"
      public static readonly RoutedEvent ClipboardDataEvent = EventManager.RegisterRoutedEvent("ClipboardData", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(Window));
      public event RoutedEventHandler ClipboardData
      {
       add { AddHandler(ClipboardMonitor.ClipboardDataEvent, value); }
       remove { RemoveHandler(ClipboardMonitor.ClipboardDataEvent, value); }
      }
      protected virtual void OnRaiseClipboardData(ClipboardDataEventArgs e)
      {
       RaiseEvent(e);
      }
      #endregion
      
      #region "Win32 API"
      private const int WM_DRAWCLIPBOARD = 0x308;
      private const int WM_CHANGECBCHAIN = 0x030D;
      [DllImport("User32.dll")]
      private static extern int SetClipboardViewer(int 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);
      #endregion
    
      #region "overrides"
      protected override void OnSourceInitialized(EventArgs e)
      {
       base.OnSourceInitialized(e);
       nextClipboardViewer = (IntPtr)SetClipboardViewer((int)this.handle);
       source = PresentationSource.FromVisual(this) as HwndSource;
       source.AddHook(WndProc);
      }
      protected override void OnClosed(EventArgs e)
      {
       base.OnClosed(e);
       ChangeClipboardChain(this.handle, nextClipboardViewer);
       if (null != source)
        source.RemoveHook(WndProc);
      }
      #endregion
    
      #region "Clipboard data"
      private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
      {
       switch (msg)
       {
        case WM_DRAWCLIPBOARD:
         clipboardData();
         SendMessage(nextClipboardViewer, msg, wParam, lParam);
         break;
        case WM_CHANGECBCHAIN:
         if (wParam == nextClipboardViewer)
          nextClipboardViewer = lParam;
         else
          SendMessage(nextClipboardViewer, msg, wParam, lParam);
         break;
       }
       return IntPtr.Zero;
      }
      private void clipboardData()
      {
       IDataObject iData = Clipboard.GetDataObject();
       this.ClipboardContainsImage = iData.GetDataPresent(DataFormats.Bitmap);
       this.ClipboardContainsText = iData.GetDataPresent(DataFormats.Text);
       this.ClipboardImage = this.ClipboardContainsImage ? iData.GetData(DataFormats.Bitmap) as BitmapSource : null;
       this.ClipboardText = this.ClipboardContainsText ? iData.GetData(DataFormats.Text) as string : string.Empty;
       OnRaiseClipboardData(new ClipboardDataEventArgs(ClipboardMonitor.ClipboardDataEvent, iData));
      }
      #endregion
     }
    
     public class ClipboardDataEventArgs : RoutedEventArgs
     {
      public IDataObject Data { get; set; }
      public ClipboardDataEventArgs(RoutedEvent routedEvent, IDataObject data)
       : base(routedEvent)
      {
       this.Data = data;
      }
     }
    }
    
    

    Please note that the class inherits from Window. This is because we need to override the WndProc method in order to process the clipboard events.

    You can bind it to any control in .xaml thanks to the implementation of some Dependency properties (ClipboardContainsImage, ClipboardImage, ClipboardContainsText and ClipboardText).

    Here is an example to use the ClipboardMonitor class:

    <Window x:Class="WpfApplication1.Window1"
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     xmlns:local="clr-namespace:WpfApplication1"
     Title="Window1" Height="300" Width="300" >
     <Window.Resources>
      <local:ClipboardWatcher x:Key="ClipWatch" />
     </Window.Resources>
     <Grid>
      <Grid.RowDefinitions>
       <RowDefinition Height="50" />
       <RowDefinition Height="*" />
      </Grid.RowDefinitions>
      <Grid.ColumnDefinitions>
       <ColumnDefinition Width="78" />
       <ColumnDefinition Width="78" />
       <ColumnDefinition Width="*" />
      </Grid.ColumnDefinitions>
      <TextBlock Grid.Row="0" Grid.Column="0" Text="Text:" />
      <TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Source={StaticResource ClipWatch}, Path=ClipboardContainsText}" />
      <TextBlock Grid.Row="0" Grid.Column="2" Text="{Binding Source={StaticResource ClipWatch}, Path=ClipboardText}" />
      <TextBlock Grid.Row="1" Grid.Column="0" Text ="Image:" />
      <TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Source={StaticResource ClipWatch}, Path=ClipboardContainsImage}" />
      <Image Grid.Row="1" Grid.Column="2" Name="image" Stretch="Fill" Source="{Binding Source={StaticResource ClipWatch}, Path=ClipboardImage}" />
     </Grid>
    </Window>
    
    

    This extremely simple window will monitor the clipboard and show the "text" or "image" that is copied the clipboard.
    If you load the window and press PrintScreen button (screenshot to the clipboard), it will show something like:

    ClipMon

    And if you copy some text to the clipboard:

    Photobucket

     Maybe the code deserves more comments... Let me know if you want more description.

    Also note that there is a little problem; the monitor window will not let the application finish until it's closed (but is hidden!), so you must close the window to avoid this. With the following lines in the code-behind, the Closed event of the main window, also closes the Monitor window:

     public partial class Window1 : Window
     {
      public Window1()
      {
       InitializeComponent();
       this.Closed += new EventHandler((o, e) => (this.FindResource("ClipWatch") as ClipboardMonitor).Close());
      }
     }
    
    

    Hope it helps... And sorry for my english.

    Federico Colombo

    Saturday, October 02, 2010 6:43 AM