Reputation: 2947
I found a way to handler right click in a ListView Header
column.
I needed to define a new ListView
item and override WndProc
.
public class ListViewUpdated : ListView
{
public ContextMenuStrip HeaderContextMenu { get; set; }
private frmMain mainForm;
public ListViewUpdated()
{
mainForm = null;
}
public void setAttributes(frmMain frm, ContextMenuStrip menu)
{
mainForm = frm;
HeaderContextMenu = menu;
}
protected override void WndProc(ref System.Windows.Forms.Message m)
{
base.WndProc(ref m);
if (m.Msg == 0x7b)
{ //WM_CONTEXTMENU
if (m.WParam != this.Handle)
{
mainForm.Invoke(mainForm.delegateDeleteColumn,
new Object[] { HeaderContextMenu });
}
}
}
private void InitializeComponent()
{
this.SuspendLayout();
this.ResumeLayout(false);
}
}
This does work fine. I used a delegate so the event trigger could be a function within the form.
The event so far within the form is this:
if (tabControl1.SelectedIndex == 0)
{
System.Drawing.Point mousePos = lsvRequestsByParameters.PointToClient(Control.MousePosition);
hitTest = lsvRequestsByParameters.HitTest(mousePos);
}
else
{
System.Drawing.Point mousePos = lsvRequestsByParametersToSave.PointToClient(Control.MousePosition);
hitTest = lsvRequestsByParametersToSave.HitTest(mousePos);
}
HeaderContextMenu.Show(Control.MousePosition);
It is called properly.
It does allow me to popup the context menu on the header.
The problem is one of the option is delete column:
private void deleteColumnToolStripMenuItem2_Click(object sender, EventArgs e)
{
int columnIndex = hitTest.Item.SubItems.IndexOf(hitTest.SubItem);
lsvRequestsByParameters.Columns.RemoveAt(columnIndex);
}
Problem is the following line:
int columnIndex = hitTest.Item.SubItems.IndexOf(hitTest.SubItem);
It seems that when I set the hitTest
variable, if the mouse is over a header, it doesn't return a content usable. Note that using hitTest this way would return me the columnIndex
properly if I click on a empty cell.
So what can I do instead to obtain the columnIndex
?
Upvotes: 2
Views: 1483
Reputation: 54453
As long as there are Items
in the ListView
you could fool the HitTest
by feeding in a location that is below the header..:
Point eLoc = listView1.PointToClient(Control.MousePosition);
ListViewHitTestInfo hit = null;
int i = 0;
do
{
Point vp = new Point(eLoc.X, listView1.Items[i].Bounds.Top);
hit = listView1.HitTest(vp);
i++;
}
while( hit.SubItem == null && i < listView1.Items.Count);
var ColumnIndex = hit.Item.SubItems.IndexOf(hit.SubItem);
Note that the loop is only necessary if your SubItems
are jagged. Otherwise you can simply always go for Items[0]
.
Update:
If for reason you want to allow clicking the headers of an empty ListView
, you can use this workaround:
Declare a variable that holds all the bounding rectangles of the headers:
private List<Rectangle> lvHeaderBounds = new List<Rectangle>();
In the above code that processes the ColumnClick
use this:
Point eLoc = listView1.PointToClient(Control.MousePosition);
var ColumnIndex = -1;
if (listView1.Items.Count == 0)
{
getHeaders(listView1);
for (int i = 0; i < lvHeaderBounds.Count; i++)
if (lvHeaderBounds[i].X + lvHeaderBounds[i].Width > eLoc.X)
{
ColumnIndex = i;
break;
}
}
else...
To collect the bounding rectangles we need to loop over the ColumHeader
collection. Here is a function that achieves this:
void getHeaders(ListView lv)
{
lvHeaderBounds.Clear();
for (int i = 0; i < lv.Columns.Count; i++)
{
ColumnHeader ch = lv.Columns[i];
int left = i == 0 ? 0 : lvHeaderBounds[i-1].X + ch.Width;
Rectangle r = new Rectangle(left, 0, ch.Width, 0);
lvHeaderBounds.Add(r);
}
}
We also need to refresh our collection when the user or code modifies the header layout:
private void listView1_ColumnReordered(object sender, ColumnReorderedEventArgs e)
{
if (lvHeaderBounds != null) lvHeaderBounds.Clear();
getHeaders(sender as ListView);
}
private void listView1_ColumnWidthChanged(object sender, ColumnWidthChangedEventArgs e)
{
if (lvHeaderBounds != null) lvHeaderBounds.Clear();
getHeaders(sender as ListView);
}
Note that the Height
values in our list are all 0
!
Also note that in my tests the code works even after resizing the headers and/or scrolling them to the right..
Upvotes: 2