How to create a dynamically built Context Menu clickEvent - c#

I have a DataGridView and a context menu that opens when you right click a specific column.
What shows up in the context menu is dependant on what's in the field clicked on - paths to multiple files (the paths are manipulated to create a full UNC path to the correct file).
The only problem is that I can't get the click working.
I did not drag and drop the context menu from the toolbar, I created it programmically.
I figured that if I can get the path (let's call it ContextMenuChosen) to show up in MessageBox.Show(ContextMenuChosen); I could set the same to System.Diagnostics.Process.Start(ContextMenuChosen);
The Mydgv_MouseUp event below actually works to the point where I can get it to fire off MessageBox.Show("foo!"); when something in the context menu is selected but that's where it ends. I left in a bunch of comments below showing what I've tried when it one of the paths are clicked. Some result in empty strings, others error (Object not set to an instance...).
I searched code all day yesterday but couldn't find another way to hook up a dynamically built Context Menu clickEvent.
Code and comments:
ContextMenu m = new ContextMenu();
// SHOW THE RIGHT CLICK MENU
private void Mydgv_MouseClick(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
int currentMouseOverCol = Mydgv.HitTest(e.X, e.Y).ColumnIndex;
int currentMouseOverRow = Mydgv.HitTest(e.X, e.Y).RowIndex;
if (currentMouseOverRow >= 0 && currentMouseOverCol == 6)
{
string[] paths = myPaths.Split(';');
foreach (string path in paths)
{
string UNCPath = "\\\\1.1.1.1\\c$\\MyPath\\";
string FilePath = path.Replace("c:\\MyPath\\", #"");
m.MenuItems.Add(new MenuItem(UNCPath + FilePath));
}
}
m.Show(Mydgv, new Point(e.X, e.Y));
}
}
// SELECTING SOMETHING IN THE RIGHT CLICK MENU
private void Mydgv_MouseUp(object sender, MouseEventArgs e)
{
DataGridView.HitTestInfo hitTestInfo;
if (e.Button == MouseButtons.Right)
{
hitTestInfo = Mydgv.HitTest(e.X, e.Y);
// If column is first column
if (hitTestInfo.Type == DataGridViewHitTestType.Cell && hitTestInfo.ColumnIndex == 6)
{
//MessageBox.Show(m.ToString());
////MessageBox.Show(m.Tag.ToString());
//MessageBox.Show(m.Name.ToString());
//MessageBox.Show(m.MenuItems.ToString());
////MessageBox.Show(m.MdiListItem.ToString());
// MessageBox.Show(m.Name);
//if (m.MenuItems.Count > 0)
//MessageBox.Show(m.MdiListItem.Text);
//MessageBox.Show(m.ToString());
//MessageBox.Show(m.MenuItems.ToString());
//Mydgv.ContextMenu.Show(m.Name.ToString());
//MessageBox.Show(ContextMenu.ToString());
//MessageBox.Show(ContextMenu.MenuItems.ToString());
//MenuItem.text
//MessageBox.Show(this.ContextMenu.MenuItems.ToString());
}
m.MenuItems.Clear();
}
}
I'm very close to completing this so any help would be much appreciated.

I don't see an event handler wired to your menu item, so that something will happen when you click it:
void menu_Click(object sender, EventArgs e) {
MessageBox.Show(((MenuItem)sender).Text);
}
Then attach it when you add the menu item to the context menu:
MenuItem mi = new MenuItem(UNCPath + FilePath);
mi.Click += menu_Click;
m.MenuItems.Add(mi);

You may handle CellMouseDown like this when you when you right click a cell of a specific column. The specific column is achieved by e.ColumnIndex check
Aditionally, you can use GetCellDisplayRectangle instead of hittest
ContextMenu cm = new ContextMenu();
void dataGridView1_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
if (e.ColumnIndex == 0)
{
cm.MenuItems.Clear();
var mi = new MenuItem("C:\temp");
mi.MenuItems.Add(mi);
// handle menu item click event here [as required]
mi.Click += OnMenuItemClick;
cm.MenuItems.Add(0, mi);
var bounds = dataGridView1.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, false);
if (sender != null)
{
cm.Show(sender as DataGridView, new Point(bounds.X, bounds.Y));
}
}
}
}
void OnMenuItemClick(object sender, EventArgs e)
{
MessageBox.Show(((MenuItem)sender).Text);
}

Related

Setting up and understanding ContextMenuStrip

first of all:
I´m a student that is still learning about programming.
The problem is that when I right click in a row insde the dataGridView, the RightClickDataView.Items.Add("Abgegeben"); appears as many times as I click. How can I change this?
private void dataGridViewBestellungen_MouseDown(object sender, MouseEventArgs e)
{
if(e.Button == MouseButtons.Right)
{
var hti = dataGridViewBestellungen.HitTest(e.X, e.Y);
dataGridViewBestellungen.Rows[hti.RowIndex].Selected = true;
RightClickDataView.Items.Add("Abgegeben");
RightClickDataView.Show(Cursor.Position);
var xy = dataGridViewBestellungen.SelectedRows;
foreach (DataGridViewRow row in xy)
{
//take the id in the datagridview
}
RightClickDataView.ItemClicked += new ToolStripItemClickedEventHandler(rightclickmenu_ItemClicked);
// close if mouse goes away from window
}
}
private void rightclickmenu_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
{
ToolStripItem item = e.ClickedItem;
dataGridViewBestellungen.ClearSelection();
if (e.ClickedItem.Text == "Zurück")
{
//change the state to erledigt
}
}
Just remove RightClickDataView.Items.Add("Abgegeben"); from your dataGridViewBestellungen_MouseDown and place this line on your constructor for example.

Menu item event handler

I am trying to modify a DataGridView object through the use of a right-click menu.
At the moment I can successfully create the right-click menu and identify the method that will be called after the menu item was selected, however the target method can not access information about which record within the DataGridView was selected when the menu item was selected -- or any other information for that matter (unless it was a class-level variable).
My goal is to find a way to either send information to the target method, or find a way to modify the DataGridView object within the same method that created the right-click menu.
https://msdn.microsoft.com/en-us/library/system.windows.forms.menuitem(v=vs.110).aspx
private void InspectionDataGridViewCellMouseClick(object sender, DataGridViewCellMouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
ContextMenu m = new ContextMenu();
if (this.currentInspection != null) // the row that is being right-clicked might not be the row that is selected.
{
m.MenuItems.Add(new MenuItem(string.Format("Mark inspection point {0} as complete.", this.currentInspection.InspectionNumber), this.markInspectionPointComplete));
}
m.Show(this.InspectionDataGridView, new Point(e.X,e.Y));
}
}
private void markInspectionPointComplete(object sender, EventArgs e)
{
MessageBox.Show("the right-click menu works.");
}
I tried invoking the target method using a DataGridViewCellMouseEventArgs object, but that creates an error with the m.MenuItems.Add() line because it ONLY expects an EventArgs object.
So, either I need to modify the EventArgs object that is sent, find another method signature that will serve this purpose, or find a way to perform actions on the DataGridView within the same method that created the right-click menu.
Thanks in advance!
The simplest solution is to use 'Tag' property of the 'MenuItem' object.
private void InspectionDataGridViewCellMouseClick(object sender, DataGridViewCellMouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
ContextMenu m = new ContextMenu();
int currentRow = e.RowIndex;
if (this.currentInspection != null) // the row that is being right-clicked might not be the row that is selected.
{
var MI = new MenuItem(string.Format("Mark inspection point {0} as complete.", this.currentInspection.InspectionNumber), this.markInspectionPointComplete)
MI.Tag = e;
m.MenuItems.Add(MI);
}
m.Show(this.InspectionDataGridView, new Point(e.X,e.Y));
}
}
private void markInspectionPointComplete(object sender, EventArgs e)
{
var MI = (MenuItem)sender;
DataGridViewCellMouseEventArgs Obj = (DataGridViewCellMouseEventArgs) MI.Tag;
// Do whatever you want with your Obj
MessageBox.Show("the right-click menu works.");
}
OR
Use lambda expressions like this:
private void InspectionDataGridViewCellMouseClick(object sender, DataGridViewCellMouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
ContextMenu m = new ContextMenu();
int currentRow = e.RowIndex;
if (this.currentInspection != null) // the row that is being right-clicked might not be the row that is selected.
{
var MI = new MenuItem(string.Format("Mark inspection point {0} as complete.", this.currentInspection.InspectionNumber));
MI.Click += (s, x) =>
{
// Use 'e' or 'sender' here
}
m.MenuItems.Add(MI);
}
m.Show(this.InspectionDataGridView, new Point(e.X,e.Y));
}
}

Listview Large Icon right click to open ContextMenuStrip

In my project I have a ListView and I would like to open my ContextMenuStrip when I clicked right button in the large icon. I tried many things but I am unsuccessful. When I right click inside of ListView the ContextMenuStrip opens, but I want to see just when I right clicked the large icon.
Also I need to help about get the clicked icon's name (properties).
This is a quick and dirty solution; please do put more work into it than I did..
// a class level reference, prepare it where you want..
ContextMenuStrip ms = new ContextMenuStrip();
You should either code the MouseDown or the MouseUp event:
private void listView1_MouseDown(object sender, MouseEventArgs e)
{
// disassociate from listview at first:
listView1.ContextMenuStrip = null;
// check for right button
if (e.Button != System.Windows.Forms.MouseButtons.Right) return;
// get item info:
ListViewHitTestInfo hi = listView1.HitTest(e.Location);
// no item hit:
if (hi.Item == null) return;
// calculate the image rectangle:
// this contains the unscrolled y coordinate:
Point iloc = listView1.GetItemRect(hi.Item.Index).Location;
// we combine it with the x-position:
Rectangle r = new Rectangle(new Point (hi.Item.Position.X, iloc.Y),
imageList1.ImageSize);
// no image hit:
if ( !r.Contains(e.Location) ) return;
// maybe prepare or change the menue now..
// here I display the image name from the keys array:
ms.Items[0].Text = imageList1.Images.Keys[hi.Item.ImageIndex];
ms.Location = e.Location;
// associate with listview and show
listView1.ContextMenuStrip = ms;
ms.Show();
}
Can you please try the following and let see wether it works or not...
private void listView1_MouseClick(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
if (listView1.FocusedItem.Bounds.Contains(e.Location) == true)
{
contextMenuStrip1.Show(Cursor.Position);
}
}
}
This should work
private void listView1_MouseClick(object sender, MouseEventArgs e)
{
ListView listView = sender as ListView;
if (e.Button == System.Windows.Forms.MouseButtons.Right)
{
ListViewItem item = listView.GetItemAt(e.X, e.Y);
if (item != null)
{
item.Selected = true;
contextMenuStrip1.Show(listView , e.Location);
}
}
}
Search the listview item on mouse click location. If it is there, show the menu.........

Showing a Context Menu for an item in a ListView

I know how to make a contextMenu that pops up when I right click on a listView, what I want is for it to pop up when I right click on an item. I am trying to make a chat server and client, and now... Now I want to view client info when I right click on a connected client's item.
How can I do this?
private void listView1_MouseClick(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
var focusedItem = listView1.FocusedItem;
if (focusedItem != null && focusedItem.Bounds.Contains(e.Location))
{
contextMenuStrip1.Show(Cursor.Position);
}
}
}
You can put connected client information in the contextMenuStrip1. and when you right click on a item, you can show the information from that contextMenuStrip1.
You are going to have to use the ListViews Context Menu, but change it according to the ListView Item you right click on.
private void listView1_MouseDown(object sender, MouseEventArgs e)
{
bool match = false;
if (e.Button == System.Windows.Forms.MouseButtons.Right)
{
foreach (ListViewItem item in listView1.Items)
{
if (item.Bounds.Contains(new Point(e.X, e.Y)))
{
MenuItem[] mi = new MenuItem[] { new MenuItem("Hello"), new MenuItem("World"), new MenuItem(item.Name) };
listView1.ContextMenu = new ContextMenu(mi);
match = true;
break;
}
}
if (match)
{
listView1.ContextMenu.Show(listView1, new Point(e.X, e.Y));
}
else
{
//Show listViews context menu
}
}
}
You can trigger MouseDown or MouseUp event of ListView in which if MouseButton.Right then grab the selected Item by using ListView.Hittest and give the Context menu related to that Selected Item.
For more clear info you can go through this link
Fully solution
Pops up when user right click on a item in a listView.
Avoid an exception if the list have no items.
If an item was selected, display Delete and Edit options.
Code:
private void Form1_Load(object sender, EventArgs e)
{
listView1.MouseUp += new MouseEventHandler(listView1_MouseClick);
}
private void listView1_MouseClick(object sender, MouseEventArgs e)
{
string id = "xxx";//extra value
if (e.Button == MouseButtons.Right)
{
if (listView1.FocusedItem != null && listView1.FocusedItem.Bounds.Contains(e.Location) == true)
{
ContextMenu m = new ContextMenu();
MenuItem cashMenuItem = new MenuItem("編輯");
cashMenuItem.Click += delegate (object sender2, EventArgs e2) {
ActionClick(sender, e, id);
};// your action here
m.MenuItems.Add(cashMenuItem);
MenuItem cashMenuItem2 = new MenuItem("-");
m.MenuItems.Add(cashMenuItem2);
MenuItem delMenuItem = new MenuItem("刪除");
delMenuItem.Click += delegate (object sender2, EventArgs e2) {
DelectAction(sender, e, id);
};// your action here
m.MenuItems.Add(delMenuItem);
m.Show(listView1, new Point(e.X, e.Y));
}
}
}
private void DelectAction(object sender, MouseEventArgs e, string id)
{
ListView ListViewControl = sender as ListView;
foreach (ListViewItem eachItem in ListViewControl.SelectedItems)
{
// you can use this idea to get the ListView header's name is 'Id' before delete
Console.WriteLine(GetTextByHeaderAndIndex(ListViewControl, "Id", eachItem.Index) );
ListViewControl.Items.Remove(eachItem);
}
}
private void ActionClick(object sender, MouseEventArgs e, string id)
{
//id is extra value when you need or delete it
ListView ListViewControl = sender as ListView;
foreach (ListViewItem tmpLstView in ListViewControl.SelectedItems)
{
Console.WriteLine(tmpLstView.Text);
}
}
public static string GetTextByHeaderAndIndex(ListView listViewControl, string headerName, int index)
{
int headerIndex = -1;
foreach (ColumnHeader header in listViewControl.Columns)
{
if (header.Name == headerName)
{
headerIndex = header.Index;
break;
}
}
if (headerIndex > -1)
{
return listViewControl.Items[index].SubItems[headerIndex].Text;
}
return null;
}
The topic is quite old, but I'll leave my solution for reference.
In xaml ListView definition put your context menu:
<ListView Name="m_list" >
<ListView.ContextMenu>
<ContextMenu>
<MenuItem Header="menuItem1" Click="ContextMenuItem1Clicked" />
<MenuItem Header="menuItem2" Click="ContextMenuItem2Clicked" />
</ContextMenu>
</ListView.ContextMenu>
...
</ListView>
Now, in the code, define two event handlers that will fire up on clicking respective menu item:
private void ContextMenuItem1Clicked(object sender, RoutedEventArgs e)
{
// handle the event for the selected ListViewItem accessing it by
ListViewItem selected_lvi = this.m_list.SelectedItem as ListViewItem;
}
private void ContextMenuItem2Clicked(object sender, RoutedEventArgs e)
{
// handle the event for the selected ListViewItem accessing it by
ListViewItem selected_lvi = this.m_list.SelectedItem as ListViewItem;
}
ListView can accommodate objects, which means that the selected_lvi can be of type of your object. Just cast is to proper type.
I hope this helps.
Best regards,
Mike
I've found a new solution that doesn't rely on mouse event handlers.
The ContextMenu has a "Popup" event handler.
On popup, I add the relevant menu items I need depending on my context.
Example :
private MenuItem[] standardMenuItems;
private MenuItem[] selectedMenuItems;
public SomePanel() {
InitializeComponent();
// These are list of menu items (name / callback) that will be
// chosen depending on some context
standardMenuItems = new MenuItem[] { new MenuItem("New", OnNew) };
selectedMenuItems = new MenuItem[] { new MenuItem("Delete", OnDelete), new MenuItem("Edit", OnEdit) };
ContextMenu contextMenu = new ContextMenu();
// begin with "standard items"
contextMenu.MenuItems.AddRange(standardMenuItems);
listview.ContextMenu = contextMenu;
// add the popup handler
contextMenu.Popup += OnMenuPopup;
}
// Called right before the menu comes up
private void OnMenuPopup(object sender, EventArgs e) {
ContextMenu menu = sender as ContextMenu;
if (menu == null)
return;
// If an item was selected, display Delete and Edit options
if (listview.SelectedItems.Count > 0) {
menu.MenuItems.Clear();
menu.MenuItems.AddRange(selectedMenuItems);
}
// Else display only the New option
else {
menu.MenuItems.Clear();
menu.MenuItems.AddRange(standardMenuItems);
}
}
I'm not fluent enough in C# and Winforms to be sure there are no drawbacks to this technique, but it's an alternative to relying on mouse events (what if / does the context menu can appear on other keyboard or mouse event ?)
private void contextMenuStripExport_Opening(object sender, System.ComponentModel.CancelEventArgs e)
{
if (exportview.SelectedItems.Count <= 0)
{
uploadToolStripMenuItem.Visible = false;
exportToolStripMenuItem.Visible = false;
}
else
{
uploadToolStripMenuItem.Visible = true;
exportToolStripMenuItem.Visible = true;
}
}
Using DevExpress 20.2 Core... Winforms. This is similar to handling a menu item in a GridView.
Private WithEvents _menuViewLabelitem As MenuItem
Private Sub lvShipTracking_MouseClick(sender As Object, e As MouseEventArgs) Handles lvShipTracking.MouseClick
If e.Button = MouseButtons.Right Then
If lvShipTracking.FocusedItem IsNot Nothing AndAlso lvShipTracking.FocusedItem.Bounds.Contains(e.Location) = True Then
Dim m As ContextMenu = New ContextMenu()
_menuViewLabelitem = New MenuItem("View Label")
AddHandler Click, AddressOf Handle_ViewLabel
m.MenuItems.Add(_menuViewLabelitem)
m.Show(lvShipTracking, New Point(e.X, e.Y))
End If
End If
End Sub
Private Sub Handle_ViewLabel(sender As Object, e As EventArgs) Handles _menuViewLabelitem.Click
MsgBox("it worked!")
End Sub

How can I use a right click context menu on a DataGridView?

I've created a context menu, and associated to my DataGridView control. However, I noticed that when I right click on the control, the selection in the dataGridView isn't changed. So I can't correctly fetch the row in the context's event handler.
Any suggestions on how I could accomplish this?
Imagine I have an ID olumn, and when I click the delete context menu, I want to delete that particular entry from the database.
I just need the information on how to get that id, I can handle the deleting myself.
private void dataGridViewSource_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e)
{
if (e.Button != MouseButtons.Right || e.RowIndex == -1 || e.ColumnIndex == -1) return;
dataGridViewSource.CurrentCell = dataGridViewSource.Rows[e.RowIndex].Cells[e.ColumnIndex];
contextMenuStripGrid.Show(Cursor.Position);
}
This is how you could show context menu and select current cell if a cell is clicked.
private void dataGridView1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
DataGridView.HitTestInfo hit = dataGridView1.HitTest(e.X, e.Y);
if (hit.Type == DataGridViewHitTestType.Cell)
{
dataGridView1.CurrentCell = dataGridView1[hit.ColumnIndex, hit.RowIndex];
contextMenuStrip1.Show(dataGridView1, e.X, e.Y);
}
}
}
In Click event handler from your menu item check dataGridView1.CurrentRow to find out which row is currently selected. For example, if the grid is bound to a datasource:
private void test1ToolStripMenuItem_Click(object sender, EventArgs e)
{
var item = dataGridView1.CurrentRow.DataBoundItem;
}
When you test this code, make sure that DataGridView.ContextMenuStrip property is not set.
Add,
DataGridViewRow currentRow;
void DataGridView_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e)
{
if (e.RowIndex >= 0)
currentRow = self.Rows[e.RowIndex];
else
currentRow = null;
}
Then use currentRow in your context menu method.

Categories