热门:网页模板.net视频教程JQueryMVCjsonExtJs源码示例三级联动JQuery菜单
您现在的位置:.Net中文社区>> Silverlight>>正文内容

Silverlight中使用递归构造关系图

发布时间:2010年08月09日点击数: 佚名

 

这两天遇到一个问题,项目中需要在silverlight中使用连接图的方式来显示任务之间的关系,总体有父子和平行两种,昨天在改同事的代码,一直出问题,索性晚上写了一下实现方法。

需求:

有一个List对象中存了若干个Task,这些Task对象通过ParentID属性进行关联,现在要求将这个List中的任务使用图的方式形成如父子关系和平行关系的图示例如下图:

image

实现方法思考

刚开始接到这个任务我就想着递归应该可以搞定了,但是仔细考虑才发现每个任务的子任务需要在一定区域内才行,需要计算子级和子级之间的距离,如果使用递归,例如上图的元素“12”的位置就没有办法很好确定了。

我决定将途中的节点抽象为一个类,这个类至少应该含有上边界top,左边届left及节点的名称等属性,然后从这个List对象中构造出每个节点的属性。

实现步骤

1,首先我们为图模拟一个数据源,注意其中的任务是通过ParentID关联的

  1. private static List<Task> listTask;  
  2.         public MainPage()  
  3.         {  
  4.             InitializeComponent();  
  5.             listTask = new List<Task>();  
  6.             listTask.Add(new Task() { ID = 1, ParentID = 0, Name = "1" });  
  7.             listTask.Add(new Task() { ID = 2, ParentID = 1, Name = "11" });  
  8.             listTask.Add(new Task() { ID = 3, ParentID = 1, Name = "12" });  
  9.             listTask.Add(new Task() { ID = 4, ParentID = 2, Name = "21" });  
  10.             listTask.Add(new Task() { ID = 5, ParentID = 2, Name = "22" });  
  11.             listTask.Add(new Task() { ID = 6, ParentID = 3, Name = "31" });  
  12.             listTask.Add(new Task() { ID = 7, ParentID = 3, Name = "32" });  
  13.             listTask.Add(new Task() { ID = 8, ParentID = 3, Name = "33" });  
  14.             listTask.Add(new Task() { ID = 9, ParentID = 4, Name = "42" });  
  15.             listTask.Add(new Task() { ID = 10, ParentID =4, Name = "42" });  
  16.             listTask.Add(new Task() { ID = 11, ParentID =3, Name = "34" });  
  17.             listTask.Add(new Task() { ID = 12, ParentID = 5, Name = "51" });  
  18.             listTask.Add(new Task() { ID = 13, ParentID = 8, Name = "81" });  
  19.             this.Loaded += new RoutedEventHandler(MainPage_Loaded);  
  20.         } 

 

2,然后我们为要生成的图中节点构造一个类

  1. class TaskPro  
  2.         {  
  3.             public Task task { setget; }  
  4.             public double top { setget; }  
  5.             public double left { setget; }  
  6.             public int index { setget; }//这是为了找到节点在某层的位置来计算left  
  7.         } 

 

3,使用递归将List中的数据做初步整理,存入一个List<TaskPro>中,此时节点对象将具备top属性,上边距搞定。

  1. void AddMethod(Task task)  
  2.         {  
  3.             if (task.ParentID == 0)  
  4.             {  
  5.                 listOfTaskPro.Add(new TaskPro() { task = task, top = 0, index = 0, left = 0  });  
  6.             }  
  7.             else  
  8.             {  
  9.                 var t=listTask.Where(m=>m.ID==task.ParentID).FirstOrDefault();  
  10.                 var tpro=listOfTaskPro.Where(m=>m.task.ID==t.ID).FirstOrDefault();  
  11.                 listOfTaskPro.Add(new TaskPro() { task=task, index=0, top=tpro.top+50, left=0 });  
  12.             }  
  13.             foreach (Task t in listTask.Where(m=>m.ParentID==task.ID).ToList())  
  14.             {  
  15.                 AddMethod(t);           
  16.             }  
  17.         } 

 

4,我们需要算出节点对象的左边距,在第3步中我没能找到方法,于是想到利用每一级的元素个数来计算每个节点的位置,然后使用每一级的平均节点距离*节点的索引便可得到left

  1. //构造各层及数量  
  2.             foreach (TaskPro t in listOfTaskPro)  
  3.             {  
  4.                 bool IsExist = false;  
  5.                 foreach (TaskCount tc in listTopAndTasks)  
  6.                 {  
  7.                     IsExist = tc.Top==t.top?true:false;  
  8.                 }  
  9.                 if (!IsExist)  
  10.                 {  
  11.                     listTopAndTasks.Add(new TaskCount() { Top = t.top, Tasks = new List<Task>() });  
  12.                 }  
  13.                 var topAndTasks = listTopAndTasks.Where(m => m.Top == t.top).FirstOrDefault();  
  14.                 topAndTasks.Tasks.Add(t.task);  
  15.             }  
  16.             //构造index  
  17.             foreach (TaskPro t in listOfTaskPro)  
  18.             {  
  19.                 for (int i = 0; i < listTopAndTasks.Count; i++)  
  20.                 {  
  21.                     for (int j = 0; j < listTopAndTasks[i].Tasks.Count; j++)  
  22.                     {  
  23.                         if (listTopAndTasks[i].Tasks[j].ID == t.task.ID)  
  24.                         {  
  25.                             t.index = j + 1;  
  26.                         }  
  27.                     }  
  28.                 }  
  29.             }  
  30.             //构造left  
  31.             for (int i = 0; i < listOfTaskPro.Count; i++)  
  32.             {  
  33.                 if (listOfTaskPro[i].task.ParentID == 0)  
  34.                 {  
  35.                     listOfTaskPro[i].left = this.canvas1.Width / 2;  
  36.                 }  
  37.                 else  
  38.                 {  
  39.                     var childCount = listOfTaskPro.Where(m => m.task.ParentID == listOfTaskPro[i].task.ParentID).Count();  
  40.                     var parentLeft = listOfTaskPro.Where(m => m.task.ID == listOfTaskPro[i].task.ParentID).FirstOrDefault().left;  
  41.                     var perLength = parentLeft * 1.5 / (childCount + 1);  
  42.                     listOfTaskPro[i].left = listOfTaskPro[i].index * perLength;  
  43.                 }  
  44.             } 

 

5,至此,节点对象已经具备了left,top属性,我们只需要找到每个节点的父节点即可将两个几点的坐标确定,进而进行划线的操作了。

  1. foreach (TaskPro t in listOfTaskPro)  
  2.             {  
  3.                 AddBtn(t.task.Name, t.left, t.top);  
  4.                 if (t.task.ParentID != 0)  
  5.                 {  
  6.                     TaskPro tp = listOfTaskPro.Where(m => m.task.ID == t.task.ParentID).FirstOrDefault();  
  7.                     AddLine(tp.left + buttonWidth / 2, tp.top + buttonHeight, t.left + buttonWidth / 2, t.top);  
  8.                 }  
  9.             } 

 

6,添加按钮及划线的方法

  1. #region 添加按钮及线条  
  2.         double buttonHeight = 20;  
  3.         double buttonWidth = 50;  
  4.  
  5.         void AddBtn(string content, double left, double top)  
  6.         {  
  7.             Button btn = new Button();  
  8.             btn.Content = content;  
  9.             btn.Width = buttonWidth;  
  10.             btn.Height = buttonHeight;  
  11.             this.canvas1.Children.Add(btn);  
  12.             Canvas.SetLeft(btn, left);  
  13.             Canvas.SetTop(btn, top);  
  14.         }  
  15.  
  16. //画线方法,只需要有起始亮点的坐标即可 
  17.  
  18.         void AddLine(double startLeft, double startTop, double endLeft, double endTop)  
  19.         {  
  20.             Path p = new Path();  
  21.             LineGeometry geometry = new LineGeometry();  
  22.             SolidColorBrush brush = new SolidColorBrush();  
  23.             brush.Color = Colors.Black;  
  24.             geometry.StartPoint = new Point(startLeft, startTop);  
  25.             geometry.EndPoint = new Point(endLeft, endTop);  
  26.             p.Data = geometry;  
  27.             p.Stroke = brush;  
  28.             p.StrokeThickness = 1;  
  29.             canvas1.Children.Add(p);  
  30.         }  
  31.         #endregion 

 

运行一下,如上图。
之前没有使用递归的方法是只有这样的:


  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Net;  
  5. using System.Windows;  
  6. using System.Windows.Controls;  
  7. using System.Windows.Documents;  
  8. using System.Windows.Input;  
  9. using System.Windows.Media;  
  10. using System.Windows.Media.Animation;  
  11. using System.Windows.Shapes;  
  12.  
  13. namespace SilverlightApplication2  
  14. {  
  15.     public class Task  
  16.     {  
  17.         public int ID { setget; }  
  18.         public int ParentID { setget; }  
  19.         public string Name { setget; }  
  20.     }  
  21.     public partial class MainPage : UserControl  
  22.     {  
  23.         private static List<Task> listTask;  
  24.         public MainPage()  
  25.         {  
  26.             InitializeComponent();  
  27.             listTask = new List<Task>();  
  28.             listTask.Add(new Task() { ID = 1, ParentID = 0, Name = "1" });  
  29.             listTask.Add(new Task() { ID = 2, ParentID = 1, Name = "11" });  
  30.             listTask.Add(new Task() { ID = 3, ParentID = 1, Name = "12" });  
  31.             listTask.Add(new Task() { ID = 4, ParentID = 2, Name = "21" });  
  32.             listTask.Add(new Task() { ID = 5, ParentID = 2, Name = "22" });  
  33.             listTask.Add(new Task() { ID = 6, ParentID = 3, Name = "31" });  
  34.             listTask.Add(new Task() { ID = 7, ParentID = 3, Name = "32" });  
  35.             listTask.Add(new Task() { ID = 8, ParentID = 3, Name = "33" });  
  36.             listTask.Add(new Task() { ID = 9, ParentID = 4, Name = "42" });  
  37.             listTask.Add(new Task() { ID = 10, ParentID =4, Name = "42" });  
  38.             listTask.Add(new Task() { ID = 11, ParentID =3, Name = "34" });  
  39.             listTask.Add(new Task() { ID = 12, ParentID = 5, Name = "51" });  
  40.             listTask.Add(new Task() { ID = 13, ParentID = 8, Name = "81" });  
  41.             this.Loaded += new RoutedEventHandler(MainPage_Loaded);  
  42.         }  
  43.         void MainPage_Loaded(object sender, RoutedEventArgs e)  
  44.         {              
  45.             AddAll();          
  46.         }         
  47.         class TaskPro  
  48.         {  
  49.             public Task task { setget; }  
  50.             public double top { setget; }  
  51.             public double left { setget; }  
  52.             public int index { setget; }  
  53.         }  
  54.         class TaskCount  
  55.         {  
  56.             public double Top { setget; }  
  57.             public List<Task> Tasks { setget; }  
  58.         }  
  59.         static List<TaskPro> listTaskPro = new List<TaskPro>();  
  60.         static List<TaskCount> listTopAndTasks = new List<TaskCount>();  
  61.         void AddAll()  
  62.         {  
  63.             foreach(Task t in listTask)  
  64.             {  
  65.                 if (t.ParentID == 0)  
  66.                 {  
  67.                     listTaskPro.Add(new TaskPro() { task = t, index = 1, left = this.canvas1.Width / 2, top = 0 });  
  68.                 }  
  69.                 else  
  70.                 {  
  71.                     for(int i=0;i<listTaskPro.Count;i++)  
  72.                     {  
  73.                         if (t.ParentID == listTaskPro[i].task.ID)  
  74.                         {  
  75.                             listTaskPro.Add(new TaskPro() { task = t, top = listTaskPro[i].top + 80, index = 0, left = 0 });  
  76.                         }  
  77.                     }  
  78.                 }  
  79.             }  
  80.  
  81.             #region 汇总层及层内的元素个数  
  82.             foreach (TaskPro t in listTaskPro)  
  83.             {  
  84.                 bool IsExist = false;  
  85.                 foreach(TaskCount tc in listTopAndTasks)  
  86.                 {  
  87.                     if(tc.Top==t.top)  
  88.                     {  
  89.                         IsExist = true;  
  90.                     }  
  91.                 }  
  92.                 if(!IsExist)  
  93.                 {  
  94.                     listTopAndTasks.Add(new TaskCount() { Top=t.top, Tasks=new List<Task>() });  
  95.                 }  
  96.                 var topAndTasks = listTopAndTasks.Where(m=>m.Top==t.top).FirstOrDefault();  
  97.                 topAndTasks.Tasks.Add(t.task);                
  98.             }  
  99.             #endregion  
  100.  
  101.             foreach (TaskPro t in listTaskPro)  
  102.             {  
  103.                 for (int i = 0; i < listTopAndTasks.Count;i++ )  
  104.                 {  
  105.                     for (int j = 0; j < listTopAndTasks[i].Tasks.Count;j++ )  
  106.                     {  
  107.                         if (listTopAndTasks[i].Tasks[j].ID == t.task.ID)  
  108.                         {  
  109.                             t.index = j + 1;  
  110.                         }  
  111.                     }  
  112.                 }  
  113.             }  
  114.  
  115.             for (int i = 0; i < listTaskPro.Count; i++)  
  116.             {  
  117.                 if (listTaskPro[i].task.ParentID == 0)  
  118.                 {  
  119.                     listTaskPro[i].left = this.canvas1.Width / 2;  
  120.                 }  
  121.                 else  
  122.                 {  
  123.                     var childCount = listTaskPro.Where(m => m.task.ParentID == listTaskPro[i].task.ParentID).Count();  
  124.                     var parentLeft = listTaskPro.Where(m => m.task.ID == listTaskPro[i].task.ParentID).FirstOrDefault().left;  
  125.                     var perLength = parentLeft*1.5 / (childCount + 1);  
  126.                     listTaskPro[i].left=listTaskPro[i].index*perLength;                                      
  127.                 }  
  128.             }  
  129.             foreach (TaskPro t in listTaskPro)  
  130.             {  
  131.                 AddBtn(t.task.Name, t.left, t.top);  
  132.                 if(t.task.ParentID!=0)  
  133.                 {  
  134.                     TaskPro tp = listTaskPro.Where(m=>m.task.ID==t.task.ParentID).FirstOrDefault();  
  135.                     AddLine(tp.left+buttonWidth/2, tp.top+buttonHeight, t.left+buttonWidth/2, t.top);  
  136.                 }  
  137.             }  
  138.         }  
  139.         double buttonHeight = 20;  
  140.         double buttonWidth = 50;  
  141.         void AddBtn(string content,double left,double top)  
  142.         {  
  143.             Button btn = new Button();  
  144.             btn.Content = content;  
  145.             btn.Width = buttonWidth;  
  146.             btn.Height = buttonHeight;  
  147.             this.canvas1.Children.Add(btn);  
  148.             Canvas.SetLeft(btn, left);  
  149.             Canvas.SetTop(btn, top);  
  150.         }  
  151.  
  152.         void AddLine(double startLeft,double startTop,double endLeft,double endTop)  
  153.         {  
  154.             Path p = new Path();  
  155.             LineGeometry geometry = new LineGeometry();  
  156.             SolidColorBrush brush = new SolidColorBrush();              
  157.  
  158.             brush.Color = Colors.Black;  
  159.             geometry.StartPoint = new Point(startLeft, startTop);  
  160.             geometry.EndPoint = new Point(endLeft, endTop);  
  161.             p.Data = geometry;  
  162.             p.Stroke = brush;  
  163.             p.StrokeThickness = 1;  
  164.  
  165.             canvas1.Children.Add(p);  
  166.         }  
  167.     }  

 

本站热点业务

更多模板/案例展示

关于我们 | 联系我们 | 团队日志 | 网站地图 | 网站合作