I am moving a TreeNode in a Windows Forms TreeView control as a consequence of a DragDrop event:
void treeViewDragDrop(object sender, DragEventArgs e)
{
// ...
sourceNode.Remove();
targetNode.Nodes.Add(sourceNode);
}
However, the Remove call triggers the AfterSelect event of the TreeView, which I need to react to if the selection really changes.
But in this case, the apparent selection change is only a result of temporarily removing the node. I don't want the selection to change at all after the node has finally moved to its target location in the tree.
So what would be the correct way to deal with that situation, i.e. how to suppress selection changes due to moving nodes?
If you do not set selection programmatically, you can cancel the selection in BeforeSelect event if the action is not user-generated:
private void TreeView1_BeforeSelect(object sender, TreeViewCancelEventArgs e)
{
if (e.Action == TreeViewAction.Unknown)
e.Cancel=true;
}
If you change selection programmatically, you may use a simple workaround by extending the TreeView class and setting a sort of "flag":
public class MyTreeViwe : TreeView
{
public bool IsSuspendSelection { get; set; }
protected override void OnBeforeSelect(TreeViewCancelEventArgs e)
{
if (IsSuspendSelection)
e.Cancel = true;
else base.OnBeforeSelect(e);
}
}
then do:
var tv = sourceNode.TreeView;
tv.IsSuspendSelection=true;
sourceNode.Remove();
tv.IsSuspendSelection=false;
You could also unsubscribe and then subscribe again to the AfterSelect event on sourceNode.TreeView, but I personally find this method accident-prone. There's no way to know if a given delegate is currently subscribing to a given event, and how many times. You may end up subscribing to the event multiple times and then your AfterSelect delegate method will run more than once.
Related
I have an event that fires using the AfterSelect Event, and this works just fine, but I do not want this event to fire if the user either checks or unchecks the checkbox of this or any node in the tree. Alternatively, the event can fire, but then I need to know inside the event if it was fired as a result of a check or uncheck of the checkbox. I have tried using NodeMouseClick instead but I get the same unwanted result, and I have also tried removing the event handler using BeforeCheck and adding it back using AfterCheck, like so:
private void MyTree_BeforeCheck(object sender, TreeViewCancelEventArgs e)
{
this.MyTree.AfterSelect -= new System.Windows.Forms.TreeViewEventHandler(this.MyTree_AfterSelect);
}
private void MyTree_AfterCheck(object sender, TreeViewEventArgs e)
{
this.MyTree.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.MyTree_AfterSelect);
}
private void MyTree_AfterSelect(object sender, TreeViewEventArgs e)
{
ShowMyInfo(e.Node);
}
but the AfterSelect event still fires.
I cannot rely on AfterCheck because then it won't fire if you simply select the node. I also cannot rely on checking if e.Node.Selected == true, because the node might already be checked, and I want the event to fire if the node is in fact deliberately selected.
Is there any event handler I can use that will trigger only if a node is SELECTED, and not CHECKED or UNCHECKED, or any way I can detect if a node that's just been selected has been checked or unchecked.
Using Visual Studio 2019 with a C# WinForms .NET Framework application.
As #Jimi and #dr.null have pointed out, I am using the right event after all. AfterSelect will not fire just because you checked or unchecked the checkbox.
The problem that I had was that I have code that will select a node if you right click it (right clicking does not automatically select a node).
Old Code:
private void MyTree_MouseDown(object sender, MouseEventArgs e)
{
MyTree.SelectedNode = MyTree.GetNodeAt(e.X, e.Y);
}
New Code:
private void MyTree_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right) MyTree.SelectedNode = MyTree.GetNodeAt(e.X, e.Y);
}
I was inadvertently firing off my own event because I did not check for which mouse button was clicked on the MouseDown event. Thank you for the help!
I am using the Windows Forms TreeView control.
The way i have it hooked up is as followed (simplified):
TreeView treeView = new TreeView();
treeView.BeforeSelect += beforeSelect;
private void beforeSelect(sender, args)
{
MessageBox.Show("Some msg");
// more code
}
In certain scenarios, the call to MessageBox.Show triggers another raising of the BeforeSelect event, which triggers another, and another, ...
It seems this event is raised PER ITEM in the treeview (i have counted the number of times it is raised).
I have searched all over the internet on more information for why this could occur.
One thing i've found was that TreeView will automatically select the first node when gaining focus.
This does not explain however why the event is fired as the number of treenode items in the tree.
Any help would be appreciated on this. I am considering raising a Microsoft Connect bug for this, as it seems like a very weird behavior that is not consistent with how i think the control should work.
Would it be enough to simply block yourself like the following?
private bool _inside;
private void beforeSelect( object sender, EventArgs args )
{
if ( !_inside )
{
_inside = true;
MessageBox.Show("Some msg");
// more code
_inside = false;
}
}
This would disallow "recursive" calls of your function.
the BeforeSelect event isn't fired multiple times by default.
when you select a node, you show a dialog(here messagebox) which interrupts the selection event or task however and after you close the dialog the selection event fires again based on the interruption. You should use AfterSelect event of the treeview to do things... and BeforeSelect only for validation..
Please look at this code - run it
void treeView1_BeforeSelect(object sender, TreeViewCancelEventArgs e)
{
e.Node.Tag = (int)(e.Node.Tag ?? 0) + 1;
int count = (int)(e.Node.Tag);
e.Node.Text = String.Format("selected {0} Count: {1}", e.Action.ToString(), count);
}
When you define an object,you should write like this;
True Write:
private static TreeView projectagac;
...
...
...
projectagac = new TreeView();
thus you will create only one object.
I'm creating listviews in a flowpanel at run time which later will accept drag and dropped files. the reason being is i want these to act as folders so a user double clicks and gets a window displaying the contents.
i'm having difficulty setting up the events for my listviews as they are added.
how do i create some events (like MouseDoubleClick and DragDrop) dynamically for each added listview? can i create a single function for both of these events and have listview1, listview2, listviewX use it?
i have a button that is adding the listviews, which works fine. please advise, i apologize if this is too conceptual and not exact enough.
private void addNewWOButton_Click(object sender, EventArgs e)
{
ListView newListView = new ListView();
newListView.AllowDrop = true;
flowPanel.Controls.Add(newListView);
}
You would have to have the routine already created in your code:
private void listView_DragDrop(object sender, DragEventArgs e) {
// do stuff
}
private void listView_DragEnter(object sender, DragEventArgs e) {
// do stuff
}
and then in your routine, your wire it up:
private void addNewWOButton_Click(object sender, EventArgs e)
{
ListView newListView = new ListView();
newListView.AllowDrop = true;
newListView.DragDrop += listView_DragDrop;
newListView.DragEnter += listView_DragEnter;
flowPanel.Controls.Add(newListView);
}
You would have to check who the "sender" is if you need to know which ListView control is firing the event.
You can also just use a lambda function for simple things:
newListView.DragEnter += (s, de) => de.Effect = DragDropEffects.Copy;
Just make sure to unwire the events with -= if you also remove the ListViews dynamically.
To answer the other half of your question, you can use a single handler for any event, from any source, that has the handler's signature. In the body of the handler, you just have to check the sender argument to determine which control raised the event.
You need a way to tell one control from a different one of the same class, however. One way to do this is to make sure to set the Name property on each control when you create it; e.g., newListView.Name = "FilesListView".
Then, before you do anything else in your event handler, check the sender.
private void listView_DragDrop(object sender, DragEventArgs e) {
ListView sendingListView = sender as ListView;
if(sendingListView == null) {
// Sender wasn't a ListView. (But bear in mind it could be any class of
// control that you've wired to this handler, so check those classes if
// need be.)
return;
}
switch(sendingListView.Name) {
case "FilesListView":
// do stuff for a dropped file
break;
case "TextListView":
// do stuff for dropped text
break;
.....
}
}
I have a TreeView and a ListView in my winforms application. The problem is when user selects a node from the treeview, it will take a while for a listview to get populated (Due to heavy calculations!).
Now I want to prevent any node to be selected unless the listview has been populated. The reason is if you keep selecting the nodes very fast using mouse or by tapping or holding down arrow key, the list is not getting populated. since this is for a monitoring of data usage, I want to prevent this behaviour. What are the avilable options to do such thing?
You could use a flag to track your ListView populating status and use the BeforeSelect event of the TreeView. If your ListView is still being populated have the BeforeSelect event handler cancel the event:
private void treeView1_BeforeSelect(object sender, TreeViewCancelEventArgs e)
{
if (_loading)
e.Cancel = true;
}
bool _loading = false;
void treeView1_AfterSelect(object sender, TreeViewEventArgs e)
{
_loading = true;
// ListView populating
_loading = false;
}
I have a CheckedListBox which contains many items which can be checked or unchecked individually.
Next to this are CheckBoxes representing supersets of the many items opposite. These are tri-state corresponding to what portion of their items are selected opposite.
Both the CheckedListBox and individual CheckBoxes subscribe to check changed type events which is causing a circular reference and overflow. How can I disable one from receiving change events when the other is updating it, programmatically?
void checkBox1_N_CheckStateChanged(object sender, EventArgs e) {
checkedListBox1.ItemCheck -= this.CheckedListBox1_ItemCheck;
// Handler body where affecting CheckedListBox1.
checkedListBox1.ItemCheck += this.CheckedListBox1_ItemCheck;
}
It works best to disable each others' event handlers as the other is updating.
void CheckedListBox1_ItemCheck(object sender, EventArgs e) {
//Find which 'i' is affected.
arrOfCBox[i].cb.CheckStateChanged -= arrOfCBox[i].eh;
// Handler body where affecting checkBox[i].
arrOfCBox[i].cb.CheckStateChanged += arrOfCBox[i].eh;
}
where BoxHandler is, at a minimum:
class BoxHandler
{
public EventHandler eh;
public CheckBox cb;
}
Similar to WinForms: temporarily disable an event handler, but amazingly that didn't get awarded an answer, (prolly too abstract?).