Blog | Tag | Local | Guest | Login | Write |  RSS

안녕하세요. 예전에 제가 Treeview를 이용해서 Architecture View를 구현하는 방법에 대해 소개해 드린적이 있습니다. 이번시간에는 Treeview를 이용하여 Org Chart를 표현하는 방법에 대해 소개해 드리도록 하겠습니다. 아래는 결과 그림 입니다.


이번 예제에서 여기서 중요한 부분은 Treeview의 Template과 연결선을 그리는 방법으로 포스팅에서는 해당 부분만 다루고 나머지는 소스코드를 첨부하도록 하겠습니다.

먼저 Style부분입니다.
  1. <STYLE TargetType="TreeViewItem">  
  2.     <Setter Property="Template">  
  3.         <Setter.Value>  
  4.             <ControlTemplate TargetType="TreeViewItem">  
  5.                 <StackPanel>  
  6.                     <Border Width="60" Height="30" Background="Transparent" BorderBrush="Black" BorderThickness="1"    
  7.                             CornerRadius="5" Margin="2,0,2,30" Padding="5" Name="PART_Header">  
  8.                         <ContentPresenter ContentSource="Header"      
  9.                           HorizontalAlignment="Center" VerticalAlignment="Center"/>  
  10.                     </Border>  
  11.                     <ItemsPresenter/>  
  12.                 </StackPanel>  
  13.             </ControlTemplate>  
  14.         </Setter.Value>  
  15.     </Setter>  
  16.     <Setter Property="ItemsPanel">  
  17.         <Setter.Value>  
  18.             <ItemsPanelTemplate>  
  19.                 <StackPanel HorizontalAlignment="Center"       
  20.                      IsItemsHost="True" Margin="5"       
  21.                      Orientation="Horizontal"  />  
  22.             </ItemsPanelTemplate>  
  23.         </Setter.Value>  
  24.     </Setter>  
  25. </STYLE>  



이전에 소개해 드렸던 Architecture View와 비슷함으로 코드 설명은 ArchitectureView를 참고하시기 바랍니다.  다음은 C#코드입니다.

  1. public static Pen ConnectionPen = new Pen(Brushes.Black, 1);   
  2.   
  3. protected override void OnRender(DrawingContext DrawingContext)   
  4. {   
  5.        
  6.     if (this.Items.Count > 0)   
  7.         DrawConnections(DrawingContext, (TreeViewItem)this.Items[0]);   
  8.     base.OnRender(DrawingContext);   
  9. }   
  10.   
  11.   
  12. private void DrawConnections(DrawingContext DrawingContext, TreeViewItem ParentNode)   
  13. {   
  14.   
  15.     Point ParentNodePoint = GetHeaderCenterPoint(ParentNode, false);   
  16.   
  17.     double ParentToChildDistance = 999999;   
  18.     foreach (FrameworkElement ChildNode in ParentNode.Items)   
  19.     {   
  20.         Point ChildNodePoint = GetHeaderCenterPoint(ChildNode, true);   
  21.   
  22.         if (ParentToChildDistance > ChildNodePoint.Y - ParentNodePoint.Y)   
  23.             ParentToChildDistance = ChildNodePoint.Y - ParentNodePoint.Y;                   
  24.     }   
  25.   
  26.     if(ParentToChildDistance != 999999)   
  27.         DrawingContext.DrawLine(ConnectionPen, new Point(ParentNodePoint.X, ParentNodePoint.Y + ParentToChildDistance / 2), ParentNodePoint);   
  28.   
  29.     foreach (FrameworkElement ChildNode in ParentNode.Items)   
  30.     {   
  31.         Point ChildNodePoint = GetHeaderCenterPoint(ChildNode, true);   
  32.   
  33.         DrawingContext.DrawLine(ConnectionPen, new Point(ParentNodePoint.X, ParentNodePoint.Y + ParentToChildDistance / 2), ChildNodePoint);   
  34.   
  35.         if (ChildNode.GetType() == typeof(TreeViewItem))   
  36.             DrawConnections(DrawingContext, (TreeViewItem)ChildNode);   
  37.     }   
  38.   
  39. }   
  40.   
  41.   
  42. private Point GetTopCenterPoint(FrameworkElement Visual)   
  43. {   
  44.     return Visual.TranslatePoint(new Point(Visual.ActualWidth / 2, 0), this);   
  45. }   
  46.   
  47. private Point GetBottomCenterPoint(FrameworkElement Visual)   
  48. {   
  49.     return Visual.TranslatePoint(new Point(Visual.ActualWidth / 2, Visual.ActualHeight), this);   
  50. }   
  51.   
  52. private Point GetHeaderCenterPoint(FrameworkElement Control, bool IsTopCenter)   
  53. {   
  54.     if(IsTopCenter)   
  55.         return GetTopCenterPoint(GetHeader(Control));   
  56.     else  
  57.         return GetBottomCenterPoint(GetHeader(Control));   
  58. }   
  59.   
  60.   
  61. private FrameworkElement GetHeader(FrameworkElement Control)   
  62. {   
  63.     FrameworkElement ResultControl = (VisualTreeHelper.GetChild(Control, 0) as FrameworkElement).FindName("PART_Header"as FrameworkElement;   
  64.   
  65.     return ResultControl;   
  66. }  


TreeView가 랜더링 될때 각각의 노드들의 위치를 얻고 해당 부모와 자식 노드를 연결짖는 작업을 합니다.  노드의 Header부분의 위치를 구해야 함으로 Template에 헤더의 이름을 PART_Header라고 지정한뒤 VisualTreeHelper.GetChild를 사용하여 Template의 객체를 찾았습니다. 그리고 각노드들의 Treeview로부터의 상대 좌표를 얻기 위해서 Visual.TransaltePoint사용했습니다. 

기타 궁금하신 사항이나 질문은 리플이나 메일 주시면 답변해드리도록 하겠습니다.
아래는 소스코드입니다. (OrgChart를 이용한  SearchEngine을 구현하던중이라 프로젝트 이름이 SearchTree입니다 -_-이점은 그냥 넘어가주세요~)