Extending the LabelEdit functionality of a TreeView to include validation
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.
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 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
Related articles you may be interested in
Downloads
Filename | Description | Version | Release Date | |
---|---|---|---|---|
CustomTreeViewLabelEditPart2.zip
|
Sample project for the Extending the LabelEdit functionality of a TreeView to include validation blog post. |
28/10/2013 | Download |
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?