65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 Kendo 进行带拖放的动态 Treeview

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.80/5 (52投票s)

2019 年 4 月 3 日

CDDL

5分钟阅读

viewsIcon

136535

downloadIcon

2775

本文解释了如何为 Kendo TreeView 创建数据源,特别是针对需要嵌套查询的组织结构图,例如人员或文档。当拖放节点到另一个节点后,会进行保存。请查看演示。

引言

动态树状视图(Dynamic treeview)配合拖放功能,在需要根据特定嵌套层级来排列员工或文档时非常有用。假设组织中的某位员工需要向其上级经理汇报任务,而这位经理又有一位更高级别的经理,依此类推。在另一个例子中,您想整理文档并定义它们的父级,例如 { 财务 --> 薪资 --> 个人 } 或 { 办公室 --> 信件 --> 进口 }。Kendoui TreeView 具有拖放功能,但他们的示例中使用的是 JSON 格式的静态字符串(static string),例如 [{ id: "1", text: "P1", items: [{ id: "5", text: "P2"}] }],您需要将这个字符串传递给 Kendo TreeView 的数据源。我编写了一个方法来生成这些 JSON,以便传递给数据源,并在您更改节点位置(父级)后进行保存。

背景

首先,您需要从 kendoui treeview 添加一些 CSS 和 JS 文件到您的项目中。

有关 Kendo UI treeview 的更多信息,请参阅以下链接,了解如何绑定本地数据及其结构。

使用此代码

下面简要介绍了我如何创建一个方法来生成 JSON 格式。

步骤 1:数据库

首先,创建一个数据库,然后创建一个名为 personal 的表。

步骤 1.1:表

然后填写这个表。

步骤 2:数据模型

然后创建实体数据模型(快速且简单)。

步骤 3:Model 文件夹内的 Model 类

这段代码是为 MVC 编写的,因此下面的部分位于 Model 文件夹中,从表中获取数据。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace Treeview.Models
{
    public class Personal
    {
        public int ID { get; set; }
        public int Parent_ID { get; set; }
        public string Name { get; set; }
       
    }
}

步骤 4:Controller 文件夹内的 Main 方法

我在 controllers 文件夹中创建了一个名为 "PersonalController.cs" 的文件,内容如下:

实际上,下面的逻辑描述将生成自引用的表(self reference table)并以 JSON 格式输出,然后将其内容传递给 treeview 数据源。

我为步骤 3 中的数据模型生成了这个方法,它说明了组织中可能存在有经理或雇主的人员。如果您的数据库不同,您应该查看代码并稍作修改。

要生成分层数据源(hierarchical Data Source)为 JSON 文件,请遵循以下步骤,并参阅链接以获取完整说明。

步骤 1

创建一个名为 Treeview 的**嵌套**方法。

public string Treeview(int itemID, string mystr, int j, int flag)

该方法的输出是 JSON 格式的字符串(string),并接收:

  1. itemID --> 当前节点的 ID。
  2. mystr --> 前一个 JSON 字符串。
  3. { 每次递归调用此方法时,新的字符串都会添加到 mystr 中 }
  4. j 是内部计数器。
  5. flag 表示当前节点是否有子节点。

第二步

第一次调用此方法时,可以从 MVC 的 Action 或应用程序的其他部分调用,例如 Treeview(0,””,0,0)

  1. 假设您不知道当前节点,那么 itemid 就等于零。
  2. 第一次调用时,您还没有任何 JSON 字符串。
  3. j = 0,同理。
  4. flag = 0,同理。

步骤 3

检查当前节点是否有父节点?

  1. 主节点根:如果您是主节点,只需进入此方法,假设您需要
    • 从数据库中选择。
    • 没有父节点且其父 ID 为 NULL 的节点。
    • 在此,生成您的 JSON 字符串,例如 **mystr = "["**
  2. 嵌套节点:如果此方法被调用了一次以上,请检查所有节点。
    • 其父级等于 itemid 的节点。
    • 在此,生成您的 JSON 字符串,例如 **mystr = ",item:["**

步骤 4

现在您拥有了从第三步获取的数据列表。

  1. 创建一个 foreach 循环并调用每个节点,然后按如下方式写入:
  2. foreach (item in querylist)
     **mystr = { id: “” , text: “”**
  3. 在此循环中,检查该节点是否有子节点?
    Querylist= select personal where reportsto=item.id
    1. **(它有子节点)** --> 再次调用 Treeview 方法,例如:
      mystr = Treeview (item.id, mystr, i,1)

      此时,您的 item.id 是当前节点,mystr 是已生成的所有字符串。

      到目前为止,i 对应 jflag 等于一,表示此节点是父节点且有子节点。

    2. **(它没有子节点 && 此节点不是最后一个节点)**
      **mystr =" }, "**
    3. **(它没有子节点 && 此节点是最后一个节点)**
      1. 计算此节点的父节点数量。
        Foreach parent put **mystr = "}]"**
      2. 计算此节点父节点的子节点数量。
        1. if (子节点数 = 0) **mystr = "}]"**
        2. if (子节点数 > 0) **mystr = "}]"**
          1. if (此节点是最后一个子节点 && 此节点的父节点是最后一个父节点)
            **mystr = "},"**
          2. if (此节点是最后一个子节点 && 此节点的父节点是最后一个父节点 &&
            flag=1 )
            **mystr =" },"**
          3. if (此节点是最后一个子节点 && 此节点的父节点是最后一个父节点 && flag=0 )
            **mystr =" }]"**

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Script.Serialization;
using System.Diagnostics;
using System.Text;

namespace Treeview.Controllers
{
    public class PersonalController : Controller
    { 
        int mainNode = 0;
        int childquantity = 0;
        int myflag;

  [HttpPost]
        public ActionResult SaveNode(string childid, string parentid)
        {
            //*****REVISION******Do not allow reportsto = itself 
            if (childid != null && parentid != null && parentid != childid  )
            {
                string source = childid;
                string destination = parentid;
                using (var ctx = new TreeviewEntities())
                {
                    var personals = ctx.Personals.Where(m => m.Name == source).First();

                   var personalsParent = ctx.Personals.Where(m => m.Name ==  destination).First();

                    //***check
                    //*****REVISION******Do not allow make circle in data base 
                    //such as P1 is parent for P2 and also P2 is parent for 
                    //P1 ==> Prevent these mistake by checking it
                    //
           
                    if (personalsParent.ReportsTo!=personals.ID)
                    {
                        personals.ReportsTo = Convert.ToInt16(personalsParent.ID);

                        ctx.SaveChanges();
                    }
                    //***check                    
                }                
            }
            return RedirectToAction("Index");
        }

        public string Treeview(int itemID, string mystr, int j, int flag)
        {           
            List<Personal> querylist = new List<Personal>();
            var ctx = new TreeviewEntities();
            
            if (flag==0)
            {               
               querylist = (from m in ctx.Personals
                             where m.ReportsTo == null
                             select m).ToList();
               mainNode = querylist.Count;
              
                mystr += "[";
            }
            if (flag == 1)
            {

                querylist = (from m in ctx.Personals
                             where m.ReportsTo == itemID
                             select m).ToList();
                mainNode = querylist.Count;
                mystr += ",items:[";
            }
            
            //Below line shows an example of how to make parent node with his child
            //[{ id: "1", text: "P1", items: [{ id: "5", text: "P2" }] }]

           int i=1;
                foreach (var item in querylist)
                {
                            myflag = 0;
                            mystr += "{id:\"" + item.ID + 
                            "\",text:\"" + item.Name + "\"";
                            List<Personal> querylistParent = new List<Personal>();
                            //Check this parent has child or not , if yes how many?
                            querylistParent = (from m in ctx.Personals
                                           where m.ReportsTo == item.ID
                                           select m).ToList();
                            childquantity = querylistParent.Count;
                            //If Parent Has Child again call Treeview with new parameter
                            if (childquantity > 0)
                            {
                                mystr = Treeview(item.ID, mystr, i, 1);
                            }
                            //No Child and No Last Node
                            else if (childquantity == 0 && i < querylist.Count)
                            {
                                mystr += "},";
                            }
                            //No Child and Last Node
                            else if (childquantity == 0 && i == querylist.Count)
                            {                               
                               int fcheck2=0;
                               int fcheck3 = 0;
                               int counter = 0;
                               int flagbreak = 0;

                                int currentparent;
                                List<Personal> parentquery ;
                                List<Personal> childlistquery;
                                TempData["counter"] =0;
                                currentparent = Convert.ToInt16(item.ReportsTo);
                                int coun;
                                while (currentparent != 0)
                                {
                                    //count parent of parent
                                   
                                     fcheck2 = 0;
                                     fcheck3 = 0;
                                     parentquery = new List<Personal>();
                                     parentquery = (from m in ctx.Personals
                                                      where m.ID == currentparent
                                                      select m).ToList();
                                     var rep2 = (from h in parentquery 
                                     select new { h.ReportsTo }).First();
                                   
                                    //put {[ up to end
                                    
                                    //list of child
                                     childlistquery = new List<Personal>();
                                     childlistquery = (from m in ctx.Personals
                                                      where m.ReportsTo == currentparent
                                                     select m).ToList();

                                     foreach (var item22 in childlistquery)
                                        {                                           
                                            if (mystr.Contains(item22.ID.ToString()))
                                            {

                                                if (item22.ReportsTo == currentparent)
                                                {
                                                    fcheck3 += 1;
                                                    if (fcheck3 == 1)
                                                    {
                                                        counter += 1;
                                                    }
                                                }
                                            }
                                            else
                                            {                                               
                                                myflag = 1;
                                                if (item22.ReportsTo == currentparent)
                                                {
                                                     fcheck2+=1;
                                                     if (fcheck2==1)
                                                     {
                                                         counter -= 1;
                                                         flagbreak = 1;
                                                     }
                                                }
                                            }
                                        }

                                     var result55 = 
                                     (from h in parentquery select new { h.ID }).First();
                                        coun = Convert.ToInt16(result55.ID);
                                        TempData["coun"] = Convert.ToInt16(coun);
                                        currentparent = Convert.ToInt16(rep2.ReportsTo);
                                        if (flagbreak == 1)
                                        {
                                            break;
                                        }                                       
                                }
                              
                                for (int i2 = 0; i2 < counter; i2++)
                                {
                                    mystr += "}]";
                                }

                                List<Personal> lastchild = new List<Personal>();
                                lastchild = (from m in ctx.Personals
                                                where m.ReportsTo == item.ReportsTo
                                                select m).ToList();

                                List<Personal> lastparent = new List<Personal>();
                                lastparent = (from m in ctx.Personals
                                                where m.ReportsTo == null
                                                select m).ToList();

                                if (lastchild.Count > 0)
                                {
                                    var result_lastchild = 
                                    (from h in lastchild select new { h.ID }).Last();
                                    var result_lastparent = 
                                    (from h in lastparent select new { h.ID }).Last();
                                    int mycount = Convert.ToInt16(TempData["coun"]);
                                    if (item.ID == result_lastchild.ID && 
                                    mycount == result_lastparent.ID && myflag == 0)
                                    {
                                       mystr += "}]";
                                    }
                                    else if (item.ID == result_lastchild.ID && 
                                    mycount == result_lastparent.ID && myflag == 1)
                                    {
                                       mystr += "},";
                                    }
                                    else if (item.ID == result_lastchild.ID && 
                                    mycount != result_lastparent.ID)
                                    {
                                       mystr += "},";
                                    }                                    
                                }
                                //  finish }]
                                else if (lastchild.Count == 0 && item.ReportsTo == null)
                                {
                                   mystr += "}]";
                                }
                            }
                            i++;  
                    }                    
            
            return mystr;
        }
       
        public ActionResult Index()
        {           
            ViewData["treeviewds"] = Treeview(0, "", 0, 0);
            return View();           
        }         
    }    
}

步骤 5:UI (视图)

然后,我在 Views --> Personal 文件夹中创建了一个名为 "Index.cshtml" 的文件,内容如下:

步骤 5.1:Head (头部)

head 部分,您应该定义 CSS 和 JS 文件。

步骤 5.2:Body (主体)

然后创建一个 treeview div 用于追加 JSON 字符串,以及 tvformat 用于传递 viewdata["treeviewds"]

步骤 5.3:Script (脚本)

然后编写脚本以从数据库获取 treeview 数据,并 POST (保存) 更改了父节点(位置)的节点。

步骤 5.4:CSS

然后为此创建样式。

历史

2014 年 7 月 30 日

我已将演示链接添加到本文中。

2014 年 8 月 1 日

我已在本文中修正了两个 bug:第一个是如果您点击一个节点然后将其拖放到自身,在 SaveNode Action 中,我已经编辑了代码,如果 parentid == childid,则不执行任何操作。

第二个是您无法在 treeview 中创建循环,例如 P1P2 的父节点,而 P2 也是 P1 的父节点,这是不正确的,会造成循环。

反馈

请随时对本文提供任何反馈;很高兴看到您的评论和投票。如果您有任何问题,请随时在此处提问。

© . All rights reserved.