ごまなつ Blog

楽しく働ける世界を目指して

【C#】ListViewがNonClickableの状態で、カラムヘッダーを右クリックしても右クリックメニューが出る

ListViewは、ItemのSubtextが追加されるとカラムに分けられます。ヘッダー部分をNonClickableで表示し、ListViewにContextMenustripを設定していました。。

NonClickableでも右クリックメニューは出る

NonClickableですが、MouseClickイベントは発生しませんでした。しかし、右クリックするとContextMenustripが表示されます。その時、選択しているインデックスは-1になっています。

解決法

 private void contextMenuStrip1_Opening(object sender, System.ComponentModel.CancelEventArgs e)
        {
            Point targetPoint = listView.PointToClient(Cursor.Position);
            var Item = listView.HitTest(targetPoint).Item;

            if (Item == null || !Item.Bounds.Contains(targetPoint))
            {
                e.Cancel = true;
            }
        }

ContextMenuStrip_Openingでe.Cancelすることで右クリックメニューの表示をキャンセルできます。今回の場合は、ディスプレイの左上端が(0, 0)なのでコントロールの左上端が(0, 0)に変換します。HitTestで、右クリックした座標の項目情報をListViewItemとして取得し、ListViewItemでない場合はNullか、座標を含んでいないのでキャンセルします。これで解決かと思われたのですが、なんと、下にスクロールすることでヘッダー部分の下にListViewItemが隠され、ヘッダー部分を右クリックするとその部分の隠されているアイテムが選択された右クリックメニューが表示されました。そこで、

 private void contextMenuStrip1_Opening(object sender, System.ComponentModel.CancelEventArgs e)
        {
            Point targetPoint = listView.PointToClient(Cursor.Position);
            var Item = listView.HitTest(targetPoint).Item;

            if (Item == null || !Item.Bounds.Contains(targetPoint) || listView.FocusedItem != Item)
            {
                e.Cancel = true;
            }
        }

として解決を図りました。選択されているItemがHitTestで取得したItemと異なった場合にキャンセルするようにしました。が、隠されているアイテムが選択されている状態だと、右クリックメニューが表示されました。もっと考えた結果、シンプルな方法がありました。

listView.TopItem()を使う

listView.topItemは表示されているItemの一番上のItemを取得します。よって、listView.topItemが、クリックした位置よりも上だったらキャンセルするようにします。

Point targetPoint = ChannelMaplistView.PointToClient(Cursor.Position);
 Point targetPoint = listView.PointToClient(Cursor.Position);
            var Item = listView.HitTest(targetPoint).Item;

            if (Item == null || !Item.Bounds.Contains(targetPoint) || listView.FocusedItem != Item)
            {
                e.Cancel = true;
     return;
            }
            if(ChannelMaplistView.TopItem.Position.Y > targetPoint.Y)
            {
                e.Cancel = true;
            }

座標は左上端が(0, 0)で、下が正の値になります。よって、クリックした位置より上ということは、TopItemのY座標がクリックした位置の座標よりも大きいことになります。この2つの条件を用いることで、うまくいきました。