# برنامه نویسی با محصولات مایکروسافت > برنامه نویسی مبتنی بر Microsoft .Net Framework > WPF > سوال: treeview نامحدود خود ارجاع دهنده

## masood1992

سلام... چطور میتونم یه treeview نامحدود بسازم که همشون داخل یه تیبل هستن و بوسیله parentId مشخص بشه که کدوم زیر مجموعه کدومه...

----------


## csvbcscp

<HierarchicalDataTemplate x:Key="itemTemplate" ItemsSource="{Binding Children, Mode=TwoWay}">
                    <TextBlock Text="{Binding Name}" />
                </HierarchicalDataTemplate>


<telerik:RadTreeView ItemTemplate="{StaticResource itemTemplate}" x:Name="treeView" Grid.Row="1" 
                ItemsSource="{Binding RootLevelNodes, Mode=TwoWay}"/>

----------


## تبسم ساینا

میشه توضیح بدید چجوری میشه اون itemTemplate رو از دیتابیس پرش کرد؟

----------


## csvbcscp

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Data.Entity;
using System.Linq;
using System.Windows;


namespace WpfApplicationTree
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }
    public class MainWindowViewModel : INotifyPropertyChanged
    {
        public MainWindowViewModel()
        {
            MyDbContext ctx = new MyDbContext();
            People = new ObservableCollection<Person>(ctx.People.ToList());


        }
        private ObservableCollection<Person> _People;


        public ObservableCollection<Person> People
        {
            get { return _People; }
            set { _People = value;PropertyChanged(this, new PropertyChangedEventArgs(nameof(People))) ; }
        }


        public event PropertyChangedEventHandler PropertyChanged=delegate { };
    }
    public class Person
    {
        public int Id { get; set; }
        public Person()
        {
            Children = new ObservableCollection<Person>();
        }
        public string Name { get; set; }
        public IList<Person> Children { get; set; }
    }


    public class MyDbContext:DbContext
    {
        public MyDbContext():base("DefaultConnectionString")
        {
        }


        public DbSet<Person> People { get; set; }
    }


}




و کد Xaml به شکل زیر خواهد بود :



<Window x:Class="WpfApplicationTree.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApplicationTree"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <local:MainWindowViewModel x:Key="VM"/>
    </Window.Resources>
    <Grid DataContext="{Binding Source={StaticResource VM}}">


        <TreeView ItemsSource="{Binding People}">
            <TreeView.ItemTemplate>
                <HierarchicalDataTemplate ItemsSource="{Binding Children}">
                    <TextBlock Text="{Binding Name}" />
                </HierarchicalDataTemplate>
            </TreeView.ItemTemplate>
        </TreeView>
    </Grid>
</Window>




ConnectionString رو مطابق با تنظیمات سیستمتون تغییر بدید : (کد زیر رو بعد از اعمال تغییرات به فایل config اضافه کنید)

  <connectionStrings>    <add name="DefaultConnectionString" connectionString="server=192.168.1.99;database=Tes  tTree;initial catalog=TestTree;user id=sa;password=abc123;" providerName="System.Data.SqlClient"/>
  </connectionStrings>




برای اجرای پروژه باید EntityFramework رو به پروژه اضافه کنید. برای اینکار می تونید از دستور زیر در Package Manager Console اضافه کنید.


Install-Package EntityFramework


بعد از اضافه شدن EntityFramework باید Migration رو با دستور زیر Enable کنید.


Enable-Migrations -Force


بعذ از اون یک Migration اضافه می کنیم
Add-Migration Initialize


و در انتها با اجرای دستور زیر دیتابیس رو درست می کنیم


Update-Database


توجه داشته باشید که یک فایل به اسم Configuration.cs  به پوشه Migrations اضافه شده، من این فایل رو به نحو زیر تغییر دادم تا بتونم داده های اولیه وارد دیتابیس کنم.


namespace WpfApplicationTree.Migrations
{
    using System.Collections.Generic;
    using System.Data.Entity.Migrations;


    internal sealed class Configuration : DbMigrationsConfiguration<MyDbContext>
    {
        public Configuration()
        {
            AutomaticMigrationsEnabled = false;
        }


        protected override void Seed(MyDbContext context)
        {
            context.People.AddOrUpdate(
              p => p.Name,
              new Person { Name = "Ali Bayat",Children=new List<Person> { new Person { Name="Sara"} } },
              new Person { Name = "Zahra Bayat" }
            );
        }
    }
}

----------


## تبسم ساینا

ممنونم از پاسخ گویی تون.
من پروژه رو نوشتم ولی برام همه رو دو نه کدونه در یه سطح میاره.
کدهامو می ذارم یه نگاه اگه میشه بندازید :
مدل hesabcoding:
 public class HesabCoding : NotifyPropertyChangedHelper, IDataErrorInfo
    {

        private int id;

        private string title;

        private string fullTitle;

        private string hesabCode;

        private string fullHesabCode;

        private byte hesabStructure_FK;

        private string hesabStructure;

        private byte hesabEntity_FK;

        private string hesabEntity;

        private short hesabGroup_FK;

        private string hesabGroup;

        private int parentId;

        private bool isFinal;

        private bool active;

        private bool isAddByUser;

        private string insertDateTime;

        private string insertUser;

        private string updateDateTime;

        private string updateUser;

        //ObservableCollection<HesabCoding> _children = new ObservableCollection<HesabCoding>();
        //public ObservableCollection<HesabCoding> Children
        //{
        //    get { return _children; }
        //}

        public IList<HesabCoding> HesabCodings { get; set; }
        public HesabCoding()
        {
            HesabCodings = new ObservableCollection<HesabCoding>();
        }

        [Custom(false, Visibility.Collapsed)]
        public  int Id
        {
            get
            {
                return this.id;
            }
            set
            {
                this.id = value;
                base.OnPropertyChanged("Id");

            }
        }

        [DisplayName("نام حساب")]
        [Custom(false, Visibility.Visible)]
        public  string Title
        {
            get
            {
                return this.title;
            }
            set
            {
                this.title = value;
                base.OnPropertyChanged("Title");

            }
        }


        [DisplayName("نام کامل حساب")]
        [Custom(false, Visibility.Visible)]
        public string FullTitle
        {
            get
            {
                return this.fullTitle;
            }
            set
            {
                this.fullTitle = value;
                base.OnPropertyChanged("FullTitle");

            }
        }


        [DisplayName("کد حساب")]
        [Custom(false, Visibility.Visible)]
        public string HesabCode
        {
            get
            {
                return this.hesabCode;
            }
            set
            {
                this.hesabCode = value;
                base.OnPropertyChanged("HesabCode");

            }
        }



        [DisplayName("کد کامل حساب")]
        [Custom(false, Visibility.Visible)]
        public string FullHesabCode
        {
            get
            {
                return this.fullHesabCode;
            }
            set
            {
                this.fullHesabCode = value;
                base.OnPropertyChanged("FullHesabCode");

            }
        }

        [Custom(false, Visibility.Collapsed)]
        public  byte HesabStructure_FK
        {
            get
            {
                return this.hesabStructure_FK;
            }
            set
            {
                this.hesabStructure_FK = value;
                base.OnPropertyChanged("HesabStructure_FK");

            }
        }

        [DisplayName("سطح حساب")]
        [Custom(false, Visibility.Visible)]
        public string HesabStructure
        {
            get
            {
                return this.hesabStructure;
            }
            set
            {
                this.hesabStructure = value;
                base.OnPropertyChanged("HesabStructure");

            }
        }

        [Custom(false, Visibility.Collapsed)]
        public  byte HesabEntity_FK
        {
            get
            {
                return this.hesabEntity_FK;
            }
            set
            {
                this.hesabEntity_FK = value;
                base.OnPropertyChanged("HesabEntity_FK");

            }
        }

        [DisplayName("ماهیت حساب")]
        [Custom(false, Visibility.Visible)]
        public string HesabEntity
        {
            get
            {
                return this.hesabEntity;
            }
            set
            {
                this.hesabEntity = value;
                base.OnPropertyChanged("HesabEntity");

            }
        }

        [Custom(false, Visibility.Collapsed)]
        public  short HesabGroup_FK
        {
            get
            {
                return this.hesabGroup_FK;
            }
            set
            {
                this.hesabGroup_FK = value;
                base.OnPropertyChanged("HesabGroup_FK");

            }
        }

        [DisplayName("گروه حساب")]
        [Custom(false, Visibility.Visible)]
        public string HesabGroup
        {
            get
            {
                return this.hesabGroup;
            }
            set
            {
                this.hesabGroup = value;
                base.OnPropertyChanged("HesabGroup");

            }
        }

        [Custom(false, Visibility.Visible)]
        public int ParentId
        {
            get
            {
                return this.parentId;
            }
            set
            {
                this.parentId = value;
                base.OnPropertyChanged("ParentId");
            }
        }


        [DisplayName("پایانی")]
        [Custom(false, Visibility.Visible)]
        public  bool IsFinal
        {
            get
            {
                return this.isFinal;
            }
            set
            {
                this.isFinal = value;
                base.OnPropertyChanged("IsFinal");

            }
        }

        [DisplayName("فعال")]
        [Custom(false, Visibility.Hidden)]
        public  bool Active
        {
            get
            {
                return this.active;
            }
            set
            {
                this.active = value;
                base.OnPropertyChanged("Active");

            }
        }

        [Custom(false, Visibility.Collapsed)]
        public  bool IsAddByUser
        {
            get
            {
                return this.isAddByUser;
            }
            set
            {
                this.isAddByUser = value;
                base.OnPropertyChanged("IsAddByUser");

            }
        }


        [DisplayName("زمان ایجاد")]
        [Custom(false, Visibility.Visible)]
        public string InsertDateTime
        {
            get
            {
                return this.insertDateTime;
            }
            set
            {
                this.insertDateTime = value;
                base.OnPropertyChanged("InsertDateTime");

            }
        }

        [DisplayName("کاربر ایجاد کننده")]
        [Custom(false, Visibility.Visible)]
        public string InsertUser
        {
            get
            {
                return this.insertUser;
            }
            set
            {
                this.insertUser = value;
                base.OnPropertyChanged("InsertUser");

            }
        }

        [DisplayName("زمان ویرایش")]
        [Custom(false, Visibility.Visible)]
        public string UpdateDateTime
        {
            get
            {
                return this.updateDateTime;
            }
            set
            {
                this.updateDateTime = value;
                base.OnPropertyChanged("UpdateDateTime");
            }
        }

        [DisplayName("کاربر ویرایش کننده")]
        [Custom(false, Visibility.Visible)]
        public string UpdateUser
        {
            get
            {
                return this.updateUser;
            }
            set
            {
                this.updateUser = value;
                base.OnPropertyChanged("UpdateUser");

            }
        }
HesabCodingDAL:
 public ObservableCollection<HesabCoding> HesabCodingSelect(bool onlyActive, out string errorMessage)
        {
            using (var dbcontext = new AccountingDataContext(AppData.userDbConnectionStri  ng))
            {
                try
                {
                    errorMessage = null;

                    var Ret = dbcontext.HesabCodingSelect(true);
                  
                    //     var m = new HesabCoding {Ret };

                    List<HesabCoding> hesabCodingModelList = new List<HesabCoding>();

                    CastObjectHelper.CastObjectResultToList(Ret, ref hesabCodingModelList);

                    ObservableCollection<HesabCoding> hesabCodingModelCollection = new ObservableCollection<HesabCoding>(hesabCodingModel  List);

                    return hesabCodingModelCollection;
                }

                catch (Exception ex)
                {
                    errorMessage = ex.Message;
                    return null;
                }
            }
        }
کدهای :
HesabCodingListVM 
public  class HesabCodingListVM : BaseViewModel
    {
        #region Class Contractor

        HesabCodingDAL HesabCodingDAL;

        public HesabCodingListVM()
        {
            HesabCodingDAL = new HesabCodingDAL();
            HesabCodingCollection = new ObservableCollection<HesabCoding>();
            HesabCodingModelCurrent = new HesabCoding();
            HesabCodingSelect();
        }
        #endregion

 #region Public Property
        private ObservableCollection<HesabCoding> _HesabCodingCollection;
        public ObservableCollection<HesabCoding> HesabCodingCollection
        {
            get { return this._HesabCodingCollection; }
            set
            {
                this._HesabCodingCollection = value;
                PropertyChanged(/*"HesabCodingCollection"*/this, new PropertyChangedEventArgs(nameof(HesabCodingCollect  ion)));
            }
        }

        public HesabCoding HesabCodingModelCurrent
        {
            get;
            set;
        }

        private int selectedIndex;
        public int SelectedIndex
        {
            get { return selectedIndex; }
            set
            {
                selectedIndex = value;
                base.OnPropertyChanged("SelectedIndex");

            }
        }


        #endregion
        public event PropertyChangedEventHandler PropertyChanged = delegate { };

        #region Class  Methods
        public void HesabCodingSelect()
        {

            string _errorMessage = "";
            int tmpSelectedIndex = SelectedIndex;
            ObservableCollection<HesabCoding> _HesabCodingModelCollection = new ObservableCollection<HesabCoding>();
            _HesabCodingModelCollection = HesabCodingDAL.HesabCodingSelect(false, out _errorMessage);
            if (!string.IsNullOrEmpty(errorMessage))
            {
                AvinMessageBox.Show("خطا", errorMessage, System.Windows.MessageBoxButton.YesNo, Common.MessageBoxImage.Warning, System.Windows.MessageBoxResult.OK);
            }
            else
            {
                HesabCodingCollection = _HesabCodingModelCollection;
            }
            SelectedIndex = tmpSelectedIndex;
            Messenger.Default.Send(this, "HesabCodingListNotify");
        }
  }
}

 <local:HesabCodingListVM x:Key="VM" />
 <Grid DataContext="{Binding Source={StaticResource VM}}" Grid.Row="1">

            <TreeView ItemsSource="{Binding HesabCodingCollection}">
                <TreeView.ItemTemplate>
                    <HierarchicalDataTemplate ItemsSource="{Binding HesabCodings}">
                        <TextBlock Text="{Binding Title}" />
                    </HierarchicalDataTemplate>
                </TreeView.ItemTemplate>
            </TreeView>
        </Grid>
البته در دیزاین این کد از این خط خطا میگیره :
local:HesabCodingListVM x:Key="VM


```
Severity    Code    Description    Project    File    Line    Suppression State
Error        Value cannot be null.
Parameter name: fileOrServerOrConnection    AvinSoft    F:\mhd3\Project\MyShoppingProject\AvinApp\AvinSoft\AvinSoft\View\Windows\HesabCodingListView.xaml    13    
```

----------


## csvbcscp

کد های شما رو دیدم، هیچ مشکلی نداشت، اون خطا ممکنه بدلیل درست نبودن ConnectionString باشه
برای مطمئن شدن از این موضوع می تونید برنامه خودتون رو با متد زیر که داده های Fake داره امتحان کنید:


این خط کد رو از سازنده HesabCodingListVM حذف کنید.
HesabCodingDAL = new HesabCodingDAL();


و به جاش HesabCodingDAL = new HesabCodingDALFake(); رو بنویسید.


و یک کلاس تستی به نام HesabCodingDALFake با محتوای زیر درست کنید.


    public class HesabCodingDALFake
    {
        public ObservableCollection<HesabCoding> HesabCodingSelect(bool v, out string _errorMessage)
        {
            _errorMessage = "";
            return new ObservableCollection<HesabCoding>(
                new System.Collections.Generic.List<HesabCoding>
                {
                new HesabCoding
                {
                    Title ="Parent1",
                    HesabCodings=new ObservableCollection<HesabCoding>()
                    {
                        new HesabCoding
                        {
                            Title ="Child1",HesabCodings=new System.Collections.Generic.List<HesabCoding>
                            {
                                new HesabCoding { Title="Child2"}
                            }
                        }
                    }
                },
                 new HesabCoding
                {
                    Title ="Parent2",
                    HesabCodings=new ObservableCollection<HesabCoding>()
                    {
                        new HesabCoding
                        {
                            Title ="Child2-1",HesabCodings=new System.Collections.Generic.List<HesabCoding>
                            {
                                new HesabCoding { Title="Child2-2"}
                            }
                        }
                    }
                }
                }
                );
        }
    }




می بینید که کد های شما درست کار می کنند.




یک مورد هم توی کد ها و جود داشت، اونم اینکه IDataErrorInfo در کلاس HesabCoding پیاده سازی نشده، و توی HesabCodingListVM نیازی نیست که دوباره INotifyPropertyChanged پیاده سازی بشه، چون قبلاً یک بار این کار توی کلاس BaseViewModel انجام شده.

----------


## تبسم ساینا

دقیقا با این کلاس fake جواب داد.پس چرا با داده های دیتابیس جواب نمیده؟(با داده های دیتابیس فقط یه سطح درخت رو پر میکرد!)

----------


## csvbcscp

برای حل این مشکل چند تا راه حل وجود که ساده ترین راه حل این هست

 اول پراپرتی ParentId رو از کلاس HesabCoding حذف کنید.
یک پراپرتی جدید به نام Parent و از نوع HesabCoding به کلاس HesabCoding  اضافه کنید.
توی متد HesabCodingSelect یک دستور Where به خروجی متد اضافه کنید.
مثل این
var result = new ObservableCollection<HesabCoding>(ctx.HesabCodings  .ToList().Where(x => x.Parent == null)); 

روش بعدی استفاده از Extension method به نام AsHierarchy هست.

----------


## تبسم ساینا

> برای حل این مشکل چند تا راه حل وجود که ساده ترین راه حل این هست
> 
>  اول پراپرتی ParentId رو از کلاس HesabCoding حذف کنید.
> یک پراپرتی جدید به نام Parent و از نوع HesabCoding به کلاس HesabCoding  اضافه کنید.
> توی متد HesabCodingSelect یک دستور Where به خروجی متد اضافه کنید.
> مثل این
> var result = new ObservableCollection<HesabCoding>(ctx.HesabCodings  .ToList().Where(x => x.Parent == null)); 
> 
> روش بعدی استفاده از Extension method به نام AsHierarchy هست.


یه توضیحی میدید برای این Where که اضافه کردید؟
البته من کار کردم بازم جواب نداد.

----------


## تبسم ساینا

این تابع _AsHierarchy_ رو نوشتم ولی جواب نداد :(
        public IEnumerable<HesabCodingHierarchy> GetHesabCodingsHierachy(IEnumerable<HesabCoding> allHesabCodings, HesabCoding parentHesabCoding)        {
            int? parentHesabCodingId = null;


            if (parentHesabCoding != null) //if not root
                parentHesabCodingId = parentHesabCoding.Id;


            var childHesabCoding = allHesabCodings.Where(e => e.ParentId == parentHesabCodingId);


            Collection<HesabCodingHierarchy> hierarchy = new Collection<HesabCodingHierarchy>();


            foreach (var emp in childHesabCoding)
                hierarchy.Add(new HesabCodingHierarchy() { HesabCoding = emp, HesabCodings = GetHesabCodingsHierachy(allHesabCodings, emp) });


            return hierarchy;
        }

----------


## csvbcscp

> یه توضیحی میدید برای این Where که اضافه کردید؟
> البته من کار کردم بازم جواب نداد.


به خاطر اینکه Child ها دوباره توی ریشه نشون داده نشه...
این روش حتماً کار می کنه، احتمالاً مشکل از جای دیگه است.

----------


## csvbcscp

می تونید مثال درست انجام این کار رو توی فایل ضمیمه شده ببینید.

----------

