一起谈.NET技术,Silverlight中使用递归构造关系图

简介:   这两天遇到一个问题,项目中需要在silverlight中使用连接图的方式来显示任务之间的关系,总体有父子和平行两种,昨天在改同事的代码,一直出问题,索性晚上写了一下实现方法。  需求  有一个List对象中存了若干个Task,这些Task对象通过ParentID属性进行关联,现在要求将这个List中的任务使用图的方式形成如父子关系和平行关系的图示例如下图:   实现方法思考  刚开始接到这个任务我就想着递归应该可以搞定了,但是仔细考虑才发现每个任务的子任务需要在一定区域内才行,需要计算子级和子级之间的距离,如果使用递归,例如上图的元素“12”的位置就没有办法很好确定了。

  这两天遇到一个问题,项目中需要在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 {  set get ; } 
            
public   double  top {  set get ; } 
            
public   double  left {  set get ; } 
            
public   int  index {  set get ; } // 这是为了找到节点在某层的位置来计算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 {  set get ; } 
        
public   int  ParentID {  set get ; } 
        
public   string  Name {  set get ; } 
    } 
    
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 {  set get ; } 
            
public   double  top {  set get ; } 
            
public   double  left {  set get ; } 
            
public   int  index {  set get ; } 
        } 
        
class  TaskCount 
        { 
            
public   double  Top {  set get ; } 
            
public  List < Task >  Tasks {  set get ; } 
        } 
        
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); 
        } 
    } 
}
目录
相关文章
|
5天前
|
人工智能 开发框架 量子技术
【专栏】.NET 技术:驱动创新的力量
【4月更文挑战第29天】.NET技术,作为微软的开发框架,以其跨平台、开源和语言多样性驱动软件创新。它在云计算、AI/ML、混合现实等领域发挥关键作用,通过Azure、ML.NET等工具促进新兴技术发展。未来,.NET将涉足量子计算、微服务和无服务器计算,持续拓宽软件开发边界,成为创新的重要推动力。掌握.NET技术,对于开发者而言,意味着握有开启创新的钥匙。
|
5天前
|
开发框架 .NET C#
【专栏】理解.NET 技术,提升开发水平
【4月更文挑战第29天】本文介绍了.NET技术的核心概念和应用,包括其跨平台能力、性能优化、现代编程语言支持及Web开发等特性。文章强调了深入学习.NET技术、关注社区动态、实践经验及学习现代编程理念对提升开发水平的重要性。通过这些,开发者能更好地利用.NET构建高效、可维护的多平台应用。
|
5天前
|
机器学习/深度学习 vr&ar 开发者
【专栏】.NET 技术:引领开发新方向
【4月更文挑战第29天】本文探讨了.NET技术如何引领软件开发新方向,主要体现在三方面:1) 作为跨平台开发的先锋,.NET Core支持多操作系统和移动设备,借助.NET MAUI创建统一UI,适应物联网需求;2) 提升性能和开发者生产力,采用先进技术和优化策略,同时更新C#语言特性,提高代码效率和可维护性;3) 支持现代化应用架构,包括微服务、容器化,集成Kubernetes和ASP.NET Core,保障安全性。此外,.NET还不断探索AI、ML和AR/VR技术,为软件开发带来更多创新可能。
|
5天前
|
开发框架 Cloud Native 开发者
【专栏】剖析.NET 技术的核心竞争力
【4月更文挑战第29天】本文探讨了.NET框架在软件开发中的核心竞争力:1) .NET Core实现跨平台与云原生技术的融合,支持多操作系统和容器化;2) 提升性能和开发者生产力,采用JIT、AOT优化,提供C#新特性和Roslyn编译器平台;3) 支持现代化应用架构,包括微服务和容器化,内置安全机制;4) 丰富的生态系统和社区支持,拥有庞大的开发者社区和微软的持续投入。这些优势使.NET在竞争激烈的市场中保持领先地位。
|
5天前
|
开发框架 .NET 开发者
【专栏】领略.NET 技术的创新力量
【4月更文挑战第29天】.NET技术自ASP.NET起历经创新,现以.NET Core为核心,展现跨平台能力,提升性能与生产力,支持现代化应用架构。.NET Core使开发者能用同一代码库在不同操作系统上构建应用,扩展至移动和物联网领域。性能提升,C#新特性简化编程,Roslyn编译器优化代码。拥抱微服务、容器化,内置安全机制,支持OAuth等标准。未来.NET 6将引入更快性能、Hot Reload等功能,预示着.NET将持续引领软件开发潮流,为开发者创造更多机会。
|
5天前
|
物联网 vr&ar 开发者
【专栏】.NET 技术:为开发注入活力
【4月更文挑战第29天】本文探讨了.NET技术的创新,主要体现在三个方面:1) .NET Core实现跨平台开发革命,支持多种操作系统和硬件,如.NET MAUI用于多平台UI;2) 性能提升与生产力飞跃,C#新特性简化编程,JIT和AOT优化提升性能,Roslyn提供代码分析工具;3) 引领现代化应用架构,支持微服务、容器化,内置安全机制。未来,.NET 7将带来更多新特性和前沿技术整合,如量子计算、AI,持续推动软件开发创新。开发者掌握.NET技术将赢得竞争优势。
|
5天前
|
人工智能 前端开发 Cloud Native
【专栏】洞察.NET 技术的开发趋势
【4月更文挑战第29天】本文探讨了.NET技术的三大发展趋势:1) 跨平台与云原生技术融合,通过.NET Core支持轻量级、高性能应用,适应云计算和微服务;2) 人工智能与机器学习的集成,如ML.NET框架,使开发者能用C#构建AI模型;3) 引入现代化前端开发技术,如Blazor,实现前后端一致性。随着.NET 8等新版本的发布,期待更多创新技术如量子计算、AR/VR的融合,.NET将持续推动软件开发的创新与进步。

热门文章

最新文章