2008年11月6日
複数選択可能なListViewコントロール
いや、もちろんCompact Frameworkでなければ…ListViewのMultiSelectプロパティをTrueにするだけ
なので、ListViewコントロールを見ると…ないよ
でたぁ~また、Compact Frameworkにはない
要するにチェックボックス付きがあるだろう…ってのがMS様の言い分なのかな
仕方ないので、Google先生に訊いてみるが…なかなか答えてくれない
そんな中、CodeProjectで見つけたのはCompact Frameworkとは全く関係ない…以下のXPTableというListView拡張で、何かの機会に使うためにメモ
あ、Compact Frameworkでした。調べてると同じことで質問してる人は居るんだけど…なかなか望んだ回答がでていないなぁ~と思っていると以下のページを発見。
ページの中にuuencodeされたMultiSelectListView.zipというファイルが貼り付けられてるなぁ~内容はわからないけど、どにかくエディタに貼り付けて…改行コードをLFで保存して、Cygwinのuudecodeでファイルを取り出してみた。なんか、uudecodeなんて使うの超ひさしぶりだなぁ~なんて思いつつ。
でてきた、MultiSelectListView.zipを展開すると…おぉ、VB.netのコードだったのね。量からみて、大きくないのはわかっていたので中身を見てみる。
なるほどポイントは、P/Invokeを使ってSetWindowLongを呼び出して、ウィンドウ(ListView)のLVS_SINGLESEL属性を外してやることだけなのね
わかってみれば、なるほどなんだけど…思いつかなかったなぁ。
と言うことで、C#に書き直したポイントだけのコードをペタっとしておきます
ありゃ、わざわざGetCaptureしてウィンドウハンドルを取らなくてもListViewのプロパティから取れますね
private const int GWL_STYLE = -16; private const int LVS_SINGLESEL = 0x4; [DllImport("coredll.dll")] static extern IntPtr GetCapture(); [DllImport("coredll.dll", SetLastError = true)] private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); [DllImport("coredll.dll", SetLastError = true)] private static extern UInt32 GetWindowLong(IntPtr hWnd, int nIndex); private void EnableMultiSelect(ListView listView) { listView.Capture = true; IntPtr hWnd = GetCapture(); listView.Capture = false; uint style = GetWindowLong(hWnd, GWL_STYLE); SetWindowLong(hWnd, GWL_STYLE, (int)(style & ~LVS_SINGLESEL)); }
このEnableMultiSelectメソッドをListViewを使うフォームのコンストラクタあたりで、複数選択させたいListViewを引数にして呼べば幸せになれるかも知れません。
さらに、CtrlキーやShiftキーを同時押下するのが面倒(ソフトウェアキーボードだもんね)なんて場合は、ListViewのフォーカスイン時とフォーカスアウト時にCtrlキーを制御してフォーカスのある間はCtrlキーを押した状態にしておけば…さらに幸せになれるかも知れません
Ctrlキーを制御するコードもついでなのでペタっと。
private const byte VK_CONTROL = 0x11; private const int KEYEVENTF_KEYDOWN = 0x0; private const int KEYEVENTF_KEYUP = 0x2; [DllImport("coredll.dll", EntryPoint = "keybd_event", SetLastError = true)] internal static extern void keybd_event(byte bVk, byte bScan, int dwFlags, int dwExtraInfo); private void FocusIn() { keybd_event(VK_CONTROL, 0, KEYEVENTF_KEYDOWN, 0); } private void FocusOut() { keybd_event(VK_CONTROL, 0, KEYEVENTF_KEYUP, 0); }
あ、あと選択されている項目を取り出すコードもペタっとしとかなきゃ。
ListView.SelectedIndexCollection collection = listViewMulti.SelectedIndices; int count = collection.Count; // 選択されている個数がcountに foreach (int i in collection) { // iに選択されているリストのインデックスが順次入る(当然0からの値) }
追記 2008-11-10 21:24:25
Ctrlキーの同時押しですが…FocusInとFocusOut時の制御では、やはり制御しきれないみたいです ![]()
結局、ListViewのWinProcをフックしてWM_LBUTTONDOWN時にMK_CONTROLビットをORすることで同時に押されている状態としました。
さらに、WM_LBUTTONDBLCLKとWM_KILLFOCUSメッセージを無視するようにすると…さらに幸せになれるかも知れません。
さらに追記 2008-11-13 01:13:25
WinProcのフックですが…『WndProcHooker クラスを使用する』のやり方を忠実に実装しました。当初、安易な方法で実装したら…最小化、最大化などを行うと思わぬ挙動をして半泣き。
なので、結局カスタムコントロール化してEnableMultiSelectと同様の処理をコンストラクタで行うようにして、ようやく最終形ができあがりました。
確かにカスタムコントロール内で全て閉じたのでコードは綺麗になったけど…超面倒ですね ![]()
TrackBack URL :
