zoukankan      html  css  js  c++  java
  • 一起谈.NET技术,Silverlight中使用递归构造关系图 狼人:

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

      需求

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

    image

      实现方法思考

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

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

      实现步骤

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

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

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

    代码
    class TaskPro 
            { 
                
    public Task task { setget; } 
                
    public double top { setget; } 
                
    public double left { setget; } 
                
    public int index { setget; }//这是为了找到节点在某层的位置来计算left 
            }

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

    代码
    void AddMethod(Task task) 
            { 
                
    if (task.ParentID == 0
                { 
                    listOfTaskPro.Add(
    new TaskPro() { task = task, top = 0, index = 0, left = 0  }); 
                } 
                
    else 
                { 
                    var t
    =listTask.Where(m=>m.ID==task.ParentID).FirstOrDefault(); 
                    var tpro
    =listOfTaskPro.Where(m=>m.task.ID==t.ID).FirstOrDefault(); 
                    listOfTaskPro.Add(
    new TaskPro() { task=task, index=0, top=tpro.top+50, left=0 }); 
                } 
                
    foreach (Task t in listTask.Where(m=>m.ParentID==task.ID).ToList()) 
                { 
                    AddMethod(t);          
                } 
            }

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

    代码
    //构造各层及数量 
                foreach (TaskPro t in listOfTaskPro) 
                { 
                    
    bool IsExist = false
                    
    foreach (TaskCount tc in listTopAndTasks) 
                    { 
                        IsExist 
    = tc.Top==t.top?true:false
                    } 
                    
    if (!IsExist) 
                    { 
                        listTopAndTasks.Add(
    new TaskCount() { Top = t.top, Tasks = new List<Task>() }); 
                    } 
                    var topAndTasks 
    = listTopAndTasks.Where(m => m.Top == t.top).FirstOrDefault(); 
                    topAndTasks.Tasks.Add(t.task); 
                } 
                
    //构造index 
                foreach (TaskPro t in listOfTaskPro) 
                { 
                    
    for (int i = 0; i < listTopAndTasks.Count; i++
                    { 
                        
    for (int j = 0; j < listTopAndTasks[i].Tasks.Count; j++
                        { 
                            
    if (listTopAndTasks[i].Tasks[j].ID == t.task.ID) 
                            { 
                                t.index 
    = j + 1
                            } 
                        } 
                    } 
                } 
                
    //构造left 
                for (int i = 0; i < listOfTaskPro.Count; i++
                { 
                    
    if (listOfTaskPro[i].task.ParentID == 0
                    { 
                        listOfTaskPro[i].left 
    = this.canvas1.Width / 2
                    } 
                    
    else 
                    { 
                        var childCount 
    = listOfTaskPro.Where(m => m.task.ParentID == listOfTaskPro[i].task.ParentID).Count(); 
                        var parentLeft 
    = listOfTaskPro.Where(m => m.task.ID == listOfTaskPro[i].task.ParentID).FirstOrDefault().left; 
                        var perLength 
    = parentLeft * 1.5 / (childCount + 1); 
                        listOfTaskPro[i].left 
    = listOfTaskPro[i].index * perLength; 
                    } 
                }

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

    代码
    foreach (TaskPro t in listOfTaskPro) 
                { 
                    AddBtn(t.task.Name, t.left, t.top); 
                    
    if (t.task.ParentID != 0
                    { 
                        TaskPro tp 
    = listOfTaskPro.Where(m => m.task.ID == t.task.ParentID).FirstOrDefault(); 
                        AddLine(tp.left 
    + buttonWidth / 2, tp.top + buttonHeight, t.left + buttonWidth / 2, t.top); 
                    } 
                }

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

    代码
    #region 添加按钮及线条 
            
    double buttonHeight = 20
            
    double buttonWidth = 50

            
    void AddBtn(string content, double left, double top) 
            { 
                Button btn 
    = new Button(); 
                btn.Content 
    = content; 
                btn.Width 
    = buttonWidth; 
                btn.Height 
    = buttonHeight; 
                
    this.canvas1.Children.Add(btn); 
                Canvas.SetLeft(btn, left); 
                Canvas.SetTop(btn, top); 
            } 

    //画线方法,只需要有起始亮点的坐标即可

            
    void AddLine(double startLeft, double startTop, double endLeft, double endTop) 
            { 
                Path p 
    = new Path(); 
                LineGeometry geometry 
    = new LineGeometry(); 
                SolidColorBrush brush 
    = new SolidColorBrush(); 
                brush.Color 
    = Colors.Black; 
                geometry.StartPoint 
    = new Point(startLeft, startTop); 
                geometry.EndPoint 
    = new Point(endLeft, endTop); 
                p.Data 
    = geometry; 
                p.Stroke 
    = brush; 
                p.StrokeThickness 
    = 1
                canvas1.Children.Add(p); 
            } 
            
    #endregion

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

    代码
    using System; 
    using System.Collections.Generic; 
    using System.Linq; 
    using System.Net; 
    using System.Windows; 
    using System.Windows.Controls; 
    using System.Windows.Documents; 
    using System.Windows.Input; 
    using System.Windows.Media; 
    using System.Windows.Media.Animation; 
    using System.Windows.Shapes; 

    namespace SilverlightApplication2 

        
    public class Task 
        { 
            
    public int ID { setget; } 
            
    public int ParentID { setget; } 
            
    public string Name { setget; } 
        } 
        
    public partial class MainPage : UserControl 
        { 
            
    private static List<Task> listTask; 
            
    public MainPage() 
            { 
                InitializeComponent(); 
                listTask 
    = new List<Task>(); 
                listTask.Add(
    new Task() { ID = 1, ParentID = 0, Name = "1" }); 
                listTask.Add(
    new Task() { ID = 2, ParentID = 1, Name = "11" }); 
                listTask.Add(
    new Task() { ID = 3, ParentID = 1, Name = "12" }); 
                listTask.Add(
    new Task() { ID = 4, ParentID = 2, Name = "21" }); 
                listTask.Add(
    new Task() { ID = 5, ParentID = 2, Name = "22" }); 
                listTask.Add(
    new Task() { ID = 6, ParentID = 3, Name = "31" }); 
                listTask.Add(
    new Task() { ID = 7, ParentID = 3, Name = "32" }); 
                listTask.Add(
    new Task() { ID = 8, ParentID = 3, Name = "33" }); 
                listTask.Add(
    new Task() { ID = 9, ParentID = 4, Name = "42" }); 
                listTask.Add(
    new Task() { ID = 10, ParentID =4, Name = "42" }); 
                listTask.Add(
    new Task() { ID = 11, ParentID =3, Name = "34" }); 
                listTask.Add(
    new Task() { ID = 12, ParentID = 5, Name = "51" }); 
                listTask.Add(
    new Task() { ID = 13, ParentID = 8, Name = "81" }); 
                
    this.Loaded += new RoutedEventHandler(MainPage_Loaded); 
            } 
            
    void MainPage_Loaded(object sender, RoutedEventArgs e) 
            {             
                AddAll();         
            }        
            
    class TaskPro 
            { 
                
    public Task task { setget; } 
                
    public double top { setget; } 
                
    public double left { setget; } 
                
    public int index { setget; } 
            } 
            
    class TaskCount 
            { 
                
    public double Top { setget; } 
                
    public List<Task> Tasks { setget; } 
            } 
            
    static List<TaskPro> listTaskPro = new List<TaskPro>(); 
            
    static List<TaskCount> listTopAndTasks = new List<TaskCount>(); 
            
    void AddAll() 
            { 
                
    foreach(Task t in listTask) 
                { 
                    
    if (t.ParentID == 0
                    { 
                        listTaskPro.Add(
    new TaskPro() { task = t, index = 1, left = this.canvas1.Width / 2, top = 0 }); 
                    } 
                    
    else 
                    { 
                        
    for(int i=0;i<listTaskPro.Count;i++
                        { 
                            
    if (t.ParentID == listTaskPro[i].task.ID) 
                            { 
                                listTaskPro.Add(
    new TaskPro() { task = t, top = listTaskPro[i].top + 80, index = 0, left = 0 }); 
                            } 
                        } 
                    } 
                } 

                
    #region 汇总层及层内的元素个数 
                
    foreach (TaskPro t in listTaskPro) 
                { 
                    
    bool IsExist = false
                    
    foreach(TaskCount tc in listTopAndTasks) 
                    { 
                        
    if(tc.Top==t.top) 
                        { 
                            IsExist 
    = true
                        } 
                    } 
                    
    if(!IsExist) 
                    { 
                        listTopAndTasks.Add(
    new TaskCount() { Top=t.top, Tasks=new List<Task>() }); 
                    } 
                    var topAndTasks 
    = listTopAndTasks.Where(m=>m.Top==t.top).FirstOrDefault(); 
                    topAndTasks.Tasks.Add(t.task);               
                } 
                
    #endregion 

                
    foreach (TaskPro t in listTaskPro) 
                { 
                    
    for (int i = 0; i < listTopAndTasks.Count;i++ ) 
                    { 
                        
    for (int j = 0; j < listTopAndTasks[i].Tasks.Count;j++ ) 
                        { 
                            
    if (listTopAndTasks[i].Tasks[j].ID == t.task.ID) 
                            { 
                                t.index 
    = j + 1
                            } 
                        } 
                    } 
                } 

                
    for (int i = 0; i < listTaskPro.Count; i++
                { 
                    
    if (listTaskPro[i].task.ParentID == 0
                    { 
                        listTaskPro[i].left 
    = this.canvas1.Width / 2
                    } 
                    
    else 
                    { 
                        var childCount 
    = listTaskPro.Where(m => m.task.ParentID == listTaskPro[i].task.ParentID).Count(); 
                        var parentLeft 
    = listTaskPro.Where(m => m.task.ID == listTaskPro[i].task.ParentID).FirstOrDefault().left; 
                        var perLength 
    = parentLeft*1.5 / (childCount + 1); 
                        listTaskPro[i].left
    =listTaskPro[i].index*perLength;                                     
                    } 
                } 
                
    foreach (TaskPro t in listTaskPro) 
                { 
                    AddBtn(t.task.Name, t.left, t.top); 
                    
    if(t.task.ParentID!=0
                    { 
                        TaskPro tp 
    = listTaskPro.Where(m=>m.task.ID==t.task.ParentID).FirstOrDefault(); 
                        AddLine(tp.left
    +buttonWidth/2, tp.top+buttonHeight, t.left+buttonWidth/2, t.top); 
                    } 
                } 
            } 
            
    double buttonHeight = 20
            
    double buttonWidth = 50
            
    void AddBtn(string content,double left,double top) 
            { 
                Button btn 
    = new Button(); 
                btn.Content 
    = content; 
                btn.Width 
    = buttonWidth; 
                btn.Height 
    = buttonHeight; 
                
    this.canvas1.Children.Add(btn); 
                Canvas.SetLeft(btn, left); 
                Canvas.SetTop(btn, top); 
            } 

            
    void AddLine(double startLeft,double startTop,double endLeft,double endTop) 
            { 
                Path p 
    = new Path(); 
                LineGeometry geometry 
    = new LineGeometry(); 
                SolidColorBrush brush 
    = new SolidColorBrush();             

                brush.Color 
    = Colors.Black; 
                geometry.StartPoint 
    = new Point(startLeft, startTop); 
                geometry.EndPoint 
    = new Point(endLeft, endTop); 
                p.Data 
    = geometry; 
                p.Stroke 
    = brush; 
                p.StrokeThickness 
    = 1

                canvas1.Children.Add(p); 
            } 
        } 
    }
  • 相关阅读:
    CentOS挂载NTFS移动硬盘
    【算法与数据结构】动态规划
    【算法与数据结构】图的最小生成树 MST
    【C语言工具】AddressSanitizer
    【算法与数据结构】二叉堆和优先队列 Priority Queue
    【算法与数据结构】三种简单排序
    【算法与数据结构】并查集 Disjoint Set
    【算法与数据结构】二叉堆和堆排序
    【Linux 应用编程】进程管理
    【Linux 应用编程】进程管理
  • 原文地址:https://www.cnblogs.com/waw/p/2158645.html
Copyright © 2011-2022 走看看