因为树比较大(可能有几千个点),一次全部加载速度不能接受。因此方案是动态加载:开始只列出少量一层数据,然后随着使用者不断点击节点,动态载入下级节点,这样因为每次点击只载入几十个节点,速度就可以接受了。
以下代码从 msdn 复制,可通过 treeview 控件的 PopulateNodesFromClient 等属性的说明找到这段代码。如果完全复制的话,执行时的效果是一个完全展开、写进所有数据的树,这看起来根本不是想要的效果。这时只需要改一个地方:增加树的 ExpandDepth="0" 属性。想想也是,默认树完全展开,不就是开始看到的效果?
加入了 ExpandDepth="0" 后,效果是,树只有初始节点,任何需要动态加入到节点都没有。这时点开一节点(+ 号),子节点无刷新加入;下级类似。查看 html 源文件,只有初始节点的 html 码,ok,这就是想要的。
有一点注意,通过正取设置 select,然后通过按钮或其它方式选中,把选中结果发给服务器,这时服务端程序可取得选中,然后此时返回的页面是包括树的所有已经加载数据的,即同服务器往返一次后,在查看 html 源,则树中所有已加入数据都在 html 里。这样往返的成本仍然很高,不过毕竟省下了读取数据环节。
通过 .net ajax 的 updatepanel 当然避免不了这些往返成本,可以通过 子框架,使树指向框架,这样就避免了树的往返,不过页面设计就分散了。。。
<%@ Page Language="C#" %>
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.SqlClient" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
void PopulateNode(Object sender, TreeNodeEventArgs e)
{
// Call the appropriate method to populate a node at a particular level.
switch(e.Node.Depth)
{
case 0:
// Populate the first-level nodes.
PopulateCategories(e.Node);
break;
case 1:
// Populate the second-level nodes.
PopulateProducts(e.Node);
break;
default:
// Do nothing.
break;
}
}
void PopulateCategories(TreeNode node)
{
// Query for the product categories. These are the values
// for the second-level nodes.
DataSet ResultSet = RunQuery("Select CategoryID, CategoryName From Categories");
// Create the second-level nodes.
if(ResultSet.Tables.Count > 0)
{
// Iterate through and create a new node for each row in the query results.
// Notice that the query results are stored in the table of the DataSet.
foreach (DataRow row in ResultSet.Tables[0].Rows)
{
// Create the new node. Notice that the CategoryId is stored in the Value property
// of the node. This will make querying for items in a specific category easier when
// the third-level nodes are created.
TreeNode newNode = new TreeNode();
newNode.Text = row["CategoryName"].ToString();
newNode.Value = row["CategoryID"].ToString();
// Set the PopulateOnDemand property to true so that the child nodes can be
// dynamically populated.
newNode.PopulateOnDemand = true;
// Set additional properties for the node.
newNode.SelectAction = TreeNodeSelectAction.Expand;
// Add the new node to the ChildNodes collection of the parent node.
node.ChildNodes.Add(newNode);
}
}
}
void PopulateProducts(TreeNode node)
{
// Query for the products of the current category. These are the values
// for the third-level nodes.
DataSet ResultSet = RunQuery("Select ProductName From Products Where CategoryID=" + node.Value);
// Create the third-level nodes.
if(ResultSet.Tables.Count > 0)
{
// Iterate through and create a new node for each row in the query results.
// Notice that the query results are stored in the table of the DataSet.
foreach (DataRow row in ResultSet.Tables[0].Rows)
{
// Create the new node.
TreeNode NewNode = new TreeNode(row["ProductName"].ToString());
// Set the PopulateOnDemand property to false, because these are leaf nodes and
// do not need to be populated.
NewNode.PopulateOnDemand = false;
// Set additional properties for the node.
NewNode.SelectAction = TreeNodeSelectAction.None;
// Add the new node to the ChildNodes collection of the parent node.
node.ChildNodes.Add(NewNode);
}
}
}
DataSet RunQuery(String QueryString)
{
// Declare the connection string. This example uses Microsoft SQL Server
// and connects to the Northwind sample database.
String ConnectionString = "server=localhost;database=NorthWind;Integrated Security=SSPI";
SqlConnection DBConnection = new SqlConnection(ConnectionString);
SqlDataAdapter DBAdapter;
DataSet ResultsDataSet = new DataSet();
try
{
// Run the query and create a DataSet.
DBAdapter = new SqlDataAdapter(QueryString, DBConnection);
DBAdapter.Fill(ResultsDataSet);
// Close the database connection.
DBConnection.Close();
}
catch(Exception ex)
{
// Close the database connection if it is still open.
if(DBConnection.State == ConnectionState.Open)
{
DBConnection.Close();
}
Message.Text = "Unable to connect to the database.";
}
return ResultsDataSet;
}
protected void Button1_Click(object sender, EventArgs e)
{
Response.Write(LinksTreeView.SelectedNode.Value);
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
<title>TreeView PopulateNodesFromClient Example</title>
</head>
<body>
<form id="form1" runat="server">
<h3>TreeView PopulateNodesFromClient Example</h3>
<asp:Button ID="Button1" runat="server" Text="Button" οnclick="Button1_Click" />
<asp:TreeView id="LinksTreeView"
Font-Names= "Arial"
ForeColor="Blue"
EnableClientScript="true"
PopulateNodesFromClient="true"
OnTreeNodePopulate="PopulateNode"
runat="server" ExpandDepth="0">
<Nodes>
<asp:TreeNode Text="Inventory"
SelectAction="Expand"
PopulateOnDemand="true" Value="-32768" />
</Nodes>
</asp:TreeView>
<br /><br />
<asp:Label id="Message" runat="server"/>
</form>
</body>
</html>