将TreeView存储、写入数据库的扩展类TreeViewEx
在一个项目中用到了读取、生成并保存TreeView内容到数据库的应用,经过研究,并着重参考了planet-source-code上的一篇文章<TreeView Interface (Drag and Drop; Add, Rename, and Delete Nodes; Save Tree to Database)>
,将原文的VB.NET代码改造成了C#代码,与大家共同分享。
using System;
using System.Windows.Forms;
using System.Collections;
using System.Data.OleDb;
namespace CallingCenterAdmin
{
/// <summary>
/// TreeViewToDB 的摘要说明。
/// </summary>
public class TreeViewToDB
{
// m_alDeletedNodes is used to store node deletions in memory. A call to
// CTreeView.SaveNodeCollection() will commit the deletions, and other changes,
// to the database.
public ArrayList m_alDeletedNodes;
public OleDbConnection m_conn;
public TreeViewToDB(OleDbConnection conn)
{
// Initialize the DeletedNodes collection.
m_alDeletedNodes = new ArrayList();
m_conn = conn;
}
public bool DeleteNode(TreeNode tnStart)
{
// PURPOSE: This function will delete the designated node (tnStart) and all
// of its children. The deletions will be stored in a collection. This will
// keep the deletions in memory, which configuration will allow us to rollback
// deletions.
// Get a reference to the start node parent.
TreeNode tnParent = tnStart.Parent;
// Delete the start node's children. This is performed via
// recursion, which will walk through all children regardless of number or
// arrangement. Walking through each and every child of the start node will
// allow us to synchronize node deletions with the database. Simply calling
// the remove function will remove the node and its children, but
// will leave orphan records in the database.
if (!DeleteNodeRecursive(tnStart))
{
return false;
}
// Record the deletion of the start node.
m_alDeletedNodes.Add(tnStart);
// Remove the start node from the TreeNodeCollection.
tnStart.Nodes.Remove(tnStart);
return true;
}
public bool DeleteNodeRecursive(TreeNode tnParent)
{
// PURPOSE: This function will walk through all the child nodes for a given
// node. It will remove all the nodes from the TreeNodeCollection and will
// record all deletions in memory. Deletions will be committed to the
// database when the user calls the CTreeView.SaveNodeCollection() method.
TreeNode tn = null;
int nCount = 0;
try
{
nCount = tnParent.GetNodeCount(false);
if (nCount > 0)
{
tn = tnParent.Nodes[0];
do
{
nCount = tn.GetNodeCount(false);
if (nCount > 0)
{
DeleteNodeRecursive(tn);
}
m_alDeletedNodes.Add(tn);
tn = tn.NextNode;
}
while (tn != null);
}
return true;
}
catch
{
return false;
}
}
public bool IsDropAllowed(TreeNode tnStart, TreeNode tnDrop)
{
// PURPOSE: This function will determine if a drop will cause a circular
// reference. A circular reference occurs when a node is dropped onto one
// of its children.
TreeNode tnCurrent = tnDrop;
do
{
if (tnCurrent == tnStart)
{
return false;
}
tnCurrent = tnCurrent.Parent;
}
while (tnCurrent == null);
return true;
}
public void PopulateTree(TreeView oTv)
{
// PURPOSE: This function will populate a TreeView control from the
// database.
// Clear the DeletedNodes collection; thereby rolling back any deletes
// that were made since the last call of the CTreeView.SaveTreeNodeCollection()
// method.
m_alDeletedNodes = null;
m_alDeletedNodes = new ArrayList();
// Retrieve a list of node items from the database.
string strSql = "SELECT * FROM [TreeViewItems] ORDER By iSort;";
OleDbDataReader rdr = GetDataReader(strSql);
// collNodeIDs is used to store a relationship between keys
// (in this case uids) and Nodes. The collection is used to
// maintain parent-child relationships when populating the
// TreeView control.
ArrayList collNodeKeys = new ArrayList();
TreeNode tnNew;
TreeNode tnParent;
while (rdr.Read())
{
if (rdr.GetBoolean(0))//"bRoot"
{
tnNew = oTv.Nodes.Add(rdr.GetString(1));//"sName"
tnNew.Tag = rdr.GetString(2);//"uid"
// Record the relationship of uid to node. This will allow
// us to retrieve a given node by providing the uid as a
// key.
collNodeKeys.Add(tnNew);
}
else
{
// Get the parent node based on the relationship stored in the
// database. This relationship is recorded or updated when a
// call is made to CTreeView.SaveTreeNodeCollection().
tnParent = (TreeNode)collNodeKeys[rdr.GetInt32(3)];//"iParentID"
// Add the child to the parent;
tnNew = tnParent.Nodes.Add(rdr.GetString(4));//"sName"
tnNew.Tag = rdr.GetString(2);//"uid"
// Record the relationship of uid to node. This will allow
// us to retrieve a given node by providing the uid as a
// key.
collNodeKeys.Add(tnNew);
}
}
rdr.Close();
}
public void SaveNodeCollection(TreeNode tnRootNode)
{
// PURPOSE; This method will save the TreeNodeCollection to the
// database. It uses recursion to walk through the tree. It must
// be called for each root node, if there is more than one root
// node.
int iCntr = m_alDeletedNodes.Count;
int iRecordID;
TreeNode tn;
// Synch all deleted nodes with the database.
for (int i = 0; i < iCntr; i++)
{
tn = (TreeNode)m_alDeletedNodes[i];
if (tn.Tag.ToString() != "")
{
iRecordID = int.Parse(tn.Tag.ToString());
DeleteRecord(iRecordID);
}
}
if (tnRootNode != null)
{
// Clear the deleted nodes collection because the references
// are no longer required.
m_alDeletedNodes = null;
m_alDeletedNodes = new ArrayList();
// Save all records to the database, starting with the root node. We
// maintain the sort order so that the nodes can be restored in the
// order that they were read. This will prevent adding a node before
// adding its parent.
SaveNodeToDb(tnRootNode, 1);
SaveNodeCollectionRecursive(tnRootNode, 1);
}
}
public void SaveNodeCollectionRecursive(TreeNode tnParent, int iSort)
{
// PURPOSE: This function will save all child nodes in a given order
// starting with the root node and working out towards the child nodes.
// This function uses recursion, and will walk through any tree structure
// regardless of node count or arrangement.
TreeNode tn;
int nCount = tnParent.GetNodeCount(false);
if (nCount > 0)
{
tn = tnParent.Nodes[0];
}
else
{
tn = null;
}
do
{
iSort++;
SaveNodeToDb(tn, iSort);
nCount = tn.GetNodeCount(false);
if (nCount > 0)
{
SaveNodeCollectionRecursive(tn, iSort);
}
tn = tn.NextNode;
}
while (tn == null);
}
public void SaveNodeToDb(TreeNode tn, int iSort)
{
// PURPOSE: The following method will save the designated node to the
// database.
bool bRoot;
int iNewRecordID;
int iParentID;
string sName;
string sFullPath;
string strSql;
OleDbCommand cmd = m_conn.CreateCommand();
if (tn.Parent != null)
{
iParentID = int.Parse(tn.Parent.Tag.ToString());
bRoot = false;
}
else
{
iParentID = -1;
bRoot = false;
}
// Need to escape single and double quotes; otherwise, they will cause
// exceptions when posting to the database.
sName = tn.Text;
sFullPath = tn.FullPath;
// I use the tag value to determine if a record for the node exists
// in the database and to hold the value of the primary key if the
// the record exists in the database. If the tag value is empty, then
// I know the record is newly created and not yet saved in the database.
if (tn.Tag.ToString() == "")
{
// Insert a record into the database for the node.
strSql = "INSERT INTO [TreeViewItems] (bRoot, dLastModified, iImageIndex," +
"iParentID, iSelectedImageIndex, iSort, sName, sFullName) VALUES " +
"(" + bRoot + ",'" + DateTime.Now + "'," + tn.ImageIndex + "," +
iParentID + ", " + tn.SelectedImageIndex + "," + iSort + ",'" +
sName + "', '" + sFullPath + "')";
// Execute the INSERT statement against the database.
ExecuteNonQuery(strSql);
// Get the record ID for the newly created record. This assumes that
// only one person is using the database.
iNewRecordID = GetScalar("SELECT Max(uid) FROM [TreeViewItems]");
// Place the record ID in the node's tag.
tn.Tag = iNewRecordID.ToString();
}
else
{
// Update the corresponding record in the database for the node.
strSql = "UPDATE [TreeViewItems] " +
"SET sName='" + sName + "', " +
"bRoot=" + bRoot + ", " +
"iImageIndex=" + tn.ImageIndex + ", " +
"iParentID=" + iParentID + ", " +
"iSelectedImageIndex=" + tn.SelectedImageIndex + ", " +
"iSort=" + iSort + ", " +
"sFullName='" + sFullPath + "' " +
"WHERE uid=" + int.Parse(tn.Tag.ToString());
// Execute the INSERT statement against the database.
ExecuteNonQuery(strSql);
}
}
public OleDbDataReader GetDataReader(string strSql)
{
OleDbCommand cmd = m_conn.CreateCommand();
cmd.CommandText = strSql;
OleDbDataReader rdr = cmd.ExecuteReader();
return rdr;
}
public void DeleteRecord(int nID)
{
// PURPOSE: The following function will delete the designated record from
// the database.
// NOTE: The brackets '[]' are not required around the table name. They
// are only required if the table name contains spaces. I have included
// them as a matter of style.
string strSql = "DELETE FROM [TreeViewItems] WHERE uid=" + nID + ";";
OleDbCommand cmd = m_conn.CreateCommand();
cmd.CommandText = strSql;
cmd.ExecuteNonQuery();
}
public void ExecuteNonQuery(String strSql)
{
// PURPOSE: This function will execute a non-query SQL statement such
// as an INSERT or UPDATE or DELETE statement.
OleDbCommand cmd = m_conn.CreateCommand();
cmd.CommandText = strSql;
cmd.ExecuteNonQuery();
}
public int GetScalar(string strSql)
{
OleDbCommand cmd = m_conn.CreateCommand();
cmd.CommandText = strSql;
int iNewRecordID;
iNewRecordID = int.Parse(cmd.ExecuteScalar().ToString());
return iNewRecordID;
}
}
}