I have treeview and a context menu that is shown for every node.
One node has the selection. I move now with the mouse to another node and open the context menu with a righ-mouse-click. Now there is the problem, that the selection is still on the old new node.
How can I prevent, that the menu pops up, if the user haven't selected the node before by a mouse click selection ? In other words how can I achieve that the user must select the treenode before by a normal mouse click or the treenode gets the selection with the right mouse click automatically.
Try the following code, which provides you a preselection of the treenode.
TreeNode treeNodeAtMousePosition = this.treeView1.GetNodeAt(this.treeView1.PointToClient(Control.MousePosition));
TreeNode selectedTreeNode = this.treeView1.SelectedNode;
if (treeNodeAtMousePosition != null)
{
if (treeNodeAtMousePosition != selectedTreeNode)
treeView1.SelectedNode = treeNodeAtMousePosition;
}
The context menu has an event:
http://msdn.microsoft.com/en-us/library/ms229721.aspx
This is a cancellable event. In other words, test to see if you have a selected node and cancel the event if you don't - it will stop your menu from showing.
I created the context menu and assigned it to the TreeView control in the designer. Then I add the following code to the form
private TreeNode _rightclickedNode;
private void treeView1_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e) {
if (e.Button == MouseButtons.Right) {
_rightclickedNode = e.Node;
}
}
private void copyAsPathToolStripMenuItem1_Click(object sender, EventArgs e) {
Clipboard.SetText(_rightclickedNode.FullPath);
}
Related
I've read a few articles on SO:
How to detrmine the control that cause ContextMenuStrip
Getting the control of a context menu
and a couple others that suggested use of the SourceControl property.. but none work in this context:
I have a ContextMenuStrip that has a child ToolStripMenuItem - this code from the windows forms designer generated section:
// _tileContextMenuStrip
//
this._tileContextMenuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.tileKindToolStripMenuItem,
this.forceWidthScalingToolStripMenuItem,
this.forceHeightScalingToolStripMenuItem});
this._tileContextMenuStrip.Name = "_tileContextMenuStrip";
this._tileContextMenuStrip.Size = new System.Drawing.Size(184, 70);
//
// tileKindToolStripMenuItem
//
this.tileKindToolStripMenuItem.Name = "tileKindToolStripMenuItem";
this.tileKindToolStripMenuItem.Size = new System.Drawing.Size(183, 22);
this.tileKindToolStripMenuItem.Text = "Tile Kind";
So the context menu strip and the menu item first in the list are fixed at design time. At runtime, the TSMI has child TSMIs added to it in a loop based on an enum:
foreach(TileKind t in typeof(TileKind).GetEnumValues()) {
ToolStripMenuItem tsmi = new ToolStripMenuItem(t.ToString("g"));
tsmi.Tag = t;
tsmi.Click += tsmi_Click;
tileKindToolStripMenuItem.DropDownItems.Add(tsmi);
}
Later I have 20 checkboxes on my form and I set their .ContextMenuStrip to be the same thing:
foreach(Thing t in someDataSource){
CheckBox c = new CheckBox();
c.Text = t.SomeData;
c.ContextMenuStrip = this._tileContextMenuStrip;
myPanelBlah.Controls.Add(c);
}
Great, so now I have all my checkboxes and they all show the context menu when I right click them, but when I choose one the sub-menu items, I just can't find out the control that fired the context menu...
//this the click handler for all the menu items dynamically added
void tsmi_Click(object sender, EventArgs e)
{
ToolStripMenuItem tsmi = sender as ToolStripMenuItem;
(tsmi.OwnerItem //the parent node in the menu tree hierarchy
.Owner as ContextMenuStrip) //it's a ContextMenuStrip so the cast succeeds
.SourceControl //it's always null :(
}
I can reliably get ahold of the contextmenustrip either by routing up from the event handler sender, or even just by referencing the ContextMenuStrip itself as a form instance variable, but SourceControl is always null
Any ideas what to try next?
I see the problem, quacks loudly like a bug. There's a workaround, you can subscribe the ContextMenuStrip's Opening event. At that point, well before you start navigating into the sub-items, the SourceControl property is still valid. So store it in a field of the class so you'll have it available in the Click event handler. Roughly:
private Control _tileCmsSource;
private void _tileContextMenuStrip_Opening(object sender, CancelEventArgs e) {
_tileCmsSource = _tileContextMenuStrip.SourceControl;
}
void tsmi_Click(object sender, EventArgs e)
{
ToolStripMenuItem tsmi = sender as ToolStripMenuItem;
// Use _tileCmsSource here
//...
}
hello I'm grabbing a windows form application in c # and have a question about a context menu I have my main form within the main form I have a picturebox and I created an event as the next mouse click
private void pictureBox1_MouseClick(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
ContextMenu cm = new ContextMenu();
cm.MenuItems.Add("DISPONIBLE");
cm.MenuItems.Add("RESERVAR");
cm.MenuItems.Add("OCUPADA");
pictureBox1.ContextMenu = cm;
}
}
all going well so far shows me my contextual menu when I right click but when I choose the option to "AVAILABLE" another windows form show me someone who can help me please thanks
I think this is not the way. if you did so you need to call show for the context menu, you can set that menu at the initialization of the form or the control, you create the context menu one time it will be shown automatically, you don't have to create it each time you click the control, Add the following code to the constructor of the form just to check if it works
ContextMenu cm = new ContextMenu();
cm.MenuItems.Add("DISPONIBLE");
cm.MenuItems.Add("RESERVAR");
cm.MenuItems.Add("OCUPADA");
pictureBox1.ContextMenu = cm;
I have been implementing basic drag and drop functionality using WPF and C# quite successfully for some time now. I have always had one problem after implementing it though... for some reason, the drag and drop functionality stops the ListBoxItems from becoming selected (on the first click).
If I click on a ListBoxItem but don't drag it, it doesn't get selected and the drag icon appears momentarily. On the next click I can then select any of the ListBoxItems and the drag icon does not appear. This cycle then repeats... first click won't select, second one will.
Below is a typical implementation of my drag and drop code, taken from the Micorsoft MCTS 70-511 'Training kit' book.
private void ListBox_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
object data = (ListBoxItem)(FrameworkElement)sender;
if (data != null) DragDrop.DoDragDrop(ListBox, data, DragDropEffects.Copy);
e.Handled = false;
}
private void ListBox_DragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(typeof(ListBoxItem))) e.Effects = DragDropEffects.Copy;
}
private void ListBox_Drop(object sender, DragEventArgs e)
{
object data = e.Data.GetData(typeof(ListBoxItem));
if (data != null) DoSomethingWith((DataType)((ListBoxItem)data).DataContext);
}
The drag and drop works fine, but the item selection doesn't... I presumed that by adding e.Handled = false in the ListBox_PreviewMouseLeftButtonDown handler, the ListBoxItem selection mechanism could handle the click event, but it never reaches that far.
I also tried handling the drag initiation in the MouseLeftButtonDown handler instaed of the PreviewMouseLeftButtonDown handler, but the ListBoxItem selection mechanism handles the click event and it never reached that drag drop handler.
There must be a way to initiate the drag drop operation and still have the ListBoxItem that was clicked on become selected, but I still haven't managed to find it... any clues anyone?
UPDATE >>>
Thanks to the MSDN article #icebat provided a link for, I've managed to get the drag and drop functionality working perfectly. It is now as follows:
private void SourceListBox_MouseMove(object sender, MouseButtonEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
object data = ((ListBox)(FrameworkElement)sender).SelectedItem;
if (data != null)
DragDrop.DoDragDrop(SourceListBox, data, DragDropEffects.Copy);
}
}
private void TargetListBox_DragOver(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(typeof(DragObject))) e.Effects = DragDropEffects.Copy;
}
private void TargetListBox_Drop(object sender, DragEventArgs e)
{
object data = e.Data.GetData(typeof(DragObject));
if (data != null) DoSomethingWith((DragObject)data);
}
Just use MouseMove event instead of MouseDown for drag and drop. You can find more information and some code in the Drag and Drop article on MSDN.
I have a ListBox and want the selection-mode to be extended. Also I want have to implement drag and drop functionality. The problem now is, that if the mouse is clicked on a selected item, it will be immediately be selected as single selection instead of waiting to the mouse-up-event for doing this.
Due to this behaviour, start dragging multiple items is for the user quasi impossible because always he clicks on the selection to start dragging, the selection changes to the item that is under the mouse and starts the drag-operation with this item.
Is there a good workaround for this problem or does even exists an official solution?
Here's what I've done. In your DragDrop code, subscribe to the PreviewMouseLeftButtonDown. If the item you are already clicking on is selected, then set e.Handled to true.
In my sample below, I identify a part of the list box item as a drag grip (with bumps) so that I can distinguish between the item and a drag surface. I just had to get the list box item data template and the drag and drop behavior to agree on a name of the drag grip element.
The PreviewMouseLeftButtonDown from my work in progress:
private void ItemsControl_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
dragStartPoint = e.GetPosition(null);
ItemsControl itemsControl = this.AssociatedObject as ItemsControl;
if (itemsControl != null)
{
this.sourceItemContainer = itemsControl.ContainerFromElement((Visual)e.OriginalSource) as FrameworkElement;
}
// If this is an multiple or extended selection list box, and on a drag grip, then ensure the item being hit is selected
// This prevents the ItemsControl from using this MouseDown to change selection, except over a selected item's drag grip.
if ((this.IsMultipleSelectionListBox() == true) && (this.IsOriginalSourceDragGrip(e) != false) && (this.IsSourceListBoxItemSelected() == true))
{
e.Handled = true;
}
}
The easiest workaround i can think of would be to change the ListBoxItem to select on MouseUp not Down like so and change the ContainerGenerator to serve your custom ListBoxItems:
public class CustomListBoxItem : ListBoxItem
{
protected override void OnMouseLeftButtonDown( MouseButtonEventArgs e )
{
//do nothing
}
protected override void OnMouseLeftButtonUp( MouseButtonEventArgs e )
{
base.OnMouseLeftButtonDown( e );
}
}
You might need some MouseLeave/LeftButtonDown logic if you want to prevent different items selecting when moving through the List while holding the mouse button down.
Use PreviewMouseLeftButtonDown to add the selected items for the drag operation.
I creating a custonmized box class (inherits from ComboBox). I don't want the text box to react to right mouse clicks. I can get rid of the context menu by setting this to null in ApplyTemplate, but right mouse clicks move the cursor. I tried hooking up PreviewMouseRightButtonDown in ApplyTemplate and setting Handled to True, but the event still gets through which is strange as it seems to work for the left click.
The cursor actually moves when the mouse button is released, so you want mark the MouseRightButtonUp event as handled. You could override OnMouseRightButtonUp:
protected override void OnMouseRightButtonUp(MouseButtonEventArgs e)
{
base.OnMouseRightButtonUp(e);
e.Handled = true;
}
Or you could attach a class handler to the MouseRightButtonUp event to mark it as handled:
static MyComboBox()
{
EventManager.RegisterClassHandler(
typeof(MyComboBox),
MouseRightButtonUpEvent,
new MouseButtonEventHandler(MyComboBox_MouseRightButtonUp));
}
private static void MyComboBox_MouseRightButtonUp(
object sender, MouseButtonEventArgs e)
{
e.Handled = true;
}
That will also prevent the context menu from being created without you having to set it to null explicitly.