C# と VB.NET の質問掲示板

わんくま同盟

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

ツリー一括表示

データグリッドビューでクリックだけで複数行選択 /tosh (21/10/28(Thu) 09:22) #98283
Re[1]: データグリッドビューでクリックだけで複数行選択 /大谷刑部 (21/10/28(Thu) 13:34) #98286
Re[1]: データグリッドビューでクリックだけで複数行選択 /KOZ (21/10/28(Thu) 15:39) #98287
│└ Re[2]: データグリッドビューでクリックだけで複数行選択 /tosh (21/10/28(Thu) 17:03) #98289 解決済み
Re[1]: データグリッドビューでクリックだけで複数行選択 /魔界の仮面弁士 (21/10/28(Thu) 16:37) #98288
  └ Re[2]: データグリッドビューでクリックだけで複数行選択 /tosh (21/10/28(Thu) 17:11) #98290


親記事 / ▼[ 98286 ] ▼[ 98287 ] ▼[ 98288 ]
■98283 / 親階層)  データグリッドビューでクリックだけで複数行選択
□投稿者/ tosh (2回)-(2021/10/28(Thu) 09:22:06)

分類:[C#] 

お世話になります。

DataGridViewで、Ctrlキーを押さなくてもクリックだけでセルを複数行選択できるようにしたいです。

この掲示板の過去ログを調べたところ同様の質問があり、回答として
「DataGridView.WndProcのオーバーライドでCTRLキーが押されているように偽装する」
の一文と共に、方法が書かれたページのリンクが張ってあったのですが
リンクが切れていたため、具体的な方法がわかりませんでした。

回答の一文からWParamの値を書き換えるのではないかと推測し、以下の様な継承クラスを作って
実装して検証してみましたが、左クリック時にWParamの値は書き換わったものの行の複数選択はできず
別の行をクリックすると前の行の選択は解除されてしまいます。
方法が間違っているのだろうと思いますが、どうすれば実現できるでしょうか。

namespace System.Windows.Forms
{
    public class DataGridViewCustom : DataGridView
    {
        [SecurityPermission(SecurityAction.Demand,
        Flags = SecurityPermissionFlag.UnmanagedCode)]
        protected override void WndProc(ref Message m)
        {
            const int WM_LBUTTONDOWN = 0x0201;
            const int MK_CONTROL = 0x0008;
            
            switch (m.Msg)
            {
                case WM_LBUTTONDOWN:

                    int tmp = (int)m.WParam;
                    tmp |= MK_CONTROL;

                    m.WParam = (IntPtr)tmp;

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


[ □ Tree ] 返信 編集キー/

▲[ 98283 ] / 返信無し
■98286 / 1階層)  Re[1]: データグリッドビューでクリックだけで複数行選択
□投稿者/ 大谷刑部 (157回)-(2021/10/28(Thu) 13:34:45)
No98283 (tosh さん) に返信
> お世話になります。
>
> DataGridViewで、Ctrlキーを押さなくてもクリックだけでセルを複数行選択できるようにしたいです。

MouseDownで選択されてる行インデックスの選択/未選択情報を配列等に保存しておいて、
MouseUPで保存した情報で選択しなおせば、見た目は複数選択したように見えます。

ただし、右クリックで解除とか選択解除するロジックも考えておかないと、
一度選択した行が選択しっぱなしになるので、いい方法かどうかは微妙です。

[ 親 98283 / □ Tree ] 返信 編集キー/

▲[ 98283 ] / ▼[ 98289 ]
■98287 / 1階層)  Re[1]: データグリッドビューでクリックだけで複数行選択
□投稿者/ KOZ (162回)-(2021/10/28(Thu) 15:39:56)
2021/10/28(Thu) 15:56:13 編集(投稿者)

Framework のソースを見ると Ctrl キーの検出は Control.ModifierKeys を参照しています。

「DataGridView.OnCellMouseDown」
https://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/DataGridViewMethods.cs,6a150dea03bc38af

Control.ModifierKeys では GetKeyState を呼び出しています。

「Control.ModifierKeys」
https://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/Control.cs,6561698e6020148f

「GetKeyState function (winuser.h)」
https://docs.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-getkeystate

なので、WM_LBUTTONDOWN が来たら、keybd_event で Ctrl キーを押した状態にして WndProc を呼び出せばいけるかもしれません。
(WndProc を呼び出した後は、元に戻す)

「keybd_event function (winuser.h)」
https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-keybd_event

OnCellMouseDown をオーバーライドしてもいいかもしれません。

[ 親 98283 / □ Tree ] 返信 編集キー/

▲[ 98287 ] / 返信無し
■98289 / 2階層)  Re[2]: データグリッドビューでクリックだけで複数行選択
□投稿者/ tosh (4回)-(2021/10/28(Thu) 17:03:47)
回答ありがとうございます。

頂いたヒントに従い、当初はkeybd_eventを使っての実現を試みました。
目的の複数行選択は実現できたのですが、Ctrlキーを押した状態のままにすると
その後の操作が全てCtrl押しっぱなし状態になってしまうので
下記の様に処理を行った後に解除する様にしたのですが、
そうすると複数行選択もできなくなってしまいました。

    win32api.keybd_event(VK_CONTROL, 0, 0, (System.UIntPtr)0);
    base.WndProc(ref m);
    win32api.keybd_event(VK_CONTROL, 0, 0x02, (System.UIntPtr)0);


そこでGetKeyStateでキー押下状態を取得できるとのヒントから
GetKeyboardStateとSetKeyboardStateの使い方を調べ、最終的に
以下の方法で実現する事ができました。


namespace System.Windows.Forms
{
    public class DataGridViewCustom : DataGridView
    {

        [DllImport("user32.dll")]
        private static extern int
        SetKeyboardState([MarshalAs(UnmanagedType.LPArray)]byte[] lpKeyState);

        [DllImport("user32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool GetKeyboardState(byte[] lpKeyState);

        [SecurityPermission(SecurityAction.Demand,
        Flags = SecurityPermissionFlag.UnmanagedCode)]
        protected override void WndProc(ref Message m)
        {
            const int WM_LBUTTONDOWN = 0x0201;
            const int MK_CONTROL = 0x0008;
            const int VK_CONTROL = 0x11;

            switch (m.Msg)
            {
                case WM_LBUTTONDOWN:

                    byte[] keyState = new byte[256];

                    // キー押下状態の取得
                    GetKeyboardState(keyState);
                    
                    // Ctrlキー押下状態の保存
                    byte ctrlKeyStateBack = keyState[VK_CONTROL];

                    // Ctrlキーを押下状態にする
                    keyState[VK_CONTROL] |= 0x80;
                    
                    // キー押下状態の反映
                    SetKeyboardState(keyState);

                    base.WndProc(ref m);
					
                    // Ctrlキー押下状態の復元と反映
                    keyState[VK_CONTROL] = ctrlKeyStateBack;
                    SetKeyboardState(keyState);
                    
                    return;
            }
            base.WndProc(ref m);
        }
    }
}

より簡単な方法もあるかと思いますが、ひとまず解決したので解決済みとします。
ご協力ありがとうございました。

解決済み
[ 親 98283 / □ Tree ] 返信 編集キー/

▲[ 98283 ] / ▼[ 98290 ]
■98288 / 1階層)  Re[1]: データグリッドビューでクリックだけで複数行選択
□投稿者/ 魔界の仮面弁士 (3200回)-(2021/10/28(Thu) 16:37:57)
No98283 (tosh さん) に返信
> DataGridViewで、Ctrlキーを押さなくてもクリックだけでセルを複数行選択できるようにしたいです。
> この掲示板の過去ログを調べたところ同様の質問があり、回答として
> 「DataGridView.WndProcのオーバーライドでCTRLキーが押されているように偽装する」
> の一文と共に、方法が書かれたページのリンクが張ってあったのですが

No87697 のことですね?
http://bbs.wankuma.com/index.cgi?mode=al2&namber=87696&KLOG=151

> リンクが切れていたため、具体的な方法がわかりませんでした。
あとは No48251 とか、 No55062 のスレッドとか。
http://bbs.wankuma.com/index.cgi?mode=al2&namber=48214&KLOG=81
http://bbs.wankuma.com/index.cgi?mode=al2&namber=55062&KLOG=92


> 方法が間違っているのだろうと思いますが、どうすれば実現できるでしょうか。
MK_CONTROL → VK_CONTROL とか?


public partial class Form1 : Form
{
 private DataGridView dgv;
 private DataGridViewRow[] selectedRows = { };
 public Form1()
 {
  InitializeComponent();
  Controls.Add(dgv = new ToshDataGridView { Dock = DockStyle.Fill });
  dgv.ColumnCount = 5;
  dgv.RowCount = 30;
 }
}


public class ToshDataGridView : DataGridView
{
 protected override void WndProc(ref Message m)
 {
  const int WM_LBUTTONDOWN = 0x0201;
  if(m.Msg == WM_LBUTTONDOWN)
  {
   ControlKey = true;
   Application.DoEvents();
   base.WndProc(ref m);
   ControlKey = false;
  }
  else
  {
   base.WndProc(ref m);
  }
 }

 private bool ControlKey
 {
  set
  {
   //const byte MK_CONTROL = 0x08;
   const byte VK_CONTROL = 0x11;
   const uint KEYEVENTF_KEYDOWN = 0x0u;
   const uint KEYEVENTF_KEYUP = 0x2u;
   const uint KEYEVENTF_EXTENDEDKEY = 0x1u;
   keybd_event(VK_CONTROL, 0, KEYEVENTF_EXTENDEDKEY | (value ? KEYEVENTF_KEYDOWN : KEYEVENTF_KEYUP), UIntPtr.Zero);
  }
 }
 [DllImport("user32.dll")]
 public static extern uint keybd_event(byte bVk, byte bScan, uint dwFlags, UIntPtr dwExtraInfo);
}
[ 親 98283 / □ Tree ] 返信 編集キー/

▲[ 98288 ] / 返信無し
■98290 / 2階層)  Re[2]: データグリッドビューでクリックだけで複数行選択
□投稿者/ tosh (5回)-(2021/10/28(Thu) 17:11:09)
魔界の仮面弁士様

レスを確認せずに投稿してしまいました。
私が参考にしたかったスレは、確かにNo87697です。
元々の方法でVK_CONTROLにした場合に成功するかどうかは確認してみようと思います。
ありがとうございました。
[ 親 98283 / □ Tree ] 返信 編集キー/


管理者用

- Child Tree -