根据后台树形结构数据表创建家族树形图
1.数据库结构
其中NodePkey存储父节点PKey,如若是根节点则为0
2.使用SVG动态创建树图形
(1)根据数据库原始数据获取,家族树列表,家族按家族代及每代人数表。根绝两基础数据集合,动态计算出布局以及需要连线的具体位置。
private ObjectTable ConvertData() { ObjectTable treeDataTable = new ObjectTable(); DataTable treeData = new DataTable(); DataTable treeLev = new DataTable(); #region Load data. if (IsLogin) { string dataSqlLogin = "SELECT PKey,NodePKey,IsFAlive,IsMAlive,Father,Mather,Lev" + " FROM View_FaimlyTree" + " WHERE FirstName=@FirstName AND PartPKey=@PartPKey" + " Order By Lev,NodePKey"; ODBParameterCollection opcData = new ODBParameterCollection(); opcData.Add("@FirstName", DropDownList3.SelectedValue); opcData.Add("@PartPKey", DropDownList2.SelectedValue); treeData = DataAccess.ExecuteDataTable(dataSqlLogin, opcData); string levSqlLogin = "SELECT Count(Lev) AS C,Lev FROM View_FaimlyTree" + " WHERE FirstName=@FirstName AND PartPKey=@PartPKey" + " GROUP BY Lev Order By C DESC"; ODBParameterCollection opcLev = new ODBParameterCollection(); opcLev.Add("@FirstName", DropDownList3.SelectedValue); opcLev.Add("@PartPKey", DropDownList2.SelectedValue); treeLev = DataAccess.ExecuteDataTable(levSqlLogin, opcLev); } else { string dataSqlLogin = "SELECT PKey,NodePKey,IsFAlive,IsMAlive,Father,Mather,Lev" + " FROM View_FaimlyTree" + " WHERE FirstName=@FirstName AND PartPKey=@PartPKey AND ISFAlive = 0 AND IsMAlive = 0" + " Order By Lev,NodePKey"; ODBParameterCollection opcData = new ODBParameterCollection(); opcData.Add("@FirstName", DropDownList3.SelectedValue); opcData.Add("@PartPKey", DropDownList2.SelectedValue); treeData = DataAccess.ExecuteDataTable(dataSqlLogin, opcData); string levSqlLogin = "SELECT Count(Lev) AS C,Lev FROM View_FaimlyTree" + " WHERE FirstName=@FirstName AND PartPKey=@PartPKey AND ISFAlive = 0 AND IsMAlive = 0" + " GROUP BY Lev Order By C DESC"; ODBParameterCollection opcLev = new ODBParameterCollection(); opcLev.Add("@FirstName", DropDownList3.SelectedValue); opcLev.Add("@PartPKey", DropDownList2.SelectedValue); treeLev = DataAccess.ExecuteDataTable(levSqlLogin, opcLev); } #endregion #region Load data withe sytle,lines. if (treeData.Rows.Count > 0) { int maxWidth = ((int)treeLev.Rows[0]["C"]) * 80; int middleLeft = maxWidth / 2 - 30; int count = 0; int lev = 0; //--------control the modle of the node. int width = 120; int innerWidth = 100; int innerHeight = 50; int height = 80; //-------------------------------------. if (maxWidth < 500) { middleLeft = 470; } #region Math the Point should be lining. foreach (DataRow treeDataDr in treeData.Rows) { if (lev != (int)treeDataDr["Lev"]) { count = 0; } else { count++; } lev = (int)treeDataDr["Lev"]; if (lev == 0) { count--; } int left = middleLeft - ((width / 2) * ((int)treeLev.Select("Lev ='" + lev.ToString() + "'")[0]["C"]) - innerWidth / 2); ObjectRow objRow = new ObjectRow(); Point tPoint = new Point(); Point dPoint = new Point(); int top = ((int)treeDataDr["Lev"]) * height; tPoint.X = left + width * count + innerWidth / 2; tPoint.Y = top; dPoint.X = left + width * count + innerWidth / 2; dPoint.Y = top + height / 2 + 10; objRow.Id = treeDataDr["PKey"].ToString(); objRow["Father"] = new ObjectCell(treeDataDr["Father"]); objRow["Mather"] = new ObjectCell(treeDataDr["Mather"]); objRow["IsFAlive"] = new ObjectCell(treeDataDr["IsFAlive"]); objRow["IsMAlive"] = new ObjectCell(treeDataDr["IsMAlive"]); objRow["Lev"] = new ObjectCell(treeDataDr["Lev"]); objRow["NodePKey"] = new ObjectCell(treeDataDr["NodePKey"]); objRow["Count"] = new ObjectCell(count); objRow["tPoint"] = new ObjectCell(tPoint); objRow["dPoint"] = new ObjectCell(dPoint); objRow["sPoint"] = new ObjectCell(new Point(0, 0)); objRow["Y"] = new ObjectCell(top); objRow["X"] = new ObjectCell(left + width * count); objRow["Line"] = new ObjectCell(""); objRow["innerWidth"] = new ObjectCell(innerWidth); objRow["innerHeight"] = new ObjectCell(innerHeight); treeDataTable.Rows.Add(objRow); } #endregion #region lining. foreach (ObjectRow or in treeDataTable.Rows) { if (or["NodePKey"].value.ToString().Trim() != "0") { Point sPoint = new Point(); Point ePoint = new Point(); ePoint = (Point)or["tPoint"].value; sPoint = (Point)treeDataTable.Rows.Find(delegate(ObjectRow n) { return n.Id == or["NodePKey"].ToString(); })["dPoint"].value; or["sPoint"] = new ObjectCell(sPoint); } } #endregion } #endregion return treeDataTable; }
(2) 转换数据加入SVG标签以及SVG连线
private void SVGDraw(ObjectTable data) { // rect : X,Y,width,height,F-X,F-Y,M-X,F-Y,F,M string nodeBlock = "<svg version='1.1' style='position:absolute; 100%; height: 100%' xmlns:svg='http://www.w3.org/2000/svg'>" + "<rect x='{0}' y='{1}' rx='5' ry='5' width='{2}' height='{3}'" + "style='fill:#00CC99;stroke:black;text-align:center;stroke-1;opacity:1'/>" + "<text x='{4}' y='{5}' dx='5' dy='2' textLength='90'>父:{8}</text>" + "<text x='{6}' y='{7}' dx='5' dy='2' textLength='90'>母:{9}</text></svg>"; string lineBlock = "<svg width='100%' style='position:absolute;' height='100%' version='1.1' xmlns='http://www.w3.org/2000/svg'>" + "<line x1='{0}' y1='{1}' x2='{2}' y2='{3}' style='stroke:rgb(99,99,99);stroke-1'/></svg>"; foreach (ObjectRow or in data.Rows) { int X = (int)or["X"].value; int Y = (int)or["Y"].value; int width = (int)or["innerWidth"].value; int height = (int)or["innerHeight"].value; int F_X = (int)or["X"].value; int F_Y = (int)or["Y"].value + 20; int M_X = (int)or["X"].value; int M_Y = (int)or["Y"].value + 40; object father = or["Father"].value; object mather = or["Mather"].value; int x0 = ((Point)or["sPoint"].value).X; int y0 = ((Point)or["sPoint"].value).Y; int x1 = ((Point)or["tPoint"].value).X; int y1 = ((Point)or["tPoint"].value).Y; if (or["NodePKey"].value.ToString().Trim() != "0") { or["NodeBlock"] = new ObjectCell(string.Format(nodeBlock, X, Y, width, height, F_X, F_Y, M_X, M_Y, father, mather)); or["LineBlock"] = new ObjectCell(string.Format(lineBlock, x0, y0, x1, y1)); } else { or["NodeBlock"] = new ObjectCell(string.Format(nodeBlock, X, Y, width, height, F_X, F_Y, M_X, M_Y, father, mather)); or["LineBlock"] = new ObjectCell(""); } } #region load template. string templatePath = Page.MapPath("~/Templates/Tree.template"); TemplateManager templateManager = TemplateManager.FromFile(templatePath); templateManager.SetValue("Data", data); output = templateManager.Process(); #endregion }
3.运行效果