Reputation: 2767
I have a subclass of System.Windows.Forms.TreeView
that's manually "bound" to a set of hierarchical data. I want the user to be able to edit the labels of the tree, and have the changes reflected back to the data. So I set LabelEdit
to true and overrode OnAfterLabelEdit
to the tune of:
protected override void OnAfterLabelEdit(NodeLabelEditEventArgs e)
{
base.OnAfterLabelEdit(e);
TreeNode node = e.Node;
if (PassesSomeValidation(e.Label))
{
MyDataNode dataNode = node.Tag as MyDataNode;
dataNode.SomeBoundValue = e.Label;
int oldIndex = node.Index;
int newIndex = RepositionChangedDataNode(dataNode);
TreeNode parent = node.Parent;
parent.Nodes.RemoveAt(oldIndex);
parent.Nodes.Insert(newIndex, node);
}
else
{
e.CancelEdit = true;
}
}
RepositionChangedDataNode()
re-sorts the data and returns the index into which the change node moved after sorting. I was hoping I could simply move the edited node to reflect this move.
The problem is that this causes the node to stay in edit mode! I've tried calling EndEdit()
, cloning the node before inserting it, setting LabelEdit
to false and back to true, wrapping the change in BeginUpdate()
/EndUpdate()
, and various combinations of these ideas, but none of them have any effect.
The culprit seems to be the insertion. Even if I attempt to insert a totally new node, it will go into edit mode immediately.
So, is there any way to make TreeView
not behave this way? And if not, is there a good workaround?
Some ideas I've considered:
Use BeginInvoke()
to push the remove-insert step back onto the message queue like so:
BeginInvoke(new MethodInvoker(delegate(
{
parent.Nodes.RemoveAt(oldIndex);
parent.Nodes.Insert(newIndex, node);
}));
This works and seems cleaner to me than #2, but I know this is probably not how BeginInvoke()
was intended to be used, and that it may have repercussions that my very limited knowledge of the message pump cannot predict.
Upvotes: 5
Views: 2261
Reputation: 31
If you set LabelEdit
for the TreeView
to false
, newly added nodes will not be in edit mode.
You just have to handle the case where the user wants to edit a label: Create a handler for the MouseClick
event of the TreeView
, where you get the clicked node by location. Set LabelEdit
to true
and call BeginEdit()
. At the end of your handler for the AfterLabelEdit
event (and after calling EndEdit(...)
at an appropriate point), set LabelEdit
to false
again.
This works for me, whereas the solution with BeginInvoke only changed which node was in edit mode at the end.
Upvotes: 3
Reputation: 1632
You could try to unhook your OnEdit handler before adding the new node and re-hooking it after. I've seen that behavior before and that's how I handled it.
Upvotes: 0
Reputation: 47978
try create a global variable, let's say:
private bool _allowEdit;
initialize it to true
,
in your OnAfterLabelEdit
method set it to false
after your changes:
... int oldIndex = node.Index;
int newIndex = RepositionChangedDataNode(dataNode);
TreeNode parent = node.Parent;
parent.Nodes.RemoveAt(oldIndex);
parent.Nodes.Insert(newIndex, node);
**_allowEdit = false;**
}
else ...
then capture the OnBeforeLabelEdit
event like this:
protected override void OnBeforeLabelEdit(NodeLabelEditEventArgs e)
{
base.OnBeforeLabelEdit(e);
e.CancelEdit = !_allowEdit;
_allowEdit = true;
}
I noticed that just after 'AfterLabelEdit' is fired, 'BeforeLabelEdit' is refired. That's why you have to stop it right there.
Upvotes: 1
Reputation: 11243
If you're using databinding, shouldn't the update to the data source (SomeBoundValue) trigger a refresh of the nodes? Maybe you can force the currency manager to repopulate the tree view.... If you're worried about performance you could use one of the sorting algorithms that works well with data that's almost already sorted (e.g., NOT quicksort - merge or heapsort come to mind)
Or you could dispense with data binding entirely and manually handle the repositioning since you're already half way there with RepositionChangedDataNode()....
Upvotes: 0