Extending the LabelEdit functionality of a TreeView to include validation

In my last post I described how to extend the default label edit funcionality of a TreeView control to be somewhat more flexible, by allowing you to specify custom text other than blindly using the text of the TreeNode being edited.

This post will extend the original code to include custom validation. For example, you may wish to restrict the available characters, or check to see if the value entered doesn't match an existing value.

Getting started

The code in this article assumes you have a base class that includes the enhancements from the previous post, or you can download the complete example from the link at the end of the article.

Firstly we need to add a new event that will present the proposed change and allow the implementer to validate it. As this is event won't allow the label to be modified, we can use the original NodeLabelEditEventArgs class rather than the custom class we created in the previous post.

[Category("Behavior")]
public event EventHandler<NodeLabelEditEventArgs> ValidateLabelEdit;

protected virtual void OnValidateLabelEdit(NodeLabelEditEventArgs e)
{
  EventHandler<NodeLabelEditEventArgs> handler;

  handler = this.ValidateLabelEdit;

  if (handler != null)
    handler(this, e);
}

We also need a backing variable to store the current text string in the event of a validation error in order to correctly re-initialize the edit field.

private string _postEditText;

Raising the validation event

In our extended TreeView component, we had overridden OnAfterLabelEdit in order to obtain the new display text after a successful edit. We're going to modify this override slightly in order to handle validation.

protected override void OnAfterLabelEdit(NodeLabelEditEventArgs e)
{
  if (e.Label != null) // if the user cancelled the edit this event is still raised, just with a null label
  {
    NodeLabelEditEventArgs validateEventArgs;

    e.CancelEdit = true; // cancel the built in operation so we can substitute our own

    validateEventArgs = new NodeLabelEditEventArgs(e.Node, e.Label);

    this.OnValidateLabelEdit(validateEventArgs); // validate the users input

    if (validateEventArgs.CancelEdit)
    {
      // if the users input was invalid, enter edit mode again using the previously entered text to give them the chance to correct it
      _postEditText = e.Label;
      e.Node.BeginEdit();
    }
    else
    {
      // -- snip --
    }
  }

  base.OnAfterLabelEdit(e);
}

Here, we automatically cancel the default handling of the label edit, as regardless of whether validation passes or not, we'll be updating node text manually.

First we raise our ValidateLabelEdit event, passing in the TreeNode to be edited, and the proposed label text. If the CancelEdit property of the passed NodeLabelEditEventArgs is set to true, then validation has failed.

If validation does fail, we update the _postEditText variable we defined earlier with the current label text, then automatically switch the control back into label editing mode.

Changing how label edits are initialized

There's just one thing left to change. As with OnAfterLabelEdit, we had also overridden OnBeforeLabelEdit in order to modify the text displayed in the edit field. We'll need to modify this to provide the current label value if a validation error occurs, otherwise the text will reset to whatever the original value was before editing started. Of course, in the event of a validation error you want he user to be able to retry with the modified value to allow correction of the error. To do this, we'll modify the block of code that obtained the text to display to use the new _postEditText variable and to skip raising the RequestEditText event if its set. We'll also reset the _postEditText to null so that the next time an edit is started, it reverts to the original behaviour. Unless it's another validation error for the current edit operation of course!

protected override void OnBeforeLabelEdit(NodeLabelEditEventArgs e)
{
  NodeRequestTextEventArgs editTextArgs;

  // get the text to apply to the label
  editTextArgs = new NodeRequestTextEventArgs(e.Node, _postEditText ?? e.Node.Text);
  if (_postEditText == null)
    this.OnRequestEditText(editTextArgs);
  _postEditText = null;

  // -- snip --

  base.OnBeforeLabelEdit(e);
}

And that is it. Extremely simple, but very useful if you need to validate this sort of input!

Sample application

The sample project available with this article demonstrates validation, as shown in the following snippet.

private void subclassedTreeView_ValidateLabelEdit(object sender, NodeLabelEditEventArgs e)
{
  TreeNode[] matchingNodes;

  // Check to make sure the value the user enters isn't used by any other node than the current.
  // This code assumes that all names in the tree are unique, regardless of level
  matchingNodes = subclassedTreeView.Nodes.Find(e.Label, true);
  if (matchingNodes.Length != 0 && matchingNodes[0] != e.Node)
  {
    MessageBox.Show("You must enter a unique value.", "Validation Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
    e.CancelEdit = true;
  }
}

Further Improvements

As can be seen from the simple animation at the start of the article, the edit field is hidden and the original node text displayed, validation occurs, then editing restarts in the event of an error. This means, if you display a message box for example, the original tree state is displayed. It also means that the cursor and selection state of the edit field is lost. Ideally, it would be preferable to do validation without causing the edit field to vanish first, but that would require some more pinvoke, and probably isn't necessary for most cases - this method keeps the users entered text which is the important bit.

Downloads

Filename Description Version Release Date
CustomTreeViewLabelEditPart2.zip
  • md5: 84276c0e1d02b4750fc1ecd76d71bedd

Sample project for the Extending the LabelEdit functionality of a TreeView to include validation blog post.

28/10/2013 Download

About The Author

Gravatar

The founder of Cyotek, Richard enjoys creating new blog content for the site. Much more though, he likes to develop programs, and can often found writing reams of code. A long term gamer, he has aspirations in one day creating an epic video game. Until that time, he is mostly content with adding new bugs to WebCopy and the other Cyotek products.

Leave a Comment

While we appreciate comments from our users, please follow our posting guidelines. Have you tried the Cyotek Forums for support from Cyotek and the community?

Styling with Markdown is supported