[C#]ComboBoxのカスタムコントロールで貼付けWM_PASTEを捕捉する方法

.NETや.NetFrameworkのWindowsFormの話になります。

ComboBoxのカスタムコントロールでWndProcメソッドをオーバーライドさせても、WM_PASTEは届きません。ComboBoxの文字入力部分は、実際は内部にあるEditコントロールの子ウィンドウによって実装されているため、親のComboBoxのWndProcにはWM_PASTEが届かないということの様です。

実際に古くからこのことは話題になっていました。VBですが、次のサイト↓のQAが良く取り上げられています。

「コンボボックスの右クリックの貼り付けイベント取得について」(1) Insider.NET − @IT
「コンボボックスの右クリックの貼り付けイベント取得について」に関する質問と回答の一覧です。(1) Insider.NET − @IT

解決策としては、Edit部分のウィンドウハンドルに対してサブクラス化してWM_PASTEを捕捉するということになります。それでは実際にC#ではどのようにプログラムしたら良いかやってみたいと思います。

①新しいプロジェクトでWindowsフォームをを選び、Form1だけできた状態にします。

②ソリューションエクスプローラーのツリーのプロジェクトを右クリックし「追加」→「新しい項目」で「カスタムコントロール」を追加します。名前は「CustomComboBox.cs」にすることにします。

③今追加したカスタムコントロールをコードビューにして、次のコードの様にします。

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

namespace CustomComboboxTest
{
    public partial class CustomComboBox : ComboBox
    {
        public CustomComboBox()
        {
            InitializeComponent();
        }

        private EditBoxNativeWindow _editWnd;

        protected override void OnHandleCreated(EventArgs e)
        {
            base.OnHandleCreated(e);
            AttachEditControl();
        }

        private void AttachEditControl()
        {
            COMBOBOXINFO info = new COMBOBOXINFO();
            info.cbSize = Marshal.SizeOf(info);
            if (GetComboBoxInfo(this.Handle, ref info))
            {
                _editWnd = new EditBoxNativeWindow(info.hwndEdit, this);
            }
        }

        // COMBOBOXINFO構造体とP/Invoke
        [StructLayout(LayoutKind.Sequential)]
        private struct COMBOBOXINFO
        {
            public int cbSize;
            public RECT rcItem;
            public RECT rcButton;
            public int stateButton;
            public IntPtr hwndCombo;
            public IntPtr hwndEdit;
            public IntPtr hwndList;
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct RECT
        {
            public int Left, Top, Right, Bottom;
        }

        [DllImport("user32.dll")]
        private static extern bool GetComboBoxInfo(IntPtr hwndCombo, ref COMBOBOXINFO info);

        private class EditBoxNativeWindow : NativeWindow
        {
            private ComboBox _owner;

            public EditBoxNativeWindow(IntPtr handle, ComboBox owner)
            {
                _owner = owner;
                AssignHandle(handle);
            }

            protected override void WndProc(ref Message m)
            {
                const int WM_PASTE = 0x0302;

                if (m.Msg == WM_PASTE)
                {
                    if (Clipboard.ContainsText())
                    {
                        string pasted = Clipboard.GetText();
                        MessageBox.Show("貼り付け文字: " + pasted);
                    }
                }
                base.WndProc(ref m);
            }
        }
    }
}

④一旦ビルドし、カスタムコントロールを使えるようにします。

⑤Form1のデザインビューに今追加したComboBoxのカスタムコントロール「CustomComboBox」を追加します。

⑥実行してみてください。CustomComboBoxに文字列を貼付けするとWM_PASTEで捕捉されメッセージボックスが表示されるはずです。

コメント