ListView の仮想モードを有効にした状態で、Shiftキーを押下しながらリスト上の要素を選択すると例外が発生する

こんにちは、Visual Studio サポート チームです。

今回は、仮想モード有効時の ListView コントロールで例外が発生する現象について、回避策とあわせてご案内します。

 

現象

.NET Framework の Windows フォーム アプリケーションにおいて、Shift キーを押下しながら ListView コントロールの要素を選択すると、以下の例外が発生することがあります。

発生する例外 : System.ArgumentException
発生元クラス : ListViewVirtualItemsSelectionRangeChangedEventArgs
メッセージ : startIndex 値に endIndex 値より大きい値を指定することはできません。

本例外は、ListView コントロールの複数の要素のうち、最初以外の要素を Shift キーを押下しながら選択した後、最初の要素を Shift キーを押下しながら選択すると発生します。

また、この現象は、次の条件に当てはまる場合に発生します。

  • アプリケーションの実行環境が .NET Framework 4 である
  • ListView コントロールの VirtualMode プロパティが True に設定されている

 

回避策

本現象は、.NET Framework 4 の不具合に起因して発生します。
回避策としては、以下の 2 つがあります。

1. .NET Framework をバージョン アップする

本不具合は、.NET Framework 4.5 では修正されています。
このため、実行環境の .NET Framework を 4.5 以上にバージョン アップすることで、現象を回避することができます。

2. アプリケーションに対処コードを追加する

本現象は、以下のような手順でアプリケーションに変更を追加することでも回避することができます。

1. ListView クラスを継承して、以下のような MyListView クラスを作成します。

コード例
--------------------------------------------

    using System.Runtime.InteropServices;

    public class MyListView : ListView
    {
        private const int WM_USER = 0x400;
        private const int WM_REFLECT = WM_USER + 0x1C00;
        private const int WM_NOTIFY = 0x004E;
        private const int LVN_ODSTATECHANGED = ((0 - 100) - 15);
        private const int LVIS_SELECTED = 0x0002;

        [StructLayout(LayoutKind.Sequential)]
        public struct NMHDR
        {
            public IntPtr hwndFrom;
            public IntPtr idFrom; //This is declared as UINT_PTR in winuser.h
            public int code;
        }

        [StructLayout(LayoutKind.Sequential)]
        public class NMLVODSTATECHANGE
        {
            public NMHDR hdr;
            public int iFrom = 0;
            public int iTo = 0;
            public int uNewState = 0;
            public int uOldState = 0;
        }

        protected override void WndProc(ref Message m)
        {
            if (!MyWndProc(ref m))
            {
                base.WndProc(ref m);
            }
        }

        unsafe bool MyWndProc(ref Message m)
        {
            switch (m.Msg)
            {
                case WM_REFLECT + WM_NOTIFY:
                    NMHDR* nmhdr = (NMHDR*)m.LParam;
                    switch (nmhdr->code)
                    {
                        case LVN_ODSTATECHANGED:
                            if (VirtualMode && m.LParam != IntPtr.Zero)
                            {
                                NMLVODSTATECHANGE odStateChange = (NMLVODSTATECHANGE)m.GetLParam(typeof(NMLVODSTATECHANGE));
                                bool selectedChanged = (odStateChange.uNewState & LVIS_SELECTED) != (odStateChange.uOldState & LVIS_SELECTED);
                                if (selectedChanged)
                                {
                                    ListViewVirtualItemsSelectionRangeChangedEventArgs lvvisrce = new ListViewVirtualItemsSelectionRangeChangedEventArgs(odStateChange.iFrom, odStateChange.iTo, (odStateChange.uNewState & LVIS_SELECTED) != 0);
                                    OnVirtualItemsSelectionRangeChanged(lvvisrce);
                                }
                                return true;
                            }
                            break;
                        default:
                            break;
                    }
                    break;
                default:
                    break;
            }
            return false;
        }
    }

--------------------------------------------

2. フォームのデザイナ コード (xxxx.Designer.cs) で、MyListView クラスを使用するよう、以下のように変更します。

コード例
--------------------------------------------

変更前 :

    private System.Windows.Forms.ListView listView1;
    this.listView1 = new System.Windows.Forms.ListView();

変更後 :

    private MyListView listView1;
    this.listView1 = new MyListView();

--------------------------------------------

3. プロジェクトのプロパティから、ビルド - 全般 の [アンセーフ コードの許可] にチェックを入れ、アプリケーションをリビルドします。

 

関連情報

ListView.VirtualMode プロパティ
https://msdn.microsoft.com/ja-jp/library/system.windows.forms.listview.virtualmode(v=vs.100).aspx

 

最後に

マイクロソフトではこの問題を製品の不具合と認識しておりますが、現状では、修正プログラムは提供されておりません。
本問題が発生する環境でアプリケーションを実行する必要がある場合は、お手数ですが、上述の回避策の利用をご検討ください。
弊社製品の不具合でご迷惑をおかけいたしておりますこと、深くお詫び申し上げます。