Editable TreeView
As I was updating the COM Shim Wizards (the new version should be published in a couple of weeks), I found I had a need for an editable TreeView. There is only limited support for editing in the standard TreeView. So, I did what all sensible people do – I LiveSearched for it to see if I could reuse something out there. To my surprise, there really wasn't anything (I double-checked with Google). However, I did find an article on building a better combobox, so I borrowed a couple of the techniques in that.
In the end, it turned out to be pretty simple. Obviously, you need to derive a class from TreeView, and override the WndProc so that you can hijack the messages of interest before (or instead of) the parent implementation. I wanted to trap the user double-clicking the left mouse button, and also pressing the delete key.
My requirements were pretty simple: if the user double-clicks an existing root node in the tree, I want to add a child to that node. If they double-click somewhere that's not on a root node, I want to add a new root node to the end of the tree. This scheme only supports 2 levels of node – which is exactly what I wanted for the wizard dialog – but, clearly, you could expand this to suit your own requirements. If they press Delete, I want to delete the selected node. That's it. Implementation was also pretty simple – the significant code is listed below:
public class TreeViewEx : TreeView
{
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case (int)Win32Messages.WM_LBUTTONDBLCLK :
int x = Win32Macros.LoWord(m.LParam.ToInt32());
int y = Win32Macros.HiWord(m.LParam.ToInt32());
TreeViewHitTestInfo info = this.HitTest(x, y);
if (info.Node != null)
{
// They've clicked on a node.
TreeNode hitNode = info.Node;
// If this is a root node, add a child to it.
// Otherwise, do nothing.
if (hitNode.Level == 0)
{
hitNode = info.Node;
TreeNode newNode = hitNode.Nodes.Add("New Child Node");
this.SelectedNode = newNode;
}
}
else
{
// They've clicked somewhere other than a node.
// Add a root node to the end of the tree.
this.Nodes.Add("New Root Node");
}
return;
case (int)Win32Messages.WM_KEYDOWN :
// If they press Delete, we'll delete the selected node.
if (m.WParam.ToInt32() == Win32VirtualKeys.VK_DELETE)
{
if (this.SelectedNode != null)
{
this.SelectedNode.Remove();
}
}
return;
}
base.WndProc(ref m);
}
}
The above listing is my entire custom TreeView class – it doesn't get much simpler than this. You can see from this that I've also defined some helpers: Win32Messages, Win32VirtualKeys, and Win32Macros. They look like this:
public enum Win32Messages
{
// etc
WM_KEYDOWN = 0x100,
WM_KEYFIRST = 0x100,
WM_KEYLAST = 0x108,
WM_KEYUP = 0x101,
WM_LBUTTONDBLCLK = 0x203,
WM_LBUTTONDOWN = 0x201,
WM_LBUTTONUP = 0x202,
// etc
}
class Win32VirtualKeys
{
// etc
public const uint VK_INSERT = 0x2D;
public const uint VK_DELETE = 0x2E;
public const uint VK_HELP = 0x2F;
// etc
}
Of course, I got most of these from www.pinvoke.net. The macros were a bit trickier – I hacked these together from the C++ sources in windef.h, winuser.h:
public class Win32Macros
{
public static int MakeLong(int LoWord, int HiWord)
{
return (HiWord << 16) | (LoWord & 0xffff);
}
public static IntPtr MakeLParam(int LoWord, int HiWord)
{
return (IntPtr)((HiWord << 16) | (LoWord & 0xffff));
}
public static int HiWord(int Number)
{
return (Number >> 16) & 0xffff;
}
public static int LoWord(int Number)
{
return Number & 0xffff;
}
}