In my last post I described how to extend the default label edit functionality 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.

An example project showing the use of validation in node editing, and preserving the entered text between errors
An example project showing the use of validation in node editing, and preserving the entered text between errors

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.

csharp
[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.

csharp
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.

csharp
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!

csharp
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.

csharp
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 p-invoke, and probably isn't necessary for most cases - this method keeps the users entered text which is the important bit.

Update History

  • 2013-10-28 - First published
  • 2020-11-21 - Updated formatting, corrected misspellings

Like what you're reading? Perhaps you like to buy us a coffee?

Donate via Buy Me a Coffee

Donate via PayPal


Files


Comments