支持树型的GridView
实现思路: 继承自Gridview,处理gridview的数据源,使其在帮定时,就已经按照树型菜单顺序排列好,那样只需在帮定处理其图片是javascript脚本就可以了
。
源代码下载:https://files.cnblogs.com/wschacker/TreeListView.rar
效果图:

代码:

Code
using System;
using System.Data;
using System.Collections;
using System.Collections.Specialized;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Web;
using System.IO;
using System.Drawing;
using System.Drawing.Design;
using System.Reflection;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
[ToolboxData("<{0}:TreeListView runat=server></{0}:TreeListView>"), DefaultProperty("Text")]
public class TreeListView : GridView, IPostBackDataHandler

{
public TreeListView()

{
base.AllowPaging = false;
base.AllowSorting = false;
base.AutoGenerateColumns = false;

}


Tree的属性设置#region Tree的属性设置
private int _nodeColumnIndex;

/**//// <summary>
/// 显示树型的列 Index
/// </summary>
public int NodeColumnIndex

{

get
{ return this._nodeColumnIndex; }
set

{
_nodeColumnIndex = value;
}
}

private string _columnKey;

/**//// <summary>
/// Key字段
/// </summary>
public string ColumnKey

{

get
{ return _columnKey; }
set

{
_columnKey = value;
}
}

private string _parentKey;

/**//// <summary>
/// 指向父节点的字段
/// </summary>
public string ParentKey

{
set

{
_parentKey = value;
}
}

private string _sortKey;

/**//// <summary>
/// 排序字段
/// </summary>
public string SortKey

{

set
{ _sortKey = value; }
}

private object _rootNodeFlag;

/**//// <summary>
/// 根节点的标记 这里采用 ParentKey为什么字符
/// </summary>
public object RootNodeFlag

{
set

{
_rootNodeFlag = value;
}
}

private static string _treeImageFolder = "/Images/Tree/";
public static string TreeImageFolder

{
get

{
return _treeImageFolder;
}
set

{
_treeImageFolder = value;
}
}

private int _expendDepth = 0;

/**//// <summary>
/// 展开的深度
/// </summary>
public int ExpendDepth

{
get

{
return _expendDepth;
}

set
{ _expendDepth = value; }
}
#endregion

public override object DataSource

{
get

{
return base.DataSource;
}
set

{
DataTable dtSource = new DataTable();
if (value is DataSet && ((DataSet)value).Tables.Count > 0)

{
DataSet ds = value as DataSet;
dtSource = OrderData(ds.Tables[0]);
}
else

{
throw new Exception("DataSource is not DataSet!");
}
base.DataSource = dtSource;
}
}

DataTable OrderData(DataTable dtSource)

{
DataTable dtResult = dtSource.Clone();
dtResult.Columns.Add("TreeListView$Row$Depth", typeof(int));
dtResult.Columns.Add("TreeListView$Row$IsLeaf", typeof(bool));
dtResult.Columns.Add("TreeListView$Row$IsBottom", typeof(bool));
dtResult.Columns.Add("TreeListView$Row$ParentRow", typeof(DataRow));
dtResult.Columns.Add("TreeList$ViewRow$ClientID", typeof(string));
RecursionOrderData(dtSource, dtResult, _rootNodeFlag, -1, null);
return dtResult;
}
string FormatToRowFilter(object val)

{
Type type = val.GetType();
if (type == typeof(string))

{
return string.Format("'{0}'", val.ToString().Replace("'", "''"));
}
else if (type == typeof(Guid))

{
return string.Format("'{0}'", val);
}
else if (type.IsValueType)

{
return val.ToString();
}
else

{
return string.Format("'{0}'", val.ToString().Replace("'", "''"));
}
}
bool RecursionOrderData(DataTable dtSource, DataTable dtResult, object parentID, int depth, DataRow parentDatarow)

{
DataView dv = new DataView(dtSource);
dv.RowFilter = string.Format("{0}={1}", _parentKey, FormatToRowFilter(parentID));
dv.Sort = _sortKey;
DataRow dr = null;
depth++;
for (int i = 0; i < dv.Count; i++)

{
dr = dtResult.NewRow();

for (int j = 0; j < dv[i].Row.ItemArray.Length; j++)

{
dr[j] = dv[i][j];
}

if (i == dv.Count - 1) //isBottom

{
dr["TreeListView$Row$IsBottom"] = true;
}
else

{
dr["TreeListView$Row$IsBottom"] = false;
}
dr["TreeListView$Row$Depth"] = depth;
dr["TreeListView$Row$ParentRow"] = parentDatarow;
if (depth == 0)

{
dr["TreeList$ViewRow$ClientID"] = Guid.NewGuid().ToString();
}
else

{
dr["TreeList$ViewRow$ClientID"] = parentDatarow["TreeList$ViewRow$ClientID"].ToString() + "/" + Guid.NewGuid().ToString();
}

dtResult.Rows.Add(dr);
dr["TreeListView$Row$IsLeaf"] = !RecursionOrderData(dtSource, dtResult, dv[i][_columnKey], depth, dr);
}

return dv.Count > 0;
}

public override bool AllowPaging

{
get

{
return base.AllowPaging;
}
set

{
base.AllowPaging = false;
}
}

public override bool AutoGenerateColumns

{
get

{
return base.AutoGenerateColumns;
}
set

{
base.AutoGenerateColumns = false;
}
}


重载:CreateRow#region 重载:CreateRow
protected override GridViewRow CreateRow(int rowIndex, int dataSourceIndex, DataControlRowType rowType, DataControlRowState rowState)

{
return new TreeListViewRow(rowIndex, dataSourceIndex, rowType, rowState);
}
#endregion


重写:Rows#region 重写:Rows
private TreeListViewRowCollection _rowsCollection;
[Browsable(false)]
public new TreeListViewRowCollection Rows

{
get

{
ArrayList _rowsArray = new ArrayList();
for (int i = 0; i < base.Rows.Count; i++)

{
_rowsArray.Add((TreeListViewRow)base.Rows[i]);
}
this._rowsCollection = new TreeListViewRowCollection(_rowsArray);
return this._rowsCollection;
}
}
#endregion


重载:OnInit#region 重载:OnInit
protected override void OnInit(EventArgs e)

{
base.OnInit(e);
Page.RegisterRequiresPostBack(this);

if (!Page.ClientScript.IsClientScriptIncludeRegistered("JS_TreeListView"))

{
this.Page.ClientScript.RegisterClientScriptInclude(this.GetType(), "JS_TreeListView", Page.ClientScript.GetWebResourceUrl(this.GetType(), "GoldMantis.Web.UI.Resource.TreeListView.JS_TreeListView.js"));
}
}
#endregion


IPostBackDataHandler Members#region IPostBackDataHandler Members

public bool LoadPostData(string postDataKey, NameValueCollection postCollection)

{
return false;
}

public void RaisePostDataChangedEvent()

{

}

#endregion


方法:RenderCheckBoxExField#region 方法:RenderCheckBoxExField

/**//// <summary>
/// 处理CheckBoxExField类型的列
/// </summary>
private void RenderCheckBoxExField()

{
if (!this.ShowHeader && !this.ShowFooter)

{
return;
}

foreach (DataControlField field in Columns)

{
if (field is CheckBoxExField)

{
int checkBoxExFieldIndex = Columns.IndexOf(field) + this.GetAutoGenerateButtonCount();
foreach (GridViewRow row in Rows)

{
if (row.RowType == DataControlRowType.Header)

{
row.Cells[checkBoxExFieldIndex].Controls.Clear();
}
if (row.RowType == DataControlRowType.DataRow)

{
row.Cells[checkBoxExFieldIndex].Controls[0].ID = "cbChoose";
((CheckBox)row.Cells[checkBoxExFieldIndex].Controls[0]).Attributes.Add("onclick", "ChooseTree(this);");
}
}


注册脚本#region 注册脚本
string script = @"
var modifyId = """" ;
var choosedId = """";
var choosedIndex;
function ChooseTree(obj)
{
var cTrId = obj.parentElement.parentElement.parentElement.id;
var treeTable = document.getElementById("""+this.ClientID+@""");
for( var i = 0; i < treeTable.rows.length; i++ )
{
if( treeTable.rows[i].id.indexOf(cTrId) != -1 && treeTable.rows[i].id != cTrId )
{
document.getElementById(treeTable.rows[i].id+""_cbChoose"").checked = obj.checked;
}
}
choosedId = """";
choosedIndex = new Array();
for( var i = 1; i < treeTable.rows.length; i++ )
{
if( document.getElementById(treeTable.rows[i].id+""_cbChoose"").checked )
{
choosedId += treeTable.rows[i].id.substring(treeTable.rows[i].id.lastIndexOf(""/"")+1) + "","";
choosedIndex.push(i);
}
}
choosedId = choosedId.substring(0,choosedId.length-1);
}";
if (!Page.ClientScript.IsStartupScriptRegistered("TreeListView_CheckBoxExField"))

{
Page.ClientScript.RegisterStartupScript(GetType(), "TreeListView_CheckBoxExField", script, true);
}
#endregion
}
}
}

方法:GetAutoGenerateButtonCount#region 方法:GetAutoGenerateButtonCount
private int GetAutoGenerateButtonCount()

{
int num = 0;
if (this.AutoGenerateDeleteButton || this.AutoGenerateEditButton || this.AutoGenerateSelectButton)

{
num = 1;
}
return num;
}
#endregion
#endregion

protected override void Render(HtmlTextWriter writer)

{
RenderCheckBoxExField();
base.Render(writer);
}
}
public class TreeListViewRow : GridViewRow

{
public TreeListViewRow(int rowIndex, int dataItemIndex, DataControlRowType rowType, DataControlRowState rowState)
: base(rowIndex, dataItemIndex, rowType, rowState)

{

}
protected override void OnPreRender(EventArgs e)

{
base.OnPreRender(e);
if (this.RowType == DataControlRowType.DataRow)

{
if (this.Parent.Parent is TreeListView)

{
TreeListView treeListView = this.Parent.Parent as TreeListView;
DataRow dr = ((DataTable)treeListView.DataSource).Rows[this.DataItemIndex] as DataRow;
string str = GetTreeNodeImg(dr, Convert.ToBoolean(dr["TreeListView$Row$IsLeaf"]), Convert.ToBoolean(dr["TreeListView$Row$IsBottom"]));
this.Cells[treeListView.NodeColumnIndex].Text = str + this.Cells[treeListView.NodeColumnIndex].Text;
this.ID = dr["TreeList$ViewRow$ClientID"].ToString();
if (treeListView.ExpendDepth > 0)

{
this.Style["display"] = treeListView.ExpendDepth >= Convert.ToInt32(dr["TreeListView$Row$Depth"]) ? "block" : "none";
}
}
}
}


获取Tree的图片#region 获取Tree的图片
string GetTreeNodeImg(DataRow dr, bool isLeaf, bool isBottom)

{
return GetTreeNodeOtherImg(dr) + GetTreeNodeLastImg(isLeaf, isBottom);
}
string GetTreeNodeOtherImg(DataRow dr)

{
if (dr["TreeListView$Row$ParentRow"] != null&&!dr["TreeListView$Row$ParentRow"].Equals(DBNull.Value))

{
DataRow drParentRow = dr["TreeListView$Row$ParentRow"] as DataRow;
bool parentIsBottom = Convert.ToBoolean(drParentRow["TreeListView$Row$IsBottom"]);
if (parentIsBottom)

{
return GetTreeNodeOtherImg(drParentRow) + string.Format("<img src={0} align=absmiddle>", TreeListView.TreeImageFolder + "white.gif");
}
else

{
return GetTreeNodeOtherImg(drParentRow) + string.Format("<img src={0} align=absmiddle>", TreeListView.TreeImageFolder + "i.gif");
}

}
else

{
return string.Empty;
}
}
string GetTreeNodeLastImg(bool isLeaf, bool isBottom)

{
//最后靠近的那个Image
string lastImg = string.Empty;
if (isLeaf)

{
if (isBottom)

{
lastImg = string.Format("<img src={0} align=absmiddle>", TreeListView.TreeImageFolder + "l.gif");
}
else

{
lastImg = string.Format("<img src={0} align=absmiddle>",TreeListView.TreeImageFolder + "t.gif");
}
}
else

{
if (isBottom)

{

lastImg = string.Format("<img src={0} align=absmiddle onclick='ClickNode(this,true,\"
{1}\");' style=\"cursor: hand\">", TreeListView.TreeImageFolder + "lminus.gif", this.Parent.Parent.ClientID);
}
else

{

lastImg = string.Format("<img src={0} align=absmiddle onclick='ClickNode(this,true,\"
{1}\");' style=\"cursor: hand\">", TreeListView.TreeImageFolder + "tminus.gif", this.Parent.Parent.ClientID);
}
}
return lastImg;

}
#endregion

Js代码实现折叠效果

Code

var lExpend = "lminus.gif";
var lPinch = "lplus.gif";
var tExpend = "tminus.gif";
var tPinch = "tplus.gif";

function ClickNode(img,isBottom,tableId)


{
var imgId = img.src.substring(img.src.lastIndexOf("/")+1);
var url = img.src.substring(0,img.src.lastIndexOf("/")+1);
var oldTrId = img.parentElement.parentElement.id;
var newTrId = oldTrId.substring(oldTrId.indexOf("_")+1);
if( isBottom)

{
if( imgId == lExpend)

{
img.src = url+ lPinch;
img.parentElement.id = lPinch;
PinchNode(newTrId,oldTrId,tableId);
}
else

{
img.src = url + lExpend;
img.parentElement.id = lExpend;
ExpendNode(newTrId,oldTrId,tableId);
}
}
else

{
if( imgId == tExpend )

{
img.src = url+ tPinch;
img.parentElement.id = tPinch;
PinchNode(newTrId,oldTrId,tableId);
}
else

{
img.src = url + tExpend;
img.parentElement.id = tExpend;
ExpendNode(newTrId,oldTrId,tableId);
}
}
}

function ExpendNode(newId,oldId,tableId)


{
var tree = document.getElementById(tableId);
for( var i = 0; i < tree.rows.length; i++ )

{
if( tree.rows[i].id.indexOf(newId) != -1 && tree.rows[i].id != oldId )

{
var isExpend = true;
var pId = tree.rows[i].id;
while( pId != oldId)

{
for( var j = 0; j < 2; j++ )
pId = pId.substring( 0,pId.lastIndexOf("/"));
var parent = document.getElementById(pId);
if( parent != null )

{
var tempId = parent.cells[2].id;
if( tempId == lExpend || tempId == tExpend || tempId == "")
;
else

{
isExpend = false;
break;
}
}
else
break;
}
if( isExpend )
tree.rows[i].style.display="block";
}
}
}

function PinchNode(newId,oldId,tableId)


{
var tree = document.getElementById(tableId);
for( var i = 0; i < tree.rows.length; i++ )

{
if( tree.rows[i].id.indexOf(newId) != -1 && tree.rows[i].id != oldId)

{
tree.rows[i].style.display = "none";
}
}
}