none
C#でのクリップボードの遅延レンダリング方法 RRS feed

  • 質問

  • クリップボードにデータを設定するためにClipboard.SetDataObjectを使っていますが、

    その際に、「コピー」の段階ではなく「貼り付け」されるまでクリップボードへのデータの設定を遅らせる「遅延レンダリング」をしたいと考えています。

    https://docs.microsoft.com/en-us/windows/win32/dataxchg/clipboard-operations 「Delayed Rendering」で記載がありましたが、C#ではどのように実装してよいかわからなく困っています。

    ご存知の方がいましたらお教えいただけますと幸いです。


    • 編集済み yj0529 2019年9月20日 1:50
    2019年9月20日 1:49

回答

  • こんな

    namespace WpfApp1
    {
        using System;
        using System.Windows;
        using System.Windows.Controls;
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
                var button = new Button() { Content = "Test" };
                button.Click += Button_Click;
    
                this.Content = button;
            }
    
            private void Button_Click(object sender, RoutedEventArgs e)
            {
                DataObject d = new DataObject();
                d.SetData(DataFormats.Text, "ダミー");
    
                this.delayed = new Delayed(d);
                delayed.RenderFormat += (a, b) =>
                {
                    d.SetData(DataFormats.Text, "遅延で設定:" + DateTime.Now.ToLongTimeString());
                };
                delayed.StartListen();
            }
    
            Delayed delayed;
    
            class Delayed : System.Windows.Forms.NativeWindow
            {
                public Delayed(DataObject d)
                {                
                    this.DataObject = d;
                }
    
                public void StartListen()
                {
                    Clipboard.SetDataObject(this.DataObject, false); //第2引数falseで遅延できる
                    IntPtr hwnd = GetClipboardOwner();
                    if (hwnd != IntPtr.Zero)
                    {
                        AssignHandle(hwnd);
                    }
                }
    
                const int WM_DESTROYCLIPBOARD = 0x307;
                const int WM_RENDERFORMAT = 0x305;
    
                [System.Runtime.InteropServices.DllImport("user32.dll")]
                private static extern IntPtr GetClipboardOwner();
    
                public DataObject DataObject { get; private set; }
                public event EventHandler RenderFormat;
    
                protected override void WndProc(ref System.Windows.Forms.Message m)
                {
                    switch (m.Msg)
                    {
                        case WM_RENDERFORMAT:
                            this.RenderFormat?.Invoke(this, EventArgs.Empty);
                            break;
                        case WM_DESTROYCLIPBOARD:
                            this.ReleaseHandle();
                            DataObject = null;
                            break;
                    }
      
                    base.WndProc(ref m);
                }
            }
        }
    }

    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 回答としてマーク yj0529 2019年10月1日 6:00
    2019年9月20日 3:37
  • 参照されているドキュメントはSetClipboardDataが挙げられていますが、.NETのClipboard.SetDataObjectはこれを使っておらず、OLEのOleSetClipboardを使っています。このためドキュメントにあるDelayed Renderingは適用されません。

    System.Windows.IDataObjectで希望を満たせない場合、より低レベルなSystem.Runtime.InteropServices.ComTypes.IDataObjectも用意されています。こちらは直接Explorerとやり取りするIDataObject interfaceそのものとなっています。この方法ではドラッグ中やドロップ時にこのインターフェースを介してやり取りするため、制御できます(たぶん)。ただし、FORMATETCやSTGMEDIUMなどの深い知識が必要になっています。

    gekkaさんが提示されている方法でDelayed Renderingを実現するためには、.NETのClipboard.SetDataObjectを使わず、SetClipboardDataを直接呼び出せばうまく行くかもしれません。

    2019年9月25日 9:09

すべての返信

  • Clipboard.SetDataObject

    システム クリップボードに配置するデータ オブジェクト (IDataObject を実装するオブジェクト)。

    とあるように、IDataObjectを実装したオブジェクトが要求されているだけですので、ご自身でIDataObjectインターフェースを実装したオブジェクトを用意するだけです。

    この場合、「貼り付け」のタイミングでIDataObject.GetDataが呼ばれますので、そこ時点で遅延レンダリング?を行うことで実現できます。

    • 編集済み 佐祐理 2019年9月20日 2:04
    2019年9月20日 2:01
  • こんな

    namespace WpfApp1
    {
        using System;
        using System.Windows;
        using System.Windows.Controls;
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
                var button = new Button() { Content = "Test" };
                button.Click += Button_Click;
    
                this.Content = button;
            }
    
            private void Button_Click(object sender, RoutedEventArgs e)
            {
                DataObject d = new DataObject();
                d.SetData(DataFormats.Text, "ダミー");
    
                this.delayed = new Delayed(d);
                delayed.RenderFormat += (a, b) =>
                {
                    d.SetData(DataFormats.Text, "遅延で設定:" + DateTime.Now.ToLongTimeString());
                };
                delayed.StartListen();
            }
    
            Delayed delayed;
    
            class Delayed : System.Windows.Forms.NativeWindow
            {
                public Delayed(DataObject d)
                {                
                    this.DataObject = d;
                }
    
                public void StartListen()
                {
                    Clipboard.SetDataObject(this.DataObject, false); //第2引数falseで遅延できる
                    IntPtr hwnd = GetClipboardOwner();
                    if (hwnd != IntPtr.Zero)
                    {
                        AssignHandle(hwnd);
                    }
                }
    
                const int WM_DESTROYCLIPBOARD = 0x307;
                const int WM_RENDERFORMAT = 0x305;
    
                [System.Runtime.InteropServices.DllImport("user32.dll")]
                private static extern IntPtr GetClipboardOwner();
    
                public DataObject DataObject { get; private set; }
                public event EventHandler RenderFormat;
    
                protected override void WndProc(ref System.Windows.Forms.Message m)
                {
                    switch (m.Msg)
                    {
                        case WM_RENDERFORMAT:
                            this.RenderFormat?.Invoke(this, EventArgs.Empty);
                            break;
                        case WM_DESTROYCLIPBOARD:
                            this.ReleaseHandle();
                            DataObject = null;
                            break;
                    }
      
                    base.WndProc(ref m);
                }
            }
        }
    }

    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 回答としてマーク yj0529 2019年10月1日 6:00
    2019年9月20日 3:37
  • 佐祐理様

    ご返信ありがとうございます。

    ちなみにIDataObject.GetDataが呼ばれるタイミングについてですが、試してみたところWindowsエクスプローラー上で右クリックした段階だったり(「貼り付け」の有効/無効に使っている?)、条件が明確ではないのですが、SetDataした瞬間に呼ばれていたりします。
    実際の「貼り付け」が実行されたことは区別できるものなのかご存知ないでしょうか?

    2019年9月25日 5:10
  • gekka様

    ご返信ありがとうございます。

    試してみたところ、delayed.StartListen直後に遅延設定が呼ばれてしまいました。(貼り付けをする前)
    ただ、Delayed.WndProc(WM_RENDERFORMAT)自体もそのタイミングで呼ばれています。

    実際の「貼り付け」処理が実行されたタイミングというのがわかる方法について何かご存知だったりしませんでしょうか?

    2019年9月25日 5:22
  • 参照されているドキュメントはSetClipboardDataが挙げられていますが、.NETのClipboard.SetDataObjectはこれを使っておらず、OLEのOleSetClipboardを使っています。このためドキュメントにあるDelayed Renderingは適用されません。

    System.Windows.IDataObjectで希望を満たせない場合、より低レベルなSystem.Runtime.InteropServices.ComTypes.IDataObjectも用意されています。こちらは直接Explorerとやり取りするIDataObject interfaceそのものとなっています。この方法ではドラッグ中やドロップ時にこのインターフェースを介してやり取りするため、制御できます(たぶん)。ただし、FORMATETCやSTGMEDIUMなどの深い知識が必要になっています。

    gekkaさんが提示されている方法でDelayed Renderingを実現するためには、.NETのClipboard.SetDataObjectを使わず、SetClipboardDataを直接呼び出せばうまく行くかもしれません。

    2019年9月25日 9:09
  • yj0529さん、こんにちは。フォーラムオペレーターのHarukaです。
    MSDNフォーラムにご投稿くださいましてありがとうございます。

    ご質問いただいた件ですが、その後いかがでしょうか。
    佐祐理さんから寄せられた投稿はお役に立ちましたか。

    参考になった投稿には [回答としてマーク] をお願い致します。

    設定いただくことで、
    他のユーザーもお役に立つ回答を見つけやすくなります。

    お手数ですが、ご協力の程どうかよろしくお願いいたします。


    MSDN/ TechNet Community Support Haruka

    ~参考になった投稿には「回答としてマーク」をご設定ください。なかった場合は「回答としてマークされていない」も設定できます。同じ問題で後から参照した方が、情報を見つけやすくなりますので、
    ご協力くださいますようお願いいたします。また、MSDNサポートに賛辞や苦情がある場合は、MSDNFSF@microsoft.comまでお気軽にお問い合わせください。~

    2019年9月27日 8:16
    モデレータ
  • 佐祐理 様

    参考になります、ありがとうございます。試してみようと思います。

    2019年10月1日 5:59