faisalthayyil Ответов: 1

Как показать количество всех прямых и косвенных дочерних элементов в WPF treeview


У меня есть один элемент управления древовидным представлением WPF, который показывает сведения о сотруднике в иерархическом порядке.Я хочу показать имя сотрудника и количество его прямых и косвенных репортеров(например , если сотрудник C отчитывается перед B, А B отчитывается перед A, то количество прямых и косвенных репортеров A будет равно 2)
Но я могу показать количество прямых репортеров(1), но не количество всех репортеров(2).

Я связал источник элементов из базы данных в виде списка(Root) :

Мой xamal:
 <TreeView x:Name="tvMain" ItemsSource="{Binding Root}" BorderThickness="0">
        <TreeView.ItemTemplate>
            <HierarchicalDataTemplate ItemsSource="{Binding Children}">
                <StackPanel Orientation="Horizontal">
                    <Border BorderBrush="#02747474" Background="#02000000" HorizontalAlignment="Center" Margin="0,5,0,0" VerticalAlignment="Top" BorderThickness="1,1,1,1" x:Name="AvatarPhotoBorder">
                        <Border.BitmapEffect>
                            <DropShadowBitmapEffect ShadowDepth="7" Softness="0.75"/>
                        </Border.BitmapEffect>
                        <Image x:Name="imgPicture"  Source="{Binding ImagePath}" Stretch="Uniform" VerticalAlignment="Top" Width="75" Height="60" HorizontalAlignment="Center" />
                    </Border>
                    <TextBlock VerticalAlignment="Center">            
                    <TextBlock.Text>
                        <MultiBinding StringFormat=" {0} {1}">
                            <Binding Path="FirstName"/>
                            <Binding Path="LastName"/>
                        </MultiBinding>
                    </TextBlock.Text>
                    </TextBlock>
                    <TextBlock Text=" [Direct and Indirect reportee:" Foreground="LightGray" />
                    <TextBlock Text="{Binding Count}" Foreground="Gray"  />??
                   <---- <TextBlock Text="{Binding Children.Count}"/>---->this will give only direct reportee count

                    <TextBlock Text="]" Foreground="LightGray"  />
                    <StackPanel.Style>
                        <Style>
                            <Style.Triggers>
                                <DataTrigger Binding="{Binding IsSelected}" Value="true">
                                    <Setter Property="StackPanel.Background" Value="LightBlue"/>
                                </DataTrigger>
                            </Style.Triggers>
                        </Style>
                    </StackPanel.Style>
                </StackPanel>
            </HierarchicalDataTemplate>
        </TreeView.ItemTemplate>
        <TreeView.ItemContainerStyle>
            <Style TargetType="{x:Type TreeViewItem}" BasedOn="{StaticResource {x:Type TreeViewItem}}">
                <Setter Property="IsExpanded" Value="True"/>
                <Setter Property="IsSelected" Value="{Binding IsSelected}"/>
            </Style>
        </TreeView.ItemContainerStyle>
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="SelectedItemChanged">
                <i:InvokeCommandAction Command="{Binding SelectedCommand}" 
                                       CommandParameter="{Binding ElementName=tvMain, Path=SelectedItem}"/>
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </TreeView>
</StackPanel>

модель представления:

public class OrgElementViewModel : ViewModelBase
   {

       private int Id;
       private string firstName;
       private string lastName;
       private string imagePath;
       public int count;
       private ObservableCollection<OrgElementViewModel> allchildren;

       private ObservableCollection<OrgElementViewModel> children;

       private bool isSelected;

        public int Count
       {
           get {  ;
           if (allchildren == null) //not yet initialized
               return GetAllChildren();
           return count;

           }
           set { count = GetAllChildren();
           OnPropertyChanged("Count");
            }
       }

       private int GetAllChildren()
       {


           int dd = 2;
           allchildren = new ObservableCollection<OrgElementViewModel>();
           //get the list of children from Model
           foreach (Node i in OrgChartManager.Instance().GetAllChildren(this.ID))
           {
               //allchildren.Add(new OrgElementViewModel(i));

               dd = dd + 1;
           }

           return dd;
       }

       public int ID
       {
           get { return Id; }
           set { Id = value; }
       }

       public string FirstName
       {
           get { return firstName; }
           set { firstName = value; }
       }

       public string LastName
       {
           get { return lastName; }
           set { lastName = value; }
       }

       public string ImagePath
       {
           get { return imagePath; }
           set { imagePath = value; }
       }

       public bool IsSelected
       {
           get { return isSelected; }
           set
           {
               isSelected = value;
               OnPropertyChanged("IsSelected");
           }
       }

       public ObservableCollection<OrgElementViewModel> Children
       {
           get
           {
               if (children == null) //not yet initialized
                   return GetChildren();
               return children;
           }
           set
           {
               children = value;
               OnPropertyChanged("Children");
           }
       }

       internal OrgElementViewModel(Node i)
       {
           this.ID = i.Id;
           this.FirstName = i.FirstName;
           this.LastName = i.LastName;
           this.ImagePath = Path.GetFullPath("Images/" + this.ID.ToString() + ".png");
           this.Count = GetAllChildren();


       }

       internal void ShowChildrenLevel(int levelsShown)
       {
           if (levelsShown == -1) //show all levels
               this.Children = GetChildren();
           else if (levelsShown == 0)  //don't show any more levels
               this.Children = new ObservableCollection<OrgElementViewModel>();  //set as empty
           else if (levelsShown > 0)  //if a level is requested
           {
               this.Children = GetChildren();

               foreach (OrgElementViewModel i in this.Children)
                   i.ShowChildrenLevel(levelsShown - 1);  //decrement 1 for next level
           }
       }

       private ObservableCollection<OrgElementViewModel> GetChildren()
       {
           int dd = 1;

           children = new ObservableCollection<OrgElementViewModel>();
           //get the list of children from Model
           foreach (Node i in OrgChartManager.Instance().GetChildren(this.ID))
           {
               children.Add(new OrgElementViewModel(i));

           }


           return children;
       }




   }
Класс моделей:
public   class OrgChartManager
   {
       private static OrgChartManager self;

       //orgchart stored in dictionary
       private Dictionary<int, Node> list = new Dictionary<int, Node>();

       PersonalDAL dd = new PersonalDAL();


       private OrgChartManager()
       {

           DataTable my_datatable = new DataTable();
           my_datatable = new PersonalDAL().loademp();
           int ColumnCount = my_datatable.Columns.Count;
           int i = 1;
           foreach (DataRow dr in my_datatable.Rows)
           {
               Node node = new Node
                {
                    Id = (int)dr["ID"],
                    FirstName = (string)dr["FirstName"],
                    LastName = (string)dr["LastName"],
                  ParentId = (int)dr["ParentId"]

                 };
               list.Add(i, node);
               i++;
           }


       }

       internal static OrgChartManager Instance()
       {
           if (self == null)
               self = new OrgChartManager();
           return self;
       }

       //get the root
       internal Node GetRoot()
       {
           return list[1];  //return the top root node
       }

       //get the directchildren of a node
       internal IEnumerable<Node> GetChildren(int parentId)
       {
                  return from a in list
                  where a.Value.ParentId == parentId
                       && a.Value.Id != parentId   //don't include the root, which has the same Id and ParentId
                  select a.Value;


       }
       // Recursion method to get all the childeren under purcular ID
       internal IEnumerable<Node> GetAllChildren(int parentId)
       {
                  var result = new List<Node>();
                  var employees = from a in list
                  where (a.Value.ParentId == parentId
                       && a.Value.Id != parentId)   //don't include the root, which has the same Id and ParentId
                 select a.Value;

                 foreach (var employee in employees)
                 {
                     result.Add(employee);
                     result.AddRange(GetAllChildren(parentId));
                 }

                 return result;

 //return from a in list
             //     where a.Value.ParentId == parentId
             //          && a.Value.Id != parentId   //don't include the root, which has the same Id and ParentId
             //     select a.Value;

       }




   }
Мой класс узлов:
 class Node
      {
    public int Id { get; set; }

    public string FirstName { get; set; }

    public string LastName { get; set; }

    public int ParentId { get; set; } 





}


Что я уже пробовал:

Я просто хочу показать количество всех детей на каждом узле элемента(прямой и косвенный), пожалуйста, помогите мне .код, где я привет освещен ?? вот где я застрял

1 Ответов

Рейтинг:
9

Graeme_Grant

Вот несколько вещей...

1. ни одно свойство не выставляется для иерархического подсчета, вы смотрите только на один уровень дочернего подсчета

2. Ваш TreeView загружается по требованию, поэтому вам нужно будет предварительно рассчитать количество. Если исходить из БД, то вы можете создавать подсчеты с помощью SQL; в противном случае это можно сделать с помощью Linq.

Обновление: Я вижу, что вы пересмотрели свой кодекс. OrgElementViewModel не правильно реализует INotifyPropertyChanged уведомление о событии, которое, как я предполагаю, реализовано в ViewModelBase класс. Вот почему Count свойство не отражает никаких обновлений. Кроме того, количество дочерних элементов не обновляется, когда ShowChildrenLevel я звонил, когда узел расширяется.


faisalthayyil

У меня модифицированная модель класса, в том числе метод рекурсии(GetAllChildren).Но при запуске этот метод выдает ошибку в строке результата.AddRange(GetAllChildren(parentId));

Graeme_Grant

И что же это за ошибка?

faisalthayyil

Я внес изменения для I INotifyPropertyChanged . Как вы уже сказали , я реализовал INotifyPropertyChanged в классе Viewmodelbase.Ошибка в классе модели в методе GetAllChildren "необработанное исключение типа 'System.StackOverflowException' произошло в DevLake.OrgChart.UI.exe-в результате линии.AddRange(GetAllChildren(parentId)).Я заметил, что тот же метод работает нормально, если я запускаю с комментируемым кодом в том же методе для прямого сотрудника.

Graeme_Grant

Переполнение стека обычно является результатом рекурсии. Установите точку останова и пройдите через свой код, чтобы увидеть, что он делает.

faisalthayyil

это был бесконечный цикл, когда я изменил результат.AddRange(GetAllChildren(parentId)); to result.AddRange(GetAllChildren(employee.Id ));.он работает.теперь только проблема в том, что он добавляет 2 дополнительных счета для каждого узла.поэтому я решил вычесть 2 из результата.