ساخت نمایش درختی (TreeView) در MVC

شنبه 21 مرداد 1396

در این مقاله نحوه استفاده از کتابخانه جاوا اسکریپتی JS Tree در MVC آموزش داده می شود. در این آموزش اطلاعات مورد نیاز برای نمایش به کاربر، از دیتابیس فراخوانی می شوند.

ساخت نمایش درختی (TreeView) در MVC

به طور کلی، ما یک دیتایس ساده برای دسته بندی ها داریم. دسته بندی ها به دو نوع دسته بندی سردسته و زیردسته تقسیم می شوند.

سپس ما به کمک دستورات Razor مانند @HTML.TreeView، اطلاعات را متناسب با روابط داخل دیتابیس به کاربر نمایش می دهیم.

در این آموزش، مراحل زیر را طی خواهیم کرد

1- شروع به کار

2- ساخت دیتابیس به روش Code First

3- ساخت کامپوننت TreeView

4- نمایش دادن اطلاعات

1- شروع به کار

در این مرحله، ما یک برنامه MVC در ویژوال استودیو 2017 می سازیم. مراحل ساخت برنامه MVC در زیر نمایش داده شده است.

مرحله 1

ویژوال استودیو 2017 را باز کرده و روی New Project کلیک کنید.

مرحله 2

از منوی سمت چپ گزینه web را انتخاب کرده و از بین آیتم های نمایش داده شده، گزینه ASP.NET Web Application را انتخاب کنید.

مرحله 3

از بخش ASP.NET Template، گزینه MVC را انتخاب کنید تا یک برنامه MVC ساخته شود.

با این کارهای انجام شده، یک برنامه MVC ساخته می شود. بعد از انجام تغییرات مورد نظر در layout پیشفرض، می توان برنامه را اجرا کرد.

2- ساخت دیتابیس به روش Code First

حالا وقت ساخت Model مورد نیاز برای دسته بندی ها است. این Model باید قابلیت مدیریت کردن رابطه های چند به چند و ارجاع به خود(self referencing) را داشته باشد. در این جا ما چندین دسته و تعداد زیادی زیر دسته خواهیم داشت.

مرحله 1

روی فولدر Model راست کلیک کرده و از منوی Add یک کلاس با نام Category بسازید.

مرحله 2

Model ساخته شده برای Category باید دارای خصوصیات زیر باشد.

Id                   // Id دسته بندی

Name             // نام دسته بندی

Description    // توضیحات دسته بندی

PID                // Id والد

    using System;  
    using System.Collections.Generic;  
    using System.ComponentModel.DataAnnotations.Schema;  
    using System.Linq;  
    using System.Web;  
      
    namespace TreeViewMVC.Models  
    {  
        public class Category  
        {  
            //Cat Id  
            public int ID { get; set; }  
      
            //Cat Name  
            public string Name { get; set; }  
      
            //Cat Description  
            public string Description { get; set; }  
      
            //represnts Parent ID and it's nullable  
            public int? Pid { get; set; }  
            [ForeignKey("Pid")]  
            public virtual Category Parent { get; set; }  
            public virtual ICollection<Category> Childs { get; set; }  
        }  
    }  

مرحله 3 : ساخت Database context
در داخل پنجره Solution Explorer، روی نام پروژه راست کلیک کرده و از منوی Add یک کلاس با نام context بسازید.

مرحله 4

حالا ما باید Entity Framework را نصب کنیم تا دیتابیس مورد نیازمان ساخته شود.

از منوی بالای ویژوال استودیو بر روی گزینه Tools کلیک کنید و سپس گزینه Nuget Package Manager و پس از آن گزینه Manage nuGet packges for sulotion را انتخاب کنید. از بین تب های بالا، تب Browse را انتخاب کنید و پس از پیدا کردن Entity Framwork، روی آن کلیک کرده و سپس از منوی سمت راست، نام پروژه تان را انتخاب کنید و دکمه install را بزنید.

مرحله 5

حالا ما باید Model که برای Category ساخته بودیم را به Entity Framework اضافه کنیم تا Model ما در دیتابیس ساخته شود.



    using System;  
    using System.Collections.Generic;  
    using System.Linq;  
    using System.Web;  
    using System.Data.Entity;  
    using TreeViewMVC.Models;  
      
    namespace TreeViewMVC  
    {  
        public class context: DbContext  
        {  
            public context()  
                //Your connection string in Web.Config File
                :base("ConnectionString")  
            {  
      
            }  
            
            DbSet<Category> Categories { get; set; }  
        }  
    }  

مرحله 6

حالا وقت ساخت دیتابیس و افزودن قابلیت migration به دیتابیس است. همچنین چند داده اولیه برای نمونه به دیتابیس اضافه خواهیم کرد. از منوی بالای ویژوال استدیو گزینه Tools و سپس NuGet Package Manager و در آخر گزینه Package Manager console را انتخاب نمایید.

حالا وقت فعال کردن migration در پروژه مان است. این عمل را با زدن دستور Enable-Migrations می توان انجام داد.

بعد از اجرا شدن این دستور، Entity Framework یک فولدر مربوط به migration در پروژه ما می سازد. حالا وقت اضافه کردن این قابلیت به پروژه است. با زدن دستور Add-Migrations و انتخاب نام init، این قابلیت به پروژه افزوده خواهد شد.

حالا می خواهیم چند داده ساختگی در متد seed اضافه کنیم. ما دسته بندی های ساختگی را به شکل زیر خواهیم ساخت.

Main Cat1 -> Sub Main Cat1 -> Sub Sub

Main Cat2

Main Cat3 -> Sub Main Cat3

در پنجره Solution Explorer، از داخل فولدر Migrations ، کلاس configuration.cs را باز می کنیم.



    namespace TreeViewMVC.Migrations  
    {  
        using System;  
        using System.Data.Entity;  
        using System.Data.Entity.Migrations;  
        using System.Linq;  
        using TreeViewMVC.Models;  
      
        internal sealed class Configuration : DbMigrationsConfiguration<TreeViewMVC.context>  
        {  
            public Configuration()  
            {  
                AutomaticMigrationsEnabled = false;  
            }  
      
      
            //Creating an object from our  context class to make the DB operations using EF  
      
      
            protected override void Seed(TreeViewMVC.context context)  
            {  
                //Creating Dummy categories and sub categories  
                context.Categories.AddOrUpdate(c=>c.Name,  
                  new Category  { ID =1 , Name="Main Cat1"     , Pid = null , Description  = "Main Cat1"    },  
                  new Category  { ID =2 , Name="Sub Main Cat1" , Pid = 1    , Description  ="Sub Main Cat1" },  
                  new Category  { ID =3 , Name="Sub Sub"       , Pid = 2    , Description  ="Sub Sub"       },  
                  new Category  { ID =4 , Name="Main Cat2"     , Pid = null , Description  ="Main Cat2"     },  
                  new Category  { ID =5 , Name="Main Cat3"     , Pid = null , Description  ="Main Cat3"     },  
                  new Category  { ID =6 , Name="Sub Main Cat3" , Pid = null , Description  ="Sub Main Cat3" }  
                  );  
            }  
        }  
    }  

حالا وقت آن است که داده های ساختگی که در متد seed اضافه کرده ایم، را به دیتابیس بیافزاییم.

از منوی بالای ویژوال استدیو گزینه Tools و سپس NuGet Package Manager و در آخر گزینه Package Manager console را انتخاب نمایید و دستور Update-Database را اجرا کنید.

به کمک SQLserver، دیتابیس ساخته شده را بررسی می کنیم تا نتیجه را مشاهده نماییم.

3- ساخت کلاس TreeView

مرحله 1

حالا وقت ساخت کامپوننت TreeView است.

از داخل پنجره soulotion explorer، بر روی فولدر Models راست کلیک کنید و از منوی Add یک کلاس با نام Tree بسازید.

مرحله 2

در این مرحله کامپوننت مان را خواهیم ساخت.

کلاس Tree پس از دریافت مجموعه اطلاعات model که در اینجا منظور همان Category است، یک HTML tree می سازد که اطلاعات داخل این صفحه با توجه به رابطه شان در دیتابیس، به کمک عناصر <ul>  و<li>  به صورت مرتب و دسته بندی شده نمایش داده خواهد شد.

    using System;  
    using System.Collections.Generic;  
    using System.Linq;  
    using System.Reflection;  
    using System.Web;  
    using System.Web.Mvc;  
    using System.Web.UI;  
    using System.Web.WebPages;  
      
    namespace TreeViewMVC.Models  
    {  
        public static class TreeViewHelper  
        {  
            /// <summary>  
            /// Create an HTML tree from a recursive collection of items  
            /// </summary>  
            public static TreeView<T> TreeView<T>(this HtmlHelper html, IEnumerable<T> items)  
            {  
                return new TreeView<T>(html, items);  
            }  
        }  
      
        /// <summary>  
        /// Create an HTML tree from a resursive collection of items  
        /// </summary>  
        public class TreeView<T> : IHtmlString  
        {  
            private readonly HtmlHelper _html;  
            private readonly IEnumerable<T> _items = Enumerable.Empty<T>();  
            private Func<T, string> _displayProperty = item => item.ToString();  
            private Func<T, IEnumerable<T>> _childrenProperty;  
            private string _emptyContent = "No children";  
            private IDictionary<string, object> _htmlAttributes = new Dictionary<string, object>();  
            private IDictionary<string, object> _childHtmlAttributes = new Dictionary<string, object>();  
            private Func<T, HelperResult> _itemTemplate;  
      
            public TreeView(HtmlHelper html, IEnumerable<T> items)  
            {  
                if (html == null) throw new ArgumentNullException("html");  
                _html = html;  
                _items = items;  
                // The ItemTemplate will default to rendering the DisplayProperty  
                _itemTemplate = item => new HelperResult(writer => writer.Write(_displayProperty(item)));  
            }  
      
            /// <summary>  
            /// The property which will display the text rendered for each item  
            /// </summary>  
            public TreeView<T> ItemText(Func<T, string> selector)  
            {  
                if (selector == null) throw new ArgumentNullException("selector");  
                _displayProperty = selector;  
                return this;  
            }  
      
      
            /// <summary>  
            /// The template used to render each item in the tree view  
            /// </summary>  
            public TreeView<T> ItemTemplate(Func<T, HelperResult> itemTemplate)  
            {  
                if (itemTemplate == null) throw new ArgumentNullException("itemTemplate");  
                _itemTemplate = itemTemplate;  
                return this;  
            }  
      
      
            /// <summary>  
            /// The property which returns the children items  
            /// </summary>  
            public TreeView<T> Children(Func<T, IEnumerable<T>> selector)  
            {  
                //  if (selector == null) //throw new ArgumentNullException("selector");  
                _childrenProperty = selector;  
                return this;  
            }  
      
            /// <summary>  
            /// Content displayed if the list is empty  
            /// </summary>  
            public TreeView<T> EmptyContent(string emptyContent)  
            {  
                if (emptyContent == null) throw new ArgumentNullException("emptyContent");  
                _emptyContent = emptyContent;  
                return this;  
            }  
      
            /// <summary>  
            /// HTML attributes appended to the root ul node  
            /// </summary>  
            public TreeView<T> HtmlAttributes(object htmlAttributes)  
            {  
                HtmlAttributes(HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));  
                return this;  
            }  
      
            /// <summary>  
            /// HTML attributes appended to the root ul node  
            /// </summary>  
            public TreeView<T> HtmlAttributes(IDictionary<string, object> htmlAttributes)  
            {  
                if (htmlAttributes == null) throw new ArgumentNullException("htmlAttributes");  
                _htmlAttributes = htmlAttributes;  
                return this;  
            }  
      
            /// <summary>  
            /// HTML attributes appended to the children items  
            /// </summary>  
            public TreeView<T> ChildrenHtmlAttributes(object htmlAttributes)  
            {  
                ChildrenHtmlAttributes(HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));  
                return this;  
            }  
      
            /// <summary>  
            /// HTML attributes appended to the children items  
            /// </summary>  
            public TreeView<T> ChildrenHtmlAttributes(IDictionary<string, object> htmlAttributes)  
            {  
                if (htmlAttributes == null) throw new ArgumentNullException("htmlAttributes");  
                _childHtmlAttributes = htmlAttributes;  
                return this;  
            }  
      
            public string ToHtmlString()  
            {  
                return ToString();  
            }  
      
            public void Render()  
            {  
                var writer = _html.ViewContext.Writer;  
                using (var textWriter = new HtmlTextWriter(writer))  
                {  
                    textWriter.Write(ToString());  
                }  
            }  
      
            private void ValidateSettings()  
            {  
                if (_childrenProperty == null)  
                {  
                    return;  
                }  
            }  
      
      
            public override string ToString()  
            {  
                ValidateSettings();  
                var listItems = new List<T>();  
                if (_items != null)  
                {  
                    listItems = _items.ToList();  
                }  
      
      
                var ul = new TagBuilder("ul");  
                ul.MergeAttributes(_htmlAttributes);  
                var li = new TagBuilder("li")  
                {  
                    InnerHtml = _emptyContent  
                };  
                li.MergeAttribute("id", "-1");  
      
                if (listItems.Count > 0)  
                {  
                    var innerUl = new TagBuilder("ul");  
                    innerUl.MergeAttributes(_childHtmlAttributes);  
      
                    foreach (var item in listItems)  
                    {  
                        BuildNestedTag(innerUl, item, _childrenProperty);  
                    }  
                    li.InnerHtml += innerUl.ToString();  
                }  
                ul.InnerHtml += li.ToString();  
      
                return ul.ToString();  
            }  
      
            private void AppendChildren(TagBuilder parentTag, T parentItem, Func<T, IEnumerable<T>> childrenProperty)  
            {  
                //  
                if (childrenProperty == null)  
                {  
                    return;  
                }  
                var children = childrenProperty(parentItem).ToList();  
                if (!children.Any())  
                {  
                    return;  
                }  
      
                var innerUl = new TagBuilder("ul");  
                innerUl.MergeAttributes(_childHtmlAttributes);  
      
                foreach (var item in children)  
                {  
                    BuildNestedTag(innerUl, item, childrenProperty);  
                }  
      
                parentTag.InnerHtml += innerUl.ToString();  
            }  
      
            private void BuildNestedTag(TagBuilder parentTag, T parentItem, Func<T, IEnumerable<T>> childrenProperty)  
            {  
                var li = GetLi(parentItem);  
                parentTag.InnerHtml += li.ToString(TagRenderMode.StartTag);  
                AppendChildren(li, parentItem, childrenProperty);  
                parentTag.InnerHtml += li.InnerHtml + li.ToString(TagRenderMode.EndTag);  
            }  
      
            private TagBuilder GetLi(T item)  
            {  
                var li = new TagBuilder("li")  
                {  
                    InnerHtml = _itemTemplate(item).ToHtmlString()  
                };  
                Type myType = item.GetType();  
                IList<PropertyInfo> props = new List<PropertyInfo>(myType.GetProperties());  
                foreach (PropertyInfo prop in props)  
                {  
                    if (prop.Name.ToLower() == "id")  
                        li.MergeAttribute("id", prop.GetValue(item, null).ToString());
                    // Do something with propValue  
                    if (prop.Name.ToLower() == "sortorder")  
                        li.MergeAttribute("priority", prop.GetValue(item, null).ToString());  
                }  
                return li;  
            }  
        }  
    }   

4- نمایش اطلاعات

حالا کار ما با کلاس TreeView و Model تمام شده است و وقت تکمیل View است.

مرحله 1

ما باید jstree را به پروژه اضافه نماییم. که در واقع شامل افزونه های jquery است تا بتوانیم tree های interactive بسازیم.

از منوی بالای ویژوال استودیو بر روی گزینه Tools کلیک کنید و سپس گزینه Nuget Package Manager و پس از آن گزینه Manage nuGet packges for sulotion را انتخاب کنید. از بین تب های بالا، تب Browse را انتخاب کنید و سپس در قسمت جستجو jsTree را تایپ نمایید. پس از پیدا کردن jsTree، روی آن کلیک کرده و دکمه install را بزنید تا jsTree به پروژه اضافه شود.

مرحله 2

از پنجره Solution Explorer، فولدر Views را باز کنید و روی فولدر Home راست کلیک کرده و یکEmpty View با نام TreeView به این فولدر اضافه نمایید.

حالا کدهای زیر را به View اضافه نمایید تا دسته بندی ها نمایش داده شوند.

    @model IEnumerable<TreeViewMVC.Models.Category>  
    @using System.Web.UI.WebControls  
    @using TreeViewMVC.Models;  
      
    <h2>TreeView</h2>  
    <link href="~/Content/jsTree/themes/default/style.min.css" rel="stylesheet" />  
    <div class="form-body">  
        <div id="jstree">  
            @(Html.TreeView(Model)  
                              .EmptyContent("root")  
                              .Children(m => m.Childs)  
                              .HtmlAttributes(new { id = "tree" })  
                          .ChildrenHtmlAttributes(new { @class = "subItem" })  
                              .ItemText(m => m.Name)  
                              .ItemTemplate(  
            @<text>  
                <a href="@item.Description" desc="@item.Description">@item.Name</a>  
            </text>)  
            )  
        </div>  
    </div>  
    @section scripts  
    {  
        <script src="~/Scripts/jsTree3/jstree.min.js"></script>  
        <script>  
            $(function () {  
                var selectedData;  
                $('#jstree').jstree({  
                    "core": {  
                        "multiple": true,  
                        "check_callback": false,  
                        'themes': {  
                            "responsive": true,  
                            'variant': 'larg',  
                            'stripes': false,  
                            'dots': false  
                        }  
                    },  
                    "types": {  
                        "default": {  
                            "icon": "fa fa-folder icon-state-warning icon-lg"  
                        },  
                        "file": {  
                            "icon": "fa fa-file icon-state-warning icon-lg"  
                        }  
                    },  
                    "plugins": ["dnd", "state", "types", "sort", "checkbox"]  
                });  
            });  
        </script>  
    }   

حالا ما باید در داخل کنترلر Home یک اکشن متد برای TreeView بسازیم.

    public ActionResult TreeView()  
    {  
        var db = new context();  
        return View(db.Categories.Where(x => !x.Pid.HasValue).ToList());  
    }   

خروجی

آموزش asp.net mvc

فایل های ضمیمه

برنامه نویسان

نویسنده 3355 مقاله در برنامه نویسان

کاربرانی که از نویسنده این مقاله تشکر کرده اند

در صورتی که در رابطه با این مقاله سوالی دارید، در تاپیک های انجمن مطرح کنید