Treeview Mimicking Windows Explorer

I am implementing a TreeView control to closely mimic Windows Explorer behaviour. The user can edit labels and these must be uniquely named. If the user edits the name of the label to a non-unique name an error message is displayed and the label remains in edit mode until the user enters a valid name. I am using the following pattern within OnAfterLabelEdit:

protected override void OnAfterLabelEdit(NodeLabelEditEventArgs e)
{
//Label is valid unless the user has changed it.
if(e.Label==null)
{
return;
}
//Peform validation.
bool IsValid=ValidateLabel(e);
if(!IsValid)
{
//Reset label.
e.CancelEdit = true;
System.Windows.Forms.MessageBox.Show("Error");
//Resume edit mode.
e.Node.BeginEdit();
}
else
{

//Valid label.
e.Node.Text = e.Label;
base.OnAfterLabelEdit(e);
}
}

This works, as expected, where the user enters an invalid label and presses enter. The node remains in edit mode, allowing the user to easily retype a valid label. However, when the user enters an invalid label and clicks on another node; the error message is displayed (as before) but the node is not in edit mode, merely highlighted.

Clicking on another node seems to result in OnAfterLabelEdit being called twice. The first time around, the e.Label property contains the changed label value. The second time OnAfterLabelEdit is called the e.Label property is null and the routine exits, since there are no modifications to the label.

Is there a bug in the TreeView control Where a folder is not uniquely named, the Windows Explorer treeview returns the node to edit mode. I have looked at a number of examples, which mimic Windows Explorer, but none of them accurately reproduce this particular feature. What do I need to do to recreate this behaviour in my TreeView



Answer this question

Treeview Mimicking Windows Explorer

  • Yingshen

    Okay, I see what's happening.  The message for the AfterLabelEdit associated with clicking another node is still in the queue, calling BeginEdit when processing the current AfterLabelEdit is getting tromped by that unprocessed message (or messages).  You can force the BeginEdit to occur after all existing message in the queue by invoking it with BeginInvoke.  This can be done with an anonymous function by replacing the call to e.Node.BeginEdit(); with this.BeginInvoke((MethodInvoker)delegate(){e.Node.BeginEdit();});.

    The drawback of this is that you lose the text the user has currently typed it.  You could do what you did before and set e.Node.Text to e.Label; but then you'll circumvent the validation.  For example, if the user edits a node and types in invalid text and clicks another node (if your routine changed e.Node.Text to e.Label) the message box would appear and the edit box would contain the current text; but, if the user clicked another node again the TreeView would think the user hadn't edited the text and raise an AfterLabelEdit event with a null e.Label.

    I don't think there's anyway to get around that with the current implementation of TreeView (ListView would have the same problem).



  • David Prentice

    Ok, thanks. I was loathe to accept that it was a limitation of the TreeView control, but that would seem to be the case.
  • Philippe Dansereau

    You're seeing this in the debugger, right

    When you break anywhere in this code and BeginEdit is called (before or after), the application no longer has focus and the edit is canceled, resulting in what appears to be another AfterLabelEdit event with a null Label.

    If you don't break in the code and simply add Trace or Debug statements to see what is going on you will see only the one event.



  • ShareCropper

    Thanks, I understand there may be legitimate reasons for two events to occur. However, it does seem to be preventing me from effecting the desired Windows Explorer style behaviour.

    If you edit a TreeNode in Windows Explorer, rename the folder to one that already exists and click on another node, then an error message appears and, crucially, the TreeNode is returned to edit mode. I want to be able to recreate exactly this behaviour (which is exhibited when I edit a node and press enter instead of clicking another node). Any ideas


  • Cong Li

    I tried the MSDN sample and it does not exhibit the same behaviour as Windows Explorer. When editing a label and clicking another node without hitting return, the label is still not returned to edit mode. So, unfortunately the last code sample is not the answer.
  • MFH Schoonbrood

    I added code to write to the Event Viewer and ran the executable. There are still 2 events.

    The first occurence of the event, where I edit the TreeNode label, has the e.Node.Text value set to the original label and the e.Label value set to the newly edited label.

    The second occurence of the event, produced where I click on another node, has the e.Node.Text value set to the original label and the e.Label value set to null.

    Where I do not click on another node, whilst editing the label, instead pressing enter to commit an invalid label name, the second invocation of the event does not occur and the TreeNode remains in edit mode - as expected. It appears that clicking another node interferes with the TreeNode remaining in edit mode.

    I am using Visual Studio 2005.


  • nildorn

    Yes, you'll always get the AfterLabelEdit with e.Label == null when the user cancels the edit by pressing another node or pressing Esc. If the user has pressed return after the label edit is begun you'll get the AfterLabelEdit with e.Label == to the new value.

    You're not getting two events for the same action, you're getting two events for two different actions; there is no bug.



  • nfreelan

    I see what you mean. The sample code for AfterLabelEdit in MSDN does not exibit the problem you are describing (typing in text that will cause an invalid text message box and clicking another node without hitting Return). That could is slightly different in that it doesn't call the base. Using that code as a guide, your could would more closely match like this

    if (e.Label != null)
    {
    if (ValidateLabel(e))
    {
    // Stop editing without canceling the label change.
    e.Node.EndEdit(false);
    }
    else
    {
    /* Cancel the label edit action, inform the user, and
    place the node in edit mode again. */
    e.CancelEdit = true;
    MessageBox.Show("Error");
    e.Node.BeginEdit();
    }
    }

    Note the call to EndEdit(false) instead of setting the node text and calling base.OnAfterLabelEdit--which would cause the AfterLabelEdit event to be raised again.

  • Treeview Mimicking Windows Explorer