Monday 30 January 2012

How to use jsTree in ASP.NET Web Forms

jsTree is a free AJAX based tree-view component for jQuery that, with its rich feature set and plug-in architecture, appears to be a perfect candidate for use in an ASP.NET Web Control.

For my purposes I needed an asynchronous tree-view control that allowed me to selectively include checkboxes and to maintain state across post backs. On the face of it jsTree appeared to tick all of the boxes but off-the-bat I discovered that, although state could be applied through the use of cookies, state couldn’t be applied to the instance of the page and it appeared that checkbox state was a bit of a misnomer.

That said, it is undoubtedly a great plug-in with a lot of potential and certainly with enough potential for me to stick with it. The control that I had in mind was one in which the tree and its nodes would be specified in similar terms to the standard ASP.NET TreeView and then consumed by jsTree using JSON. Finally, the state of the tree and its checkboxes would be maintained in the page.

The final result is a tree-view control that syndicates the sophisticated features of jsTree into a simple and easy to deploy ASP.NET Web Control.

To include this control on your ASP.NET Web Page download the JsTreeView binary or source code from http://code.zyky.com/jsTreeView/, it is available as a .NET 2.0, or a .NET 3.5 binary, depending on your requirement and then install it to the Bin directory of your site.

Add a reference to the control in your page:
<%@ Register assembly="JsTreeView" namespace="Custom.Web.UI.WebControls" tagprefix="custom"%>

As this is a jQuery plug-in you'll need to add a link to the jQuery library within the head tags of your web page:
<head runat="server">
    <title>jsTreeView</title>
    <script src="Scripts/jquery-1.7.1.min.js" type="text/javascript"></script>
</head>
Ensure that you include a ScriptManager in your page:
<asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager>

Add the control to your page:
<custom:jsTreeView ID="jsTreeView1" runat="server"
            ServiceUrl="jsTreeSvc.asmx/GetNodes"
            Theme="Default"
            CheckBoxes="true"
            CssClass="jsTreePanel"></custom:jsTreeView>

You’ll note that I have only covered a small number of the properties available in jsTree (just those that meet my current requirement). To use the control simply populate the ServiceUrl property with a path to your web service, set the theme and use checkboxes as required.

Finally you will need to add a web service to create and return the tree view nodes that jsTree will display. To do this set up an appropriate web service (asmx) page, create a new instance of jsTree to return your nodes and then populate it with your tree nodes:
jsTree tree = new jsTree();
jsTreeNode node1 = new jsTreeNode(treeId, "1", "The Good", "#", true);

tree.Nodes.Add(node1);

return tree.Json();

Demo

View a live demo or download the source code at http://code.zyky.com/jsTreeView/

Trails

32 comments:

  1. Dear David,
    your control is great, but in my case is too hard to use a simple scenario.
    Can you add a mode to use your control just passing the TreeNode as serialized Json Data?
    Why the tree state and automatic sons cheked does't work?
    Thanks a lot
    Claudio

    ReplyDelete
    Replies
    1. Hi Claudio

      I just added in the stuff for the jstree wrapper that I needed at the time I have plans to add the other jstree options into the control in the near future, just been really busy.

      You can also manually handle the Json being passed to the server by using the OnClientData property of the control.

      i.e. if you need to pass some additional parameters to the Web service then...

      You web method might be something like this...

      [WebMethod(CacheDuration = 0, EnableSession = false)]
      public string GetNodes(string id, string yourCustomStringVar, bool yourCustomBoolVar)
      {...

      then add a method to the client page in javascript...

      function jsTreeView1_OnClientData(node, tree) {

      var obj = { id: node.attr ? node.attr("id") : tree + "_undefined", yourCustomStringVar: "hello world", yourCustomBoolVar: true }

      return Sys.Serialization.JavaScriptSerializer.serialize(obj);
      }

      and add the OnClientData property to the control...

      <custom:jsTreeView ID="jsTreeView1" runat="server"
      ServiceUrl="jsTreeSvc.asmx/GetNodes"
      Theme="Default"
      CheckBoxes="true"
      OnClientData="jsTreeView1_OnClientData"
      CssClass="jsTreePanel"></custom:jsTreeView>

      I think that's what you mean, but let me know if it isn't.

      Delete
    2. Dear David,
      thanks for your reply and congratulation for your source, really professional and interesting.
      I add my modification for a simple scenario.
      Now I can set directly the jsTree to control.
      May I send you my source(if can be interesting for you and for sharing?).
      Claudio

      Delete
    3. Thanks Claudio, interested in seeing it, just drop me a line on 'contact me' button at the top.

      Delete
  2. This comment has been removed by the author.

    ReplyDelete
  3. Hello David, I have sent to an email regarding this, but there seems to be a limit to the number of nodes allowed in the tree, i get an unknown server error if I exceed circa 200 nodes. Do you have any ideas?

    ReplyDelete
    Replies
    1. Hi Simon, I don't think that I have tried it with that many root nodes but it's probably related to the maxJsonLength, you can configure this through ASP.NET see http://forums.asp.net/t/1090853.aspx/1

      Delete
    2. thank you david! worked a treat!

      Delete
  4. David, sorry to bother you again, but how do you go about retrieving the id of the node which has been clicked? I have bound to select _node as follows, but how would i notify my asp page that the event has fired?

    .bind("select_node.jstree", function (event, data) {

    })

    ReplyDelete
    Replies
    1. Hi Simon,

      You'll need to wire in the jsTree ui plugin (http://www.jstree.com/documentation/ui) as this handles selecting, deselecting and hovering tree items. I didn't need it for this project but I can roll it up for you, won't be able to get onto it for a couple of days though.

      Delete
    2. Hi David,
      I have enabled the ui plugin, and added an event which fires when an item in the tree is selected. However im stuck on a problem where I dont know how to return the ID back out of the dll and notify the asp page that the event has fired.

      Delete
    3. Im using a delegate to return data to the asp form which is working fine, however, i believe this will also be client side when executed? I dont know how i can call my delegate from javascript.

      Delete
    4. Hi Simon,

      I have taken a few minutes out today to do this for you. The class libraries now include the option to post back selected nodes as well as checked nodes, I have also pinned an example at the bottom of the demo page at http://code.zyky.com/jsTreeView/

      Delete
  5. This comment has been removed by the author.

    ReplyDelete
  6. Hi David,

    Awesome work. This plugin sure did save me a lot of time. However I have one issue. I am passing child nodes along with their Javascript actions as href links from a web service. When i click on the node itself, the link action is not triggered, but im able to trigger it by right-clicking on the node and clicking "Open" from the context menu.

    Am I missing something?

    Thanks in advance.

    ReplyDelete
    Replies
    1. Hi Natraj,

      Just set the UI property to false ...

      custom:jsTreeView ID="jsTreeView4" runat="server"
      ServiceUrl="jsTreeSvc.asmx/GetNodes"
      Theme="Classic"
      CheckBoxes="false"
      UI="false"
      CssClass="jsTreePanel"

      ... it's the same convention as described on the jsTree site at http://www.jstree.com/documentation/ui

      Delete
    2. Hi David,

      Worked like a charm. Thanks a ton once again for the fast reply :)

      Delete
  7. Hi David,

    Super!!! I am new to jstree. Do you know how to enable the onSelected event? I expect to trigger a postback SelectedIndexChanged event or invoke a onClick client event.

    ReplyDelete
    Replies
    1. Hi Jeff, I hear ya; today I'll add an 'OnClientNodeSelected' property that you can use to invoke your method.

      Delete
    2. Hi Jeff,

      I have exposed an 'OnClientNodeSelected' property that you can use to invoke your method; I have installed it onto the demo site, just download the current class libraries to use.

      custom:jsTreeView ID="jsTreeView3" runat="server"
      ServiceUrl="jsTreeSvc.asmx/GetNodes"
      Theme="Classic"
      CheckBoxes="false"
      OnClientNodeSelected="nodeSelected"
      CssClass="jsTreePanel"

      Delete
    3. Hi David,

      Thanks for this new property. I also have one more question. I want the Treeview to load only on button click and not on Page Load which is what happens with my page now.

      My Tree:
      custom:jsTreeView ID="TreeView" runat="server" ServiceUrl="Services/ServiceTreeView.asmx/GetNodes"
      OnClientData="TreeView_OnClientData"
      UI="false" CheckBoxes="false"

      On the button click function:
      function buttonClick()
      {
      $("#TreeView").jstree("loaded");
      $("#TreeView").jstree("refresh")
      }

      Client Data function:
      function TreeView_OnClientData(node, tree)
      {
      var UserId = document.getElementById('UserId').value;
      var ClientId= document.getElementById('CId').value;

      var obj = { id: node.attr ? node.attr("id") : tree + "_TreeView", User: UserId, CId: ClientId }

      return Sys.Serialization.JavaScriptSerializer.serialize(obj);
      }

      Thanks in advance once again.

      Delete
    4. Hi Natraj,

      The easiest way is to not return any nodes in the first instance...

      var interactive = false;

      function TreeView_OnClientData(node, tree)
      {
      if(interactive)
      {
      var obj = {id: node.attr ? node.attr("id") : tree + "_undefined"}
      }
      else
      {
      var obj = {id: tree + "_"};
      }

      interactive = true;

      return Sys.Serialization.JavaScriptSerializer.serialize(obj);
      }

      function buttonClick()
      {
      $("#jsTreeView1").jstree("refresh")
      }

      Delete
    5. Hi David,

      This is exactly how I have implemented it in my solution. Just wanted to know if there are any other ways/properties of the plugin to prevent the initial callback as server hits are costly in my application.

      Thanks a ton for your speedy response.

      Delete
  8. David,

    Awesome! Your solution solved my problem.

    ReplyDelete
  9. i am using jstree i would like to know how i can use the functionality on parent click all child nodes should be selected.

    ReplyDelete
    Replies
    1. just do something like this:

      checks all childs -> .bind('check_node.jstree', function (e, data) {
      data.inst.open_all(data.rslt.obj, true);
      })

      unchecks all childs -> .bind('uncheck_node.jstree', function (e, data) {
      data.inst.close_all(data.rslt.obj, true);

      })

      Delete
  10. Hey David,
    Can i have signed DLL (strong name) please?
    I tried it to manually sign it but not working well.

    can i use this with share point 2010?

    ReplyDelete
  11. How to change 'select_limit' to 1?

    ReplyDelete
  12. Hello David,

    First of all my congratulations, i have a great example here. I'm currently developing a ASP4.0 MVC 4 C# project.
    I have a model with the JSON structure, and then the controller with the data (is for use of a web-service but currently all data is hard codded).

    And then i have the js file with all the syntax of jstree. And of course my view.

    My problem is that i have in the view a button to add more nodes to jstree (this is because i may have a tree with thousands of nodes, that being load all together, even using progressive_load, is a pretty bad user experience), but the create_node of jstree does not allow to create a node with children just a parent.

    I believe what my button should do is, make a call to the web-service and then add that information to the JSON e then add those new nodes to the tree.

    I could send you the VS2010 solution, i'm really stuck in this. Thanks in advanced :)

    ReplyDelete
  13. HI David,

    First of all, thanks for your sharing. and i have according to your reference, but i have some question about the plug 'dnd', how can i apply the 'dnd' plugin, i have tried apply it by using :
    "plugins": ["themes", "json_data", "dnd", "contextmenu", "ui"],
    but seems it cannot get the event, do you have any suggestion ?

    ReplyDelete