TreeNode Selection Problems in C# - c#

Whenever I click outside the tree nodes text, on the control part, it tigers a node click event- but doesn't highlight the node. I am unsure why this is happening.
I want the node to be selected on a click- when you click the nodes text- not the whitespace- I only assume that the nodes width reaches across the whole Treenode? I have the Treeview on dock.fill mode if that has something to do with it- I tried everything but can't get it to behave correctly.
Maybe someone will know what's going on.
Update:
if (e.Location.IsEmpty)
{
Seems to work better- but still selects the node in the blank place where there is no text- Obviously the node width extends across the whole treeview it seems?
Is there a better way to accomplish what I want? Or is that the best way?
UPDATE: Previous idea isn't working- sigh- I thought it did it but it didn't.
New Problem : I think part of the problem is related to the focus now when I switch from treeview.
UPDATE-
The only code I came up with about disabling right mouse click to select node on beforeSelect event is
if (MouseButtons == System.Windows.Forms.MouseButtons.Right)
{
e.Cancel = true;
}
But it didn't work- any help is appreciated- following suggestions of only answer, for more details.

You should use the treeView.HitTest method to determine which part of the node has been clicked.
private bool IsClickOnText(TreeView treeView, TreeNode node, Point location)
{
var hitTest = treeView1.HitTest(location);
return hitTest.Node == node
&& hitTest.Location == TreeViewHitTestLocations.Label;
}
private void treeView1_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
{
if(IsClickOnText(treeView1, e.Node, e.Location))
{
MessageBox.Show("click");
}
}
private void treeView1_BeforeSelect(object sender, TreeViewCancelEventArgs e)
{
if (e.Action == TreeViewAction.ByMouse)
{
var position = treeView1.PointToClient(Cursor.Position);
e.Cancel = !IsClickOnText(treeView1, e.Node, position);
}
}

Use the .AfterSelect and/or .BeforeSelect events to handle the selection processing instead of the .Click event. Then it will select the node only when you click on the text, and it won't fire .AfterSelect or .BeforeSelect when you click on the white space.

Related

Setting the ContextMenuStrip for all nodes on a level in a TreeView?

I'm really struggling with coming up with a clever way to set the correct ContextMenuStrip for the the right node. I have a Tree View with 3 levels. And alongside with it I have 3 different ContextMenuStrips that I've created, each for their respected levels. Is there a simple way or trick that anyone's come across for solving this problem?
Also is there a way to have it so right clicking a node makes it the selected node? Or aka does the same thing as a left click.
Sorry for the 2 in 1 but I imagine if someone knows enough on this topic to solve one of my problems there's a good chance they'll know the second as well.
EDIT: I found a solution to my second problem with this line of code:
treeView.NodeMouseClick += (sender, args) => treeView.SelectedNode = args.Node;
Use the NodeMouseClick event to set the ContextMenuStrip property of the selected node:
void treeView1_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e) {
if (e.Button == MouseButtons.Right) {
treeView1.SelectedNode = e.Node;
}
if (e.Node.Level == 0) {
e.Node.ContextMenuStrip = cms1;
} else if (e.Node.Level == 1) {
e.Node.ContextMenuStrip = cms2;
} else if (e.Node.Level == 2) {
e.Node.ContextMenuStrip = cms3;
}
}

How to disable possibility to select particular child of InkCanvas?

I'm using InkCanvas and I'm trying to disable selection for its particular child. InkCanvas children collection consists of Eliipse and Path objects and I want to disable possibility to select all of the Path objects. I was trying to achieve it by checking if mouse hit specific object in PreviewMouseLeftButtonDown event handler:
private void myCanvas_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Point downPosition = e.GetPosition(myCanvas);
UIElement element = myPath;
if (myCanvas.InputHitTest(downPosition) == element) e.Handled = true;
}
Now I know that it won't work because InputHitTest function returns InkCanvas selection adorner, if I click on the object one more time, when it is already selected above function works (at least InputHitTest is returning object that interests me). Do you have any idea how to make this work?
So far what I can see is that you want particular child of inkcanvas not be selected when clicking on canvas
so assuming you have a path called myPath
private void InkCanvas_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Point downPosition = e.GetPosition(myPath);
if (myPath.InputHitTest(downPosition) == myPath)
e.Handled = true;
}
so directly do hit test on the path and see if it return itself then you can determine a click on the path and then by setting e.Handled = true you make the event consumed hence the element will not be selected.
alternate approach will be testing with null, this will have the same behavior as above, so depends on choice
if (myPath.InputHitTest(downPosition) != null)
e.Handled = true;
I propose the above solution based on assumption, you please correct me if this is not what you are looking for.

C#, treeView, How to change standard right-click behavior

I have a question.
I put a treeview control on my form and added some nodes.
public Form1()
{
InitializeComponent();
treeView1.Nodes.Add("root node #1");
treeView1.Nodes.Add("root node #2");
treeView1.Nodes.Add("root node #3");
treeView1.Nodes.Add("root node #4");
treeView1.Nodes.Add("root node #5");
}
I want to change standard right-click behavior. When I right-clicked on a tree Node then Treeview changed for a while selectedIndex. I don't want it. How can I fix standard behavior ?
In ideal, it would be: right click on a treenode text --> context menu appears, right click anywhere outside treenode text --> (absolutely) nothing happens.
private void treeView1_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
// put your logic here like
// ContextMenu1.Show();
}
}
Alex,
Try this. The BeforeSelect handlers event args Cancel is tied to the fact that the right mouse is down. This suppresses the firing of the SelectedIndex changed. The MouseDown tracks if the right mouse is depressed and displays the context menu. The display is safe to move to MouseUp instead of MouseDown. The MouseUp clears the flag indicating that the RightMouse button is depressed.
All of this information on how I did this is available on MSDN. The trick is actually reading the names of all of the events -- Yes I know there are a lot -- then making a list of the "interesting ones" in your case you named SelectedIndex changing, and Mouse clicks. That immediately limits the event names you should read in detail on... If you want the text to not highlight while you're right clicking... well that's a different matter entirely and I caution you against it as it's valuable user feedback.
bool isRBut = false;
private void treeView1_MouseDown(object sender, MouseEventArgs e)
{
isRBut = e.Button == MouseButtons.Right;
if (isRBut)
{
TreeViewHitTestInfo hti =treeView1.HitTest(e.Location);
if (hti.Location == TreeViewHitTestLocations.Label)
contextMenuStrip1.Show(treeView1, new Point(hti.Node.Bounds.Left, hti.Node.Bounds.Bottom));
}
}
private void treeView1_MouseUp(object sender, MouseEventArgs e)
{
isRBut = e.Button == MouseButtons.Right;
}
private void treeView1_BeforeSelect(object sender, TreeViewCancelEventArgs e)
{
e.Cancel = isRBut;
}
Additionally, here's a bit of human language trivia for you. Hopefully this will help you in the future. Phrases such as "No, no, no" are interpreted by native English speakers as very rude. Just do your best to list the behavior you see, and the behavior you want. Even if people misunderstand stick to the facts only and leave out the obvious signs of frustration. This will help you get what you're after. As well on SO if someone has a habit of not accepting answers many members here will have a habit of not providing future answers to such members.
Override the MouseClick event, and in the event check if the click was right click
private void treeView1_MouseClick(object sender, TreeNodeMouseClickEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
//Do something
}
}
You need to handle the NodeMouseClick event and check the right mouse button was clicked:
treeView1.NodeMouseClick += (o, e) => {
if(e.Button == MouseButtons.Right)
{
//show menu...
}
};

How can i find selected node of treeview when click right button

I m working on windows project and using c#. I want to catch treeview selected node which i click that by right click.
I'm writing tvlocation.SelectedNode.Index
but it return only Root Node's index.
Thanks for your helps...
If you are looking to identify the node that was clicked on, then handle the NodeMouseClick event, as follows:
private void treeView1_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
MessageBox.Show(string.Format("Node clicked: {0}", e.Node.Text));
}
}
You could select the node programatically here, if you need that too.

Find node clicked under context menu

How can I find out which node in a tree list the context menu has been activated? For instance right-clicking a node and selecting an option from the menu.
I can't use the TreeViews' SelectedNode property because the node is only been right-clicked and not selected.
You can add a mouse click event to the TreeView, then select the correct node using GetNodeAt given the mouse coordinates provided by the MouseEventArgs.
void treeView1MouseUp(object sender, MouseEventArgs e)
{
if(e.Button == MouseButtons.Right)
{
// Select the clicked node
treeView1.SelectedNode = treeView1.GetNodeAt(e.X, e.Y);
if(treeView1.SelectedNode != null)
{
myContextMenuStrip.Show(treeView1, e.Location);
}
}
}
Here is my solution. Put this line into NodeMouseClick event of the TreeView:
((TreeView)sender).SelectedNode = e.Node;
I find the standard windows treeview behavior selection behavior to be quite annoying. For example, if you are using Explorer and right click on a node and hit Properties, it highlights the node and shows the properties dialog for the node you clicked on. But when you return from the dialog, the highlighted node was the node previously selected/highlighted before you did the right-click. I find this causes usability problems because I am forever being confused on whether I acted on the right node.
So in many of our GUIs, we change the selected tree node on a right-click so that there is no confusion. This may not be the same as a standard iwndos app like Explorer (and I tend to strongly model our GUI behavior after standard window apps for usabiltiy reasons), I believe that this one exception case results in far more usable trees.
Here is some code that changes the selection during the right click:
private void tree_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e)
{
// only need to change selected note during right-click - otherwise tree does
// fine by itself
if ( e.Button == MouseButtons.Right )
{
Point pt = new Point( e.X, e.Y );
tree.PointToClient( pt );
TreeNode Node = tree.GetNodeAt( pt );
if ( Node != null )
{
if ( Node.Bounds.Contains( pt ) )
{
tree.SelectedNode = Node;
ResetContextMenu();
contextMenuTree.Show( tree, pt );
}
}
}
}
Reviving this question because I find this to be a much better solution.
I use the NodeMouseClick event instead.
void treeview_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
{
if( e.Button == MouseButtons.Right )
{
tree.SelectedNode = e.Node;
}
}
This is a very old question, but I still found it useful. I am using a combination of some of the answers above, because I don't want the right-clicked node to become the selectedNode. If I have the root node selected and want to delete one of it's children, I don't want the child selected when I delete it (I am also doing some work on the selectedNode that I don't want to happen on a right-click). Here is my contribution:
// Global Private Variable to hold right-clicked Node
private TreeNode _currentNode = new TreeNode();
// Set Global Variable to the Node that was right-clicked
private void treeView_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Right)
_currentNode = e.Node;
}
// Do something when the Menu Item is clicked using the _currentNode
private void toolStripMenuItem_Clicked(object sender, EventArgs e)
{
if (_currentNode != null)
MessageBox.Show(_currentNode.Text);
}
Similar to Marcus' answer, this was the solution I found worked for me:
private void treeView_MouseClick(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
treeView.SelectedNode = treeView.GetNodeAt(e.Location);
}
}
You need not show the context menu yourself if you set it to each individual node like so:
TreeNode node = new TreeNode();
node.ContextMenuStrip = contextMenu;
Then inside the ContextMenu's Opening event, the TreeView.SelectedNode property will reflect the correct node.
If you want the context menu to be dependent on the selected item you're best move I think is to use Jonesinator's code to select the clicked item. Your context menu content can then be dependent on the selected item.
Selecting the item first as opposed to just using it for the context menu gives a few advantages. The first is that the user has a visual indication as to which he clicked and thus which item the menu is associated with. The second is that this way it's a hell of a lot easier to keep compatible with other methods of invoking the context menu (e.g. keyboard shortcuts).
Here is how I do it.
private void treeView_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Right)
e.Node.TreeView.SelectedNode = e.Node;
}
Another option you could run with is to have a global variable that has the selected node. You would just need to use the TreeNodeMouseClickEventArgs.
public void treeNode_Click(object sender, TreeNodeMouseClickEventArgs e)
{
_globalVariable = e.Node;
}
Now you have access to that node and it's properties.
I would like to propose an alternative to using the click events, using the context menu's Opened event:
private void Handle_ContextMenu_Opened(object sender, EventArgs e)
{
TreeViewHitTestInfo info = treeview.HitTest(treeview.PointToClient(Cursor.Position));
TreeNode contextNode;
// was there a node where the context menu was opened?
if (info != null && info.Node != null)
{
contextNode = info.Node;
}
// Set the enabled states of the context menu elements
menuEdit.Enabled = contextNode != null;
menuDelete.Enabled = contextNode != null;
}
This has the following advantages that I can see:
It does not change the selected node
No separate event handler needed to store the target node instance
Can disable menu items if the user right-clicks empty space in the TreeView
Note: if you worry that the user may have already moved the mouse by the time the menu is opened, it is possible to use the Opening event instead.

Categories

Resources