Extending the LabelEdit functionality of a TreeView to include validation
An article which describes how to add validation support to a `TreeView` control that is using custom label edit functionality.
Recently I was updating a support tool that displays documents
in raw form and allows editing of them. This tool is centred
around a TreeView
, and the Text
property of each TreeNode
is a concatenation of a name and one or more values.
The problem with this approach is if you wish to allow the nodes to be edited using the built in functionality you're in trouble as by default you can't actually influence the text that appears in the in-line editor. In other applications of a similar nature I used owner-drawn trees as I was using different styles for the name and the value. In this case, I just wanted the standard look.
Ideally, you'd expect that by hooking into the BeforeLabelEdit
event (or overriding OnBeforeLabelEdit
) then you could
manipulate the NodeLabelEditEventArgs.Label
property. Except
this property is read only.
Scratch that then. What about setting the TreeNode.Text
property to something else in this event, then resetting it
afterwards? Nope, doesn't work either.
Therefore, using just the managed code of the TreeView
it's
not possible to do what we want. Lets get slightly outside the
black box with a little Win32 API. We'll get the handle of the
edit
control the TreeView
is using and directly set it's
text.
In order to manipulate the edit control, we first need to get a
handle to it. We can do this succinctly by overriding
OnBeforeLabelEdit
(or hooking the BeforeLabelEdit
event
although the former is preferable) and using the
TVM_GETEDITCONTROL
message.
Now that we have a handle, we can painlessly use WM_SETTEXT
to
change the text of the edit control
If you were hooking into the BeforeLabelEdit
event, then you
can just have your own logic in that event to determine the text
to apply. If however you're overriding OnBeforeEdit
in order
to make a nice reusable component, you need another way of
allowing implementers to specify the value. For this, I added a
new event to the control.
The NodeRequestTextEventArgs
class is essentially a clone of
NodeLabelEditEventArgs
except with a writeable Label
property. I also decided to allow you to cancel the node edit
from this event, so that implementers don't have to hook both
events unless necessary.
Our final version now looks like this:
And an sample usage scenario from the demo application:
In this example, we are setting the edit text to be the value of
the TreeNode
's Name
property, regardless of whatever its
Text
is.
After the conclusion of the label editing, the node's text will be set to the new label, and therefore we need to tinker that logic to allow the implementer to specify the new value text.
You could just hook the AfterLabelEdit
event and have your
custom logic in there (remembering to cancel the default edit),
as shown here:
However, I didn't want to be having to do this type of code each
time I implemented this sort of behaviour in an application.
Rather than get fancy with subclassed TreeNode
classes, I
choose to add a sister event for RequestEditText
, named
RequestDisplayText
and then handle this automatically. This is
the only aspect of this article that feels "smelly" to me -
ideally it would be nice if the control could handle this for
you without having to ask for more information. But, this should
do for the time being.
And an example of use:
The demonstration shows both of these approaches - the
TreeViewEx
control favours the RequestDisplayText
event, and
the TreeViewExNotify
control leaves it entirely to the
implementer to deal with.
And that's it. I've seen some implementations of this sort of
functionality in various places across the internet, and some of
them are pretty awful, having to override all sorts of methods,
store and restore various states. The above solution is pretty
simple and works regardless of if you are calling
TreeNode.BeginEdit
or using the "click and hover" approach on
a node.
In addition, it's trivially easy to expand this to support validation as well, I'll cover that in the next article.
I originally tried two different approaches to modifying the
value, both of these involved capturing the TVN_BEGINLABELEDIT
notification. The first approach used a NativeWindow
bound to
the TreeView
control's parent watching for the WM_NOTIFY
message. The second approach did the same thing, but used MFC's
Message Reflection via WM_REFLECT
to intercept the
notification message on the tree view itself. Both of these
solutions worked, and yet were still overkill as overriding
OnBeforeLabelEdit
is sufficient.
Although I'm not going to describe that approach here as it'll
just clutter the article, I did include an implementation of the
WM_REFLECT
solution in the demonstration project as I think it
is a neat technique and potentially useful for other
notifications.
Like what you're reading? Perhaps you like to buy us a coffee?