Specifying custom text when using the LabelEdit functionality of a TreeView

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.

How you would expect it to work

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.

Getting the handle to the EDIT control

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.

OK, but what about specifying the real text?

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.

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.

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
{
NodeRequestTextEventArgs displayTextArgs;
displayTextArgs = new NodeRequestTextEventArgs(e.Node, e.Label);
this.OnRequestDisplayText(displayTextArgs);
e.CancelEdit = true; // cancel the built in operation so we can substitute our own
if (!displayTextArgs.Cancel)
e.Node.Text = displayTextArgs.Label;
}
base.OnAfterLabelEdit(e);
}

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.

Closing notes

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.

Bonus Chatter

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.

About The Author

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?

Your name:

Your email address: (optional, will never be displayed)

Your URL: (optional)

Comments

Comment

Styling with Markdown is supported

Search

Donate

This content may be used free of charge, but as with all free content there are costs involved to develop and maintain.

If this site or its services have saved you time, please consider a donation to help with running costs and timely updates.