从您的问题中不清楚过滤器是静态的还是可以由用户配置。假设过滤器可以动态配置,您可以做的是每当过滤器发生更改时从 XML(重新)加载树、回收现有节点、根据需要创建新节点以及删除过滤的节点。 (当然,如果过滤器是静态的,这也有效。)
首先,提取一个辅助方法来从 XML 重新填充树,并在适当时按名称回收节点:
public delegate string GetString<T>(T input); // No Func<T, TResult> in c# 2.0
public static class XmlTreeViewHelper
{
public static void AddOrMergeNodes(TreeNodeCollection treeNodeCollection, XmlNodeList xmlNodeList, GetString<XmlNode> getNodeName, GetString<XmlNode> getNodeText, Predicate<XmlNode> filter)
{
Dictionary<string, List<TreeNode>> dict = ToNodeDictionary(treeNodeCollection);
int index = 0;
foreach (XmlNode inXmlNode in xmlNodeList)
{
AddOrMergeNode(treeNodeCollection, inXmlNode, ref index, getNodeName, getNodeText, filter, dict);
}
foreach (List<TreeNode> list in dict.Values)
foreach (TreeNode leftover in list)
{
treeNodeCollection.Remove(leftover);
}
}
static bool IsNodeAtIndex(TreeNodeCollection nodes, TreeNode node, int index)
{
// Avoid n-squared nodes.IndexOf(node).
if (index < 0 || index >= nodes.Count)
return false;
return nodes[index] == node;
}
static void AddOrMergeNode(TreeNodeCollection treeNodeCollection, XmlNode inXmlNode, ref int index, GetString<XmlNode> getNodeName, GetString<XmlNode> getNodeText, Predicate<XmlNode> filter, Dictionary<string, List<TreeNode>> dict)
{
if (filter != null && !filter(inXmlNode))
return;
string treeName = getNodeName(inXmlNode);
string treeText = (getNodeText == null ? treeName : getNodeText(inXmlNode));
bool added = false;
TreeNode treeNode;
if (!DictionaryExtensions.TryRemoveFirst(dict, treeName, out treeNode))
{
treeNode = new TreeNode();
treeNode.Name = treeName;
treeNode.Text = treeText;
added = true;
treeNodeCollection.Insert(index, treeNode);
}
else
{
if (!IsNodeAtIndex(treeNodeCollection, treeNode, index))
{
treeNodeCollection.Remove(treeNode);
treeNodeCollection.Insert(index, treeNode);
}
}
index++;
if (treeNode.Text != treeText)
treeNode.Text = treeText;
if (inXmlNode.HasChildNodes)
AddOrMergeNodes(treeNode.Nodes, inXmlNode.ChildNodes, getNodeName, getNodeText, filter);
else
treeNode.Nodes.Clear();
if (added)
treeNode.ExpandAll();
}
/// <summary>
/// Returns a dictionary of tree nodes by node name.
/// </summary>
/// <param name="nodes"></param>
/// <returns></returns>
static Dictionary<string, List<TreeNode>> ToNodeDictionary(TreeNodeCollection nodes)
{
Dictionary<string, List<TreeNode>> dict = new Dictionary<string, List<TreeNode>>();
foreach (TreeNode node in nodes)
DictionaryExtensions.Add(dict, node.Name, node);
return dict;
}
}
public static class DictionaryExtensions
{
public static void Add<TKey, TValueList, TValue>(IDictionary<TKey, TValueList> listDictionary, TKey key, TValue value)
where TValueList : IList<TValue>, new()
{
if (listDictionary == null)
throw new ArgumentNullException();
TValueList values;
if (!listDictionary.TryGetValue(key, out values))
listDictionary[key] = values = new TValueList();
values.Add(value);
}
public static bool TryGetValue<TKey, TValueList, TValue>(IDictionary<TKey, TValueList> listDictionary, TKey key, int index, out TValue value)
where TValueList : IList<TValue>
{
TValueList list;
if (!listDictionary.TryGetValue(key, out list))
return Returns.False(out value);
if (index < 0 || index >= list.Count)
return Returns.False(out value);
value = list[index];
return true;
}
public static bool TryRemoveFirst<TKey, TValueList, TValue>(IDictionary<TKey, TValueList> listDictionary, TKey key, out TValue value)
where TValueList : IList<TValue>
{
TValueList list;
if (!listDictionary.TryGetValue(key, out list))
return Returns.False(out value);
int count = list.Count;
if (count > 0)
{
value = list[0];
list.RemoveAt(0);
if (--count == 0)
listDictionary.Remove(key);
return true;
}
else
{
listDictionary.Remove(key); // Error?
return Returns.False(out value);
}
}
}
public static class Returns
{
public static bool False<TValue>(out TValue value)
{
value = default(TValue);
return false;
}
}
接下来,动态(重新)加载 XML 并应用任何合适的过滤器:
private void ReloadTreeFromXmlDocument(XmlDocument dom)
{
treeView1.BeginUpdate();
try
{
XmlTreeViewHelper.AddOrMergeNodes(treeView1.Nodes, dom.DocumentElement.ChildNodes, GetTreeNodeName, GetTreeNodeText, FilterNode);
}
finally
{
treeView1.EndUpdate();
}
}
static string GetTreeNodeName(XmlNode inXmlNode)
{
string text = GetAttributeText(inXmlNode, "name");
if (string.IsNullOrEmpty(text))
text = inXmlNode.Name;
return text;
}
static string GetTreeNodeText(XmlNode inXmlNode)
{
string text = GetAttributeText(inXmlNode, "name");
if (string.IsNullOrEmpty(text))
{
if (inXmlNode.HasChildNodes)
{
text = inXmlNode.Name;
}
else
{
text = (inXmlNode.OuterXml).Trim();
}
}
return text;
}
string filter = "_start"; // Reload when this changes
bool FilterNode(XmlNode inXmlNode)
{
return FilterNode(inXmlNode, filter /*filterComboBox.Text*/);
}
bool FilterNode(XmlNode inXmlNode, string nodeNameFilter)
{
if (inXmlNode.Name == "namespace" && inXmlNode.ChildNodes.Count == 0 && string.IsNullOrEmpty(GetAttributeText(inXmlNode, "name")))
return false;
if (!string.IsNullOrEmpty(nodeNameFilter))
{
string text = GetTreeNodeText(inXmlNode);
if (text.Contains(nodeNameFilter))
return false;
}
return true;
}
static string GetAttributeText(XmlNode inXmlNode, string name)
{
XmlAttribute attr = (inXmlNode.Attributes == null ? null : inXmlNode.Attributes[name]);
return attr == null ? null : attr.Value;
}
将我的解决方案向后移植到 .Net 2.0 后,我得到的是: