Создание дерева подразделений

Основной код

XAML

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition></ColumnDefinition>
        <ColumnDefinition></ColumnDefinition>
    </Grid.ColumnDefinitions>
    <Canvas Name="MainCanvas">
        <StackPanel x:Name="StackWorks"></StackPanel>
    </Canvas>
    <ListView x:Name="ListUsers" Grid.Column="1">
        <ListView.ItemTemplate>
            <DataTemplate>
                <StackPanel>
                    <TextBlock Text="{Binding Name}"></TextBlock>
                    <TextBlock Text="{Binding Work.Name}"></TextBlock>
                </StackPanel>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</Grid>
      

C#

Work contextWork;
List<StackPanel> stackPanels;
Stack<TextBlock> textBlocks;
List<User> users;
public UserTree()
{
    InitializeComponent();

    contextWork = App.DB.Works.FirstOrDefault();

    Refresh();
}

private void Refresh()
{
    stackPanels = new List<StackPanel>();
    textBlocks = new Stack<TextBlock>();
    users = new List<User>();
    StackWorks.Children.Clear();

    var work = App.DB.Works.FirstOrDefault();
    GenerateTree(work, 0, contextWork);

    GenerateUsers(contextWork);
    ListUsers.ItemsSource = users;
}

private void GenerateUsers(Work work)
{
    var usersDown = App.DB.Users.Where(x => (x.Work != null && x.Work.Id == work.Id)).ToList();
    users.AddRange(usersDown);

    var works = App.DB.Works.Where(x => (x.LiderWork != null && x.LiderWork.Id == work.Id)).ToList();
    foreach (var workDown in works)
        GenerateUsers(workDown);
}

private void GenerateTree(Work work, int level, Work targetWork)
{
    var works = App.DB.Works.Where(x => (x.LiderWork != null && x.LiderWork.Id == work.Id)).ToList();

    if (stackPanels.Count() <= level)
    {
        stackPanels.Add(new StackPanel()
        {
            Orientation = Orientation.Horizontal,
            HorizontalAlignment = HorizontalAlignment.Center,
        });
        StackWorks.Children.Add(stackPanels[level]);
    }

    var textBlock = new TextBlock()
    {
        Text = work.Name,
        HorizontalAlignment = HorizontalAlignment.Center,
        VerticalAlignment = VerticalAlignment.Center,
        Margin = new Thickness(10),
        Background = Brushes.LightGreen,
        Padding = new Thickness(5),
        DataContext = work,
    };
    stackPanels[level].Children.Add(textBlock);

    textBlock.MouseDown += TextBlock_MouseDown;

    if (work.Id == targetWork.Id)
        textBlock.Background = Brushes.Green;

    if (work.Id != 1)
        GetLine(textBlock, textBlocks.Peek());

    textBlocks.Push(textBlock);
    foreach(var workDown in works)
        GenerateTree(workDown, level + 1, targetWork);
    textBlocks.Pop();
}

private void TextBlock_MouseDown(object sender, MouseButtonEventArgs e)
{
    var work = (sender as TextBlock).DataContext as Work;
    if (work == null)
        return;

    contextWork = work;
    Refresh();
}

private void GetLine(TextBlock textBlock1, TextBlock textBlock2)
{
    var line = new Line()
    {
        Stroke = Brushes.Black,
        StrokeThickness = 2,
    };
    var triangle = new Polygon()
    {
        Fill = Brushes.Black,
        Stroke = Brushes.Black,
        StrokeThickness = 2,
    };

    CompositionTarget.Rendering += (s, e) =>
    {
        var point1 = textBlock1.TranslatePoint(new Point(textBlock1.ActualWidth / 2, textBlock1.ActualHeight / 2 - 10), MainCanvas);
        var point2 = textBlock2.TranslatePoint(new Point(textBlock2.ActualWidth / 2, textBlock2.ActualHeight / 2 + 10), MainCanvas);

        line.X1 = point1.X;
        line.Y1 = point1.Y - 10;
        line.X2 = point2.X;
        line.Y2 = point2.Y;

        triangle.Points = new PointCollection()
        {
            new Point(point1.X + 10, point1.Y - 10),
            new Point(point1.X - 10, point1.Y - 10),
            new Point(point1.X, point1.Y),
        };
    };

    if (!MainCanvas.Children.Contains(line))
        MainCanvas.Children.Add(line);
    if (!MainCanvas.Children.Contains(triangle))
        MainCanvas.Children.Add(triangle);
}