How can I add a context menu to a ListBoxItem? - c#

I have a ListBox and I want to add a context menu to each item in the list. I've seen the "solution" to have the right click select an item and suppress the context menu if on white space, but this solution feels dirty.
Does anyone know a better way?

Just to elaborate a little further to what Frans has said...Even though the ListBox owns the ContextMenuStrip, you can still customize the items in the menu strip at the time it's opening. Thus customizing it's contents based on the mouse position within the listbox.
The example below selects the item in the listbox based on a right mouse click and then customizes a context menu strip based on the item the user right-clicked on. This is a simple example but should get you going: Add a listbox to a form and add this code:
#region Private Members
private ContextMenuStrip listboxContextMenu;
#endregion
private void Form1_Load( object sender, EventArgs e )
{
//assign a contextmenustrip
listboxContextMenu = new ContextMenuStrip();
listboxContextMenu.Opening +=new CancelEventHandler(listboxContextMenu_Opening);
listBox1.ContextMenuStrip = listboxContextMenu;
//load a listbox
for ( int i = 0; i < 100; i++ )
{
listBox1.Items.Add( "Item: " + i );
}
}
private void listBox1_MouseDown( object sender, MouseEventArgs e )
{
if ( e.Button == MouseButtons.Right )
{
//select the item under the mouse pointer
listBox1.SelectedIndex = listBox1.IndexFromPoint( e.Location );
if ( listBox1.SelectedIndex != -1)
{
listboxContextMenu.Show();
}
}
}
private void listboxContextMenu_Opening( object sender, CancelEventArgs e )
{
//clear the menu and add custom items
listboxContextMenu.Items.Clear();
listboxContextMenu.Items.Add( string.Format( "Edit - {0}", listBox1.SelectedItem.ToString() ) );
}
Hope that help.

This way the menu will pop up next to the mouse
private string _selectedMenuItem;
private readonly ContextMenuStrip collectionRoundMenuStrip;
public Form1()
{
var toolStripMenuItem1 = new ToolStripMenuItem {Text = "Copy CR Name"};
toolStripMenuItem1.Click += toolStripMenuItem1_Click;
var toolStripMenuItem2 = new ToolStripMenuItem {Text = "Get information on CR"};
toolStripMenuItem2.Click += toolStripMenuItem2_Click;
collectionRoundMenuStrip = new ContextMenuStrip();
collectionRoundMenuStrip.Items.AddRange(new ToolStripItem[] {toolStripMenuItem1, toolStripMenuItem2 });
listBoxCollectionRounds.MouseDown += listBoxCollectionRounds_MouseDown;
}
private void toolStripMenuItem2_Click(object sender, EventArgs e)
{
var info = GetInfoByName(_selectedMenuItem);
MessageBox.Show(info.Name + Environment.NewLine + info.Date);
}
private void toolStripMenuItem1_Click(object sender, EventArgs e)
{
Clipboard.SetText(_selectedMenuItem);
}
private void myListBox_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button != MouseButtons.Right) return;
var index = myListBox.IndexFromPoint(e.Location);
if (index != ListBox.NoMatches)
{
_selectedMenuItem = listBoxCollectionRounds.Items[index].ToString();
collectionRoundMenuStrip.Show(Cursor.Position);
collectionRoundMenuStrip.Visible = true;
}
else
{
collectionRoundMenuStrip.Visible = false;
}
}

There's no other way: the context menu isn't owned by the item in the listbox but by the listbox itself. It's similar to the treeview control which also owns the context menu instead of the treenode. So whenever an item in the listbox is selected, set the context menu of the listbox according to the selected item.

And Here it is My Solution :
listBox_Usernames.ContextMenuStrip = contextMenuStripRemove;
listBox_Usernames.MouseUp += new MouseEventHandler(listBox_Usernames_MouseUp);
void listBox_Usernames_MouseUp(object sender, MouseEventArgs e)
{
int index = listBox_Usernames.IndexFromPoint(e.Location);
if (e.Button == System.Windows.Forms.MouseButtons.Right)
{
if (index != ListBox.NoMatches)
{
if (listBox_Usernames.SelectedIndex == index)
{
listBox_Usernames.ContextMenuStrip.Visible = true;
}
else
{
listBox_Usernames.ContextMenuStrip.Visible = false;
}
}
else
{
listBox_Usernames.ContextMenuStrip.Visible = false;
}
}
else
{
listBox_Usernames.ContextMenuStrip.Visible = false;
}
}

In XAML it shows like this:
<ListBox>
<ListBox.ContextMenu>
<ContextMenu>
<MenuItem Header="Add User..."/>
<MenuItem Header="Edit..."/>
<MenuItem Header="Disable"/>
</ContextMenu>
</ListBox.ContextMenu>
</ListBox>

In one line of code (more for binding):
var datasource = new BindingList<string>( new List<string>( new string[] { "item1" } ) );
listbox.DataSource = datasource ;
listbox.ContextMenu = new ContextMenu(
new MenuItem[] {
new MenuItem("Delete",
new EventHandler( (s,ev) =>
datasource.Remove(listbox.SelectedItem.ToString())
)
)
});
private void buttonAdd_Click(object sender, EventArgs e)
{
datasource.Add( textBox.Text );
}

If its just a question of enabling or disabling context menu items, it might be more efficient to only do it when the context menu is launched rather than every time the list box selection changes:
myListBox.ContextMenu.Popup += new EventHandler(myContextPopupHandler);
private void myContextPopupHandler(Object sender, System.EventArgs e)
{
if (SelectedItem != null)
{
ContextMenu.MenuItems[1].Enabled = true;
ContextMenu.MenuItems[2].Enabled = true;
}
else
{
ContextMenu.MenuItems[1].Enabled = false;
ContextMenu.MenuItems[2].Enabled = false;
}
}

this one is best...
using System.Windows.Forms;
ContextMenuStrip menu;
this.menu.Items.AddRange(new ToolStripItem[] { this.menuItem });
this.listBox.MouseUp += new MouseEventHandler(this.mouse_RightClick);
private void mouse_RightClick(object sender, MouseEventArgs e)
{
int index = this.listBox.IndexFromPoint(e.Location);
if (index != ListBox.NoMatches)
{
menu.Visible = true;
}
else
{
menu.Visible = false;
}
}

//Create and Initialize the contextMenuStrip component
contextMenuStrip_ListaAulas = new ContextMenuStrip();
//Adding an Item
contextMenuStrip_ListaAulas.Items.Add("Modificar");
//Binding the contextMenuStrip with the ListBox
listBox_Aulas.ContextMenuStrip = contextMenuStrip_ListaAulas;
//The solution below
if (e.Button == System.Windows.Forms.MouseButtons.Right)
{
//select the item under the mouse pointer
listBox_Aulas.SelectedIndex = listBox_Aulas.IndexFromPoint(e.Location);
//if the selected index is an item, binding the context MenuStrip with the listBox
if (listBox_Aulas.SelectedIndex != -1)
{
listBox_Aulas.ContextMenuStrip = contextMenuStrip_ListaAulas;
}
//else, untie the contextMenuStrip to the listBox
else
{
listBox_Aulas.ContextMenuStrip = null;
}
}

I do like this, this works great and fast for me.
private void contextMenuStrip1_Opening(object sender, CancelEventArgs e)
{
if (Listbox.SelectedItem == null)
e.Cancel = true;
}

Related

Combobox passed as a Control template then as a popup

I have a combobox "recent_users" as a control template and then as a poppup. How can I pass the selected value of the popup to the method below?(popup click)
recent_users.SelectedItem.ToString() always returns null.
private void usernameEnter_PreviewMouseUp(object sender, MouseButtonEventArgs e)
{
Trace.WriteLine("testing dropdown");
ControlTemplate ct = recent_users.Template;
Popup popup1 = ct.FindName("PART_Popup", recent_users) as Popup;
if (popup1 != null)
{
popup1.Placement = PlacementMode.Top;
}
recent_users.IsDropDownOpen = true;
popup1.PreviewMouseUp += new MouseButtonEventHandler((s,e) => popupClick(s,e,recent_users.SelectedItem.ToString()));
}
private void popupClick(object sender, MouseButtonEventArgs e, String recent)
{
usernameEnter.Text = recent;
Trace.WriteLine("appending norms");
}
}
}
Isn't it because the user has not made any selection in the ComboBox? I have re-written the code slightly
private void usernameEnter_PreviewMouseUp(object sender, MouseButtonEventArgs e)
{
ControlTemplate ct = recent_users.Template;
Popup popup1 = ct.FindName("PART_Popup", recent_users) as Popup;
if (popup1 != null)
{
popup1.Placement = PlacementMode.Top;
}
recent_users.IsDropDownOpen = true;
// if none selected, set first as selected
if (recent_users.SelectedIndex < 0 && recent_users.HasItems)
{
recent_users.SelectedIndex = 0;
}
var selectedItem = recent_users.SelectedItem as ComboBoxItem;
var selectedUser = selectedItem.Content.ToString();
popup1.PreviewMouseUp += new MouseButtonEventHandler((s, e) => popupClick(s, e, selectedUser));
}
But I was wondering adding a handler for PreviewMouseUp event (in the last line) is something that you really want. I would have fetched the selected user in the SelectionChanged of the recent_users rather.

How to re-enable listbox when removing a specified item from an output listbox

I am trying to re-enable only the listbox that pertains to the item that has been removed from the output listbox. For example, If I select "Wagon" from Body Type listbox and "Advance" from Package listbox, the listbox named "lstOutPut" displays the following:
What I want to do is, if I were to remove "SUV", I would only want for Body Type listbox re-enabled and not the rest
Here is my code
private void lstBody_SelectedIndexChanged(object sender, EventArgs e)
{
if (lstBody.SelectedItem != null)
{
lstOutput.Items.Add(lstBody.SelectedItem);
lstBody.SelectionMode = SelectionMode.None;
lstBody.Enabled = false;
}
}
private void lstPackage_SelectedIndexChanged(object sender, EventArgs e)
{
if (lstPackage.SelectedItem != null)
{
lstOutput.Items.Add(lstPackage.SelectedItem);
lstPackage.SelectionMode = SelectionMode.None;
lstPackage.Enabled = false;
}
}
And this is what I have for the remove button
private void btnRemove_Click(object sender, EventArgs e)
{
//remove selected item only
while (lstOutput.SelectedItems.Count > 0)
{
lstOutput.Items.Remove(lstOutput.SelectedItems[0]);
}
lstBody.Enabled = true;
lstPackage.Enabled = true;
}
Any ideas?
Thanks.
Assuming there would not be duplicates values among the different input listboxes,you could do the following in the btnRemove_Click event
var inputListBoxes = new[] { lstPackage, lstBody };
while (lstOutput.SelectedItems.Count > 0)
{
inputListBoxes.First(x => x.Items.Contains(lstOutput.SelectedItems[0])).Enabled = true;
lstOutput.Items.Remove(lstOutput.SelectedItems[0]);
}
For each listbox, enable it if non of its items exist in lstOutput:
var listBoxes = new ListBox[] {body, package, wheel, accessories};
foreach(var lst in listBoxes)
lst.Enabled = !lst.Items.Cast<string>().Any(x => lstOutput.Items.Contains(x));

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.

ListBoxItem index on ListBox mouseover

I have a ListBox in a WPF application that has a MouseMove event handler attached. What I would like to do is to use this event to get the index of the item the mouse is over.
Simplified example of my code:
<StackPanel>
<ListBox x:Name="MyList" MouseMove="OnMouseMove"/>
<Separator/>
<Button>Beep</Button>
</StackPanel>
public CodeBehindConstructor()
{
List<string> list = new List<string>();
list.Add("Hello");
list.Add("World");
list.Add("World"); //Added because my data does have duplicates like this
MyList.ItemsSource = list;
}
public void OnMouseMove(object sender, MouseEventArgs e)
{
//Code to find the item the mouse is over
}
I would try using ViusalHelper HitTest method for that, something like this :
private void listBox_MouseMove(object sender, MouseEventArgs e)
{
var item = VisualTreeHelper.HitTest(listBox, Mouse.GetPosition(listBox)).VisualHit;
// find ListViewItem (or null)
while (item != null && !(item is ListBoxItem))
item = VisualTreeHelper.GetParent(item);
if (item != null)
{
int i = listBox.Items.IndexOf(((ListBoxItem)item).DataContext);
label.Content = string.Format("I'm on item {0}", i);
}
}
Try this:
public void OnMouseMove(object sender, MouseEventArgs e)
{
int currentindex;
var result = sender as ListBoxItem;
for (int i = 0; i < lb.Items.Count; i++)
{
if ((MyList.Items[i] as ListBoxItem).Content.ToString().Equals(result.Content.ToString()))
{
currentindex = i;
break;
}
}
}
You can also try this much shorter option:
public void OnMouseMove(object sender, MouseEventArgs e)
{
int currentindex = MyList.Items.IndexOf(sender) ;
}
However I'm not too sure whether it will work with your method of binding.
Option 3:
A little hacky but you could get the point value of the current location and then use IndexFromPoint
E.g:
public void OnMouseMove(object sender, MouseEventArgs e)
{
//Create a variable to hold the Point value of the current Location
Point pt = new Point(e.Location);
//Retrieve the index of the ListBox item at the current location.
int CurrentItemIndex = lstPosts.IndexFromPoint(pt);
}

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

Categories