Using Two Dictionaries for Bidirectional Lookup
"But wait!" (
I hope you're already saying) "Okay, you've got the ability to look up a ListViewItem using a key that's a TreeNode, but what about when you get a hit on the ListViewItem, and you want to know which corresponding TreeNode should be selected?"
Well, one answer would be to have two generic dictionaries. For example, you could add a second one of the form:
private Dictionary<ListViewItem, TreeNode> LVIDictionary =
new Dictionary<ListViewItem, TreeNode>();
Now you have a way to get the corresponding TreeNode from a selected ListViewItem using the
LVIDictionary instance.
Loading the Generic Dictionaries
Now the question becomes: How can you get these dictionaries properly loaded with the TreeNode and ListViewItem key/value pairs, and link them to selection events at run-time?
You might immediately think of creating custom classes that inherit from TreeView and ListView with overridden methods, but because you can't inherit from important items such as TreeNodeCollection, you'd have to begin casting to use custom Nodes or ListViewItems in the methods you didn't override.
I strongly encourage you to forget that route unless you are a rodeo champion of such fancy overridingwith a library of already-well-tested code to draw from. Alternatively, you could load the dictionaries from DataSets or XML files, from which you would construct the TreeView Nodes and ListView ListViewItems dynamically. Or perhaps with some applications, you might want to give your users the ability to create/edit/delete entries on the fly.
This article has sufficient time and space to examine only a simple example of the mechanics of creating new entries. Look at the code for the
Click event-handler of the
makeNewItemBtn below:
private int nodeCount = 0;
private void makeNewItemBtn_Click(object sender, EventArgs e)
{
currentNode = new TreeNode();
currentNode.Text = "Item " + nodeCount.ToString();
tv1.Nodes.Add(currentNode);
currentLVI = new ListViewItem();
currentLVI.Text = currentNode.Text;
lv1.Items.Add(currentLVI);
nodeDictionary.Add(currentNode, currentLVI);
LVIDictionary.Add(currentLVI, currentNode);
// foreach (TreeNode theNode in nodeDictionary.Keys)
// {
// Console.WriteLine(theNode.Text);
// }
nodeCount++;
}
The commented-out code in the preceding example shows
foreach iteration over the current
Keys collection in the
nodeDictionary, writing the
Text property value of each key to the console. Using such code, you can easily synchronize the current selection in ListView and TreeView by defining an
AfterSelect event-handler for the TreeView, and an
ItemActivate handler for the ListView:
private void tv1_AfterSelect(object sender, TreeViewEventArgs e)
{
nodeDictionary[tv1.SelectedNode].Selected = true;
}
// note : if you use SelectedIndexChanged instead
// you'll have to handle the case of SelectedItems being
// set to null when the ListView loses focus ...
// note : ItemActivate seems a little "sluggish" compared to
// SelectedIndexChanged at run-time !
private void lv1_ItemActivate(object sender, EventArgs e)
{
tv1.SelectedNode = LVIDictionary[lv1.SelectedItems[0]];
}
While this example is very simple, because it adds only one pair of TreeNode/ListViewItem at a time, you can easily envision more complex versions that, might, for example, synchronize ListViewItems with TreeNodes at different levels of the TreeView structure, or that base synchronization on some ordering principle other than linear sequence.