I want to put a context sensitive menu on the right-click of a DataGridView. I want to have a single menu item derived from the content of the clicked cell and a variable number of items in a sub-menu, retrieved from a database.
I do this by building the ToolStripMenuItem in the ContextMenuStrip Opening event. And it works - almost...
If I leave the sub-menu undisplayed so the user has to click the single item in the toplevel menu, everything is fine but if I call the ShowDropDown method to display the submenu automatocally, the exents don't fire when the items are clicked on.
Here's the simplest code I can produce to recreate the problem - I've pulled out all the references to the DataGridView and database so my "dynamic" menu is decidedly static ;-)
If you put this is a Form definition, right-click anywhere on the form and you'll see the working but not desired behaviour - click on a sub-menui tem and see a popup. Tick the checkbox and right-click again and you'll see the submenu flies out automatically - but clicking items won't fire the aliasClick handler.
Any thoughts? In this particular application, I can code a perfectly servicable workaround which avoids using ShowDropDown - but I'd like to know what I'm doing wrong in case I need to use it in future.
public partial class Form1 : Form
{
private ContextMenuStrip cms;
private CheckBox chkAuto;
public Form1()
{
InitializeComponent();
chkAuto = new CheckBox();
Controls.Add(chkAuto);
cms = new ContextMenuStrip();
cms.Opening += contextMenuStrip1_Opening;
this.MouseClick += Form1_MouseClick;
}
private void Form1_MouseClick(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Right)
cms.Show(Cursor.Position);
}
private void contextMenuStrip1_Opening(object sender, CancelEventArgs e)
{
cms.Items.Clear();
ToolStripMenuItem tsmi = new ToolStripMenuItem("Title from datagridviewcell");
tsmi.DropDownItems.Add(new ToolStripMenuItem("First item from database lookup", null, aliasClick));
tsmi.DropDownItems.Add(new ToolStripMenuItem("Second item from database lookup", null, aliasClick));
tsmi.DropDownItems.Add(new ToolStripMenuItem("Last item from database lookup", null, aliasClick));
cms.Items.Add(tsmi);
if (chkAuto.Checked)
tsmi.ShowDropDown();
e.Cancel = false;
}
private void aliasClick(object sender, EventArgs e)
{
ToolStripMenuItem clickedItem = (ToolStripMenuItem)sender;
MessageBox.Show(clickedItem.Text);
}
}
I'm not entirely sure how to go about proving this, but I suspect that your call to tsmi.ShowDropDown() is somehow causing the coordinates to not be captured properly by the click handler. Replace it with cms.Show(MousePosition) and it works.
Perhaps some useful info gleaned from a look at the coordinates...
var mi = new ToolStripMenuItem("First item from database lookup", null, aliasClick);
tsmi.DropDownItems.Add(mi);
var mi2 = new ToolStripMenuItem("Second item from database lookup", null, aliasClick);
tsmi.DropDownItems.Add(mi2);
var mi3 = new ToolStripMenuItem("Last item from database lookup", null, aliasClick);
tsmi.DropDownItems.Add(mi3);
cms.Items.Add(tsmi);
if (chkAuto.Checked)
tsmi.ShowDropDown();
//cms.Show(MousePosition);
Debug.WriteLine(mi.Bounds.ToString());
Related
I have 2 project in my solution on Visual Studio. First project (Winforms/Application) has got a notify icon with content menu strip like this:
private void TrayMenuContext()
{
notifyIcon1.ContextMenuStrip = new ContextMenuStrip();
notifyIcon1.ContextMenuStrip.Items.Add("Test1", null, MenuTest1_Click);
notifyIcon1.ContextMenuStrip.Items.Add("Test2", null, MenuTest2_Click);
notifyIcon1.ContextMenuStrip.Items.Add("Exit", null, MenuExit_Click);
}
private void notifyIcon1_MouseClick(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
var mi = typeof(NotifyIcon).GetMethod("ShowContextMenu", BindingFlags.Instance | BindingFlags.NonPublic);
mi?.Invoke(notifyIcon1, null);
}
}
And the second project (Winforms/Application) has got a button like this:
private void button1_Click(object sender, EventArgs e)
{
/*
* How should I code here to delete the notify icon content menu strip item
* in first application named "Test2"?
*/
}
When I click the button, I want to delete the "Test2" menu item in the first application notify icon content menu strip. But unfortunately I don't have the slightest idea about it. Moreover, I do not know if such a thing is possible. However, the extraordinary features of .Net encourage me.
I noticed that by programmatically selecting a Tab in the Tab control selects a control contained in the tab page selected.Is it possible to change this behaviour. I have a control in a tabpage that I do not want to be selected when the its tab page is selected from a button click. I have a simple form with a tab control and two tab pages. When button1 is clicked the tab page 2 is selected but so is the datagridview contained in that tab page.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
dataGridView1.GotFocus += DataGridView1_GotFocus;
}
private void DataGridView1_GotFocus(object sender, EventArgs e)
{
//this event is called from button1_click
}
private void button1_Click(object sender, EventArgs e)
{
tabControl1.SelectedTab = tabPage2;
}
}
By default when you select a tab (or even when you start a form) the control which is the first in your tab order is automatically focused. I am assuming this is what is happening here.
You can solve this by simply unfocusing the datagridView in question. There are multiple ways to do this. Firstly you can set focus to the control that you wish to be selected instead of the dataGridView. This can be done by:
myControl.Focus = True;
Or alternatively if you want non of the controls to be selected you can set the active control to Null:
ActiveControl = NULL;
Note: ActiveControl is a property which contains the current active control.
As to where this code should be placed. That is totally dependent upon you. You can do it as soon as you change the tab in the button click event. This is what I would prefer.
I am sure there are other kludges as well to acheive the same. Hope this helps.
Here is code to select tab
private void button1_Click(object sender, EventArgs e)
{
// we can select tab by tab name
tabControl1.SelectTab("tabPage2");
tabControl1.SelectedIndex = 1;
tabControl1.TabPages[0].Hide();
tabControl1.TabPages[1].Show();
}
I have encountered some problems in the bookmark section for my wpf web-browser. I want to be able to delete already existing buttons but I can't seem to figure out how to detect the content of the button I right click on. (To get my ContextMenu to show).
My visual progress so far: http://puu.sh/6Dxat.png
Adding a context menu to the buttons:
public void button_MouseRightButtonDown(object sender, MouseButtonEventArgs e)//add a context menu to buttons
{
Button button = sender as Button;
menu = new ContextMenu();
menu.Items.Add(new MenuItem() { Header = "Delete" });
button.ContextMenu = menu;
menu.Closed += button_DeleteButtonClicked;//find the right event
}
(I know that the event is wrong, but that is not important right now.)
And the event:
private void button_DeleteButtonClicked(object sender, RoutedEventArgs e)//delete bookmark
{
//This is where I need help. I want the content (which is the URL) of the button
//right clicked onto, for example, show up in a messagebox. How to do?
}
Since you have hooked Close event of context menu here, so sender will be ContextMenu here and you can get button by using PlacementTarget property of ContextMenu.
private void button_DeleteButtonClicked(object sender, RoutedEventArgs e)
{
Button button = ((ContextMenu)sender).PlacementTarget as Button;
}
I have a PictureBox.
If I right click the PictureBox, my ContextMenuStrip (right click menu) appears.
In that ContextMenuStrip is a ToolStripMenuItem (one of the options in the right click menu).
There is an event on the ToolStripMenuItem that handles what happens if that option is clicked.
We're starting from ToolStripMenuItem's "Clicked" function. I need to somehow get back to the PictureBox programmatically. From there, I can modify the PictureBox.
ToolStripMenuItem -> ContextMenuStrip -> PictureBox
How would I go about this?
If the click event handler for your menu item is named OnToolStripMenuItemClick, the following might be an approach to your problem:
private void OnToolStripMenuItemClick(object sender, EventArgs e)
{
var menuItem = sender as ToolStripMenuItem;
var contextMenu = menuItem.Parent as ContextMenuStrip;
var pictureBox = contextMenu.SourceControl;
}
Of course, don't forget to check for null when accessing properties after conversion with as.
I'm not sure that I really understood your problem, but I guess you want to let users can return to the picturebox when they want to cancel current operation by clicking the right button. In this case, you should not implement your work in click event, because right and left button both can trigger the click event, instead, you should process your work in the event "MouseUp", like this:
private void menuItemBack_MouseUp(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
MessageBox.Show("back item is clicked");
}
else
{
MessageBox.Show("I will come back.");
//do your return things here.
}
}
I've just come across this same problem in C#, and the path to the value seems to be something like:
sender.Owner.SourceControl;
However, since sender, Owner and so on are generic classes, I had to cast everything as follows:
PictureBox pb = (PictureBox) ((ContextMenuStrip)((ToolStripMenuItem)sender).Owner).SourceControl;
Ugly, but it works.
With C# I am trying to only show a ContextMenuStrip (CMS) when I right click a specific column in my DataGridView. I am confused as to whether I should be using a DataGridView_CellContentClick and/or dataGridView1.HitTest(). Then to finish off my problem I want to send the data from that right clicked cell to a new form window.
My current code has strange behavior. It will not show the CMS unless I have first left click or right clicked the 4th column. However once I have it will always show the CMS on a right click.
private void dataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex == 4)
{
//Create the ContextStripMenu for Creating the PO Sub Form
ContextMenuStrip Menu = new ContextMenuStrip();
ToolStripMenuItem MenuOpenPO = new ToolStripMenuItem("Open PO");
//Assign event handlers
MenuOpenPO.MouseUp += new MouseEventHandler(MenuOpenPO_Click);
Menu.Items.AddRange(new ToolStripItem[] { MenuOpenPO });
//Assign created context menu strip to the Datagrid
dataGridView1.ContextMenuStrip = Menu;
}
}
void MenuOpenPO_Click(object sender, MouseEventArgs e)
{
var ht = dataGridView1.HitTest(e.X, e.Y);
MessageBox.Show("Hello2");
PO_Form POWindow = new PO_Form();
POWindow.Show();
}
I was going to use the var ht = dataGridView1.HitTest(e.X, e.Y); to grab the cell value.
Any help would be appreciated, thanks!
Edit 1
So I updated dataGridView1_CellContentClick to this and it gets me very close to the behaviour I am looking for. If I first left click in column 4 then right click I get my CMS. If I left click any other cell in another column then right click the CMS is no longer there. However I want to be able to just right click a cell in column 4 without having to left click first to create the CMS.
private void dataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex == 4)
{
//MessageBox.Show("Hello1");
//Create the ContextStripMenu for Creating the PO Sub Form
ContextMenuStrip Menu = new ContextMenuStrip();
ToolStripMenuItem MenuOpenPO = new ToolStripMenuItem("Open PO");
//Assign event handlers
MenuOpenPO.MouseUp += new MouseEventHandler(MenuOpenPO_Click);
Menu.Items.AddRange(new ToolStripItem[] { MenuOpenPO });
//Assign created context menu strip to the Datagrid
dataGridView1.ContextMenuStrip = Menu;
}
else
dataGridView1.ContextMenuStrip = null;
}
I did this in VB ..
Private Sub DGV_CellMouseClick(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellMouseEventArgs) Handles DGV.CellMouseClick
If e.Button = Windows.Forms.MouseButtons.Right Then
DGV.CurrentCell = DGV.Rows(e.RowIndex).Cells(e.ColumnIndex)
CMS.Show(DGV, DGV.PointToClient(Windows.Forms.Cursor.Position))
End If
End Sub
I figured out my own problem and so will post the solution. Unfortunately I am very new at C# programming and really extremely rusty at it in general. I struggled with the concept of Event handlers (not sure if correct name?), how they were being called and how they all vary depending on if you are using MouseEventArgs, EventArgs, KeyEventArgs, etc. Anyway I digress.
My solution is below. I found the using dataGridView1.MouseUp gave me poor user interaction results requireing 2 of each action for a state change. IE create my ContextMenuStrip when the correct coloumn is right Clicked. Or make it disappear if any other column is right clicked. So use dataGridView1.MouseDown for better results.
dataGridView1.MouseDown += new MouseEventHandler(this.dataGridView_MouseDown);
private void dataGridView_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
var ht = dataGridView1.HitTest(e.X, e.Y);
if (ht.ColumnIndex == 4)
{
//Create the ContextStripMenu for Creating the PO Sub Form
ContextMenuStrip Menu = new ContextMenuStrip();
ToolStripMenuItem MenuOpenPO = new ToolStripMenuItem("Open PO");
MenuOpenPO.MouseDown += new MouseEventHandler(MenuOpenPO_Click);
Menu.Items.AddRange(new ToolStripItem[] { MenuOpenPO });
//Assign created context menu strip to the DataGridView
dataGridView1.ContextMenuStrip = Menu;
}
else
dataGridView1.ContextMenuStrip = null;
}
}
If you want to create a new form or do something else I used the following code when clicking your ContextMenuStrip I used the following after creating this event handler? MenuOpenPO.MouseDown += new MouseEventHandler(MenuOpenPO_Click);
void MenuOpenPO_Click(object sender, MouseEventArgs e)
{
PO_Form POWindow = new PO_Form();
POWindow.Show();
}