KNOWLEDGE BASE

Multi-node tree picker source code from CodeGarden 2010


This post is about the data type I presented during the Umbraco package competition (which ended up winning too! :). It's a multi-node picker with the full tree interface to allow you to select the nodes you want. It's also sort-able with drag/drop functionality.

image

It's a very simple control but has the capability to be made into a very awesome data type which is why I'm starting up a new CodePlex project called: Data Types for Umbraco (found here). I'm hoping that people in the Umbraco community will want to become developers on this project so a bunch of us can collaborate to create some seriously amazing data types for Umbraco 4.5. Please let me know if you want to contribute to this project as i think it could have huge potential. Instead of everyone creating their own closed source data types and developing them all in different ways, this would help unify and standardize the way we create data types.

So the first thing I will put up there is the full source for this data type as it was meant to be. However, I'm not going to get around to that today, so in the meantime I've put the source for this control right here. Remember, this is not really the best way to make this data type but it will give you a good indication of how to use the new JavaScript tree API and how to build a simple UserControl data type.

Enjoy!

ASCX Code

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="TreePicker.ascx.cs"
   
Inherits="ExamineDemo1.usercontrols.TreePicker" %>
<%@ Register TagPrefix="umb" TagName="tree" Src="~/umbraco/controls/Tree/TreeControl.ascx" %>
<script type="text/javascript">

//a reference to the hidden field
//storing the selected node data
var hiddenField = jQuery("#<%=PickedNodes.ClientID%>");

//create a sortable, drag/drop list and
//initialize the right panel with previously
//saved data.
jQuery
(document).ready(function () {
    jQuery
(".right").sortable({
        stop
: function (event, ui) { StorePickedNodes(); }
   
});
    jQuery
(".item a.close").live("click", function () {
        jQuery
(this).parent().remove();
   
});

   
//now rebuild the selected items
   
try {
       
var json = eval('(' + hiddenField.val() + ')');
        jQuery
(".right").html(unescape(json.markup));
       
StorePickedNodes();
   
}
   
catch (err) { };
});

//Add event handler to the tree API.
//Bind to the window load event as the document ready event
//is too early to query for the tree api object
jQuery
(window).load(function () {
   
//add a handler to the tree's nodeClicked event
    jQuery
("#<%=TreePickerControl.ClientID%>")
       
.UmbracoTreeAPI()
       
.addEventHandler("nodeClicked", function (e, node) {
           
AddToRight(node);
   
});
});

function AddToRight(node) {
   
//get the node id of the node selected
   
var nodeId = jQuery(node).attr("id");
   
//check if node id already exists in the right panel
   
if (jQuery(".right").find("li[rel='" + nodeId + "']").length > 0) {
       
return;
   
}
   
//create a copy of the node clicked on the tree
   
var jNode = jQuery(node).clone().find("a:first")
   
//remove un-needed attributes
    jNode
.removeAttr("href")
       
.removeAttr("umb:nodedata")
       
.attr("href", "javascript:SyncItems(" + nodeId + ");");
   
//build a DOM object to put in the right panel
    jQuery
("<div class='item'><ul class='rightNode'>" +
           
"<li rel='" + nodeId + "' class='closed'>" +
           
"</li></ul><a class='close' href='javascript:void(0);'>[X]</a></div>")
       
.appendTo(".right")
       
.find(".closed").append(jNode);
   
//now update the hidden field with the
   
//node selection
   
StorePickedNodes();
}

//A method to sync the left tree to the item
//selected in the right panel
function SyncItems(nodeId) {
    jQuery
("#<%=TreePickerControl.ClientID%>")
       
.UmbracoTreeAPI()
       
.syncTree(nodeId.toString());
}

//were going to store both the node ids
//and the html markup to re-render the
//right hand column as JSON to be put into
//the database.
function StorePickedNodes() {
   
var val = "[";
    jQuery
(".right .item ul.rightNode li").each(function () {
        val
+= jQuery(this).attr("rel") + ",";
   
});
   
if (val != "[") val = val.substr(0, val.length - 1);
    val
+= "]";
   
//append the html markup
   
var obj = "{ \"val\": " + val + ", \"markup\": \"" + escape(jQuery(".right").html()) + "\"}";
    hiddenField
.val(obj);      
}

</script>

<%--Inline styles are dodgy, but simple for
this demonstration--%>

<style type="text/css">
.multiTreePicker .item ul.rightNode
{
   
float:left;
   
margin:0;
   
padding:0;          
}
.multiTreePicker .item ul.rightNode li
{
   
margin:0;
   
padding: 0;
   
list-style:none;
   
font:icon;
   
font-family:Arial,Lucida Grande;
   
font-size:12px;
   
min-height:20px;
}
.multiTreePicker .item ul.rightNode li a
{
   
background-repeat:no-repeat !important;
   
border:0 none;
   
color:#2F2F2F;
   
height:18px;
   
line-height:18px;
   
padding:0 0 0 18px;
   
text-decoration:none;
}
.multiTreePicker .item a
{
   
float:left;
}  
.multiTreePicker .item a.close
{
   
margin-left:5px;
}
.multiTreePicker .item
{
   
cursor: pointer;
   
width:100%;
   
height:20px;
}
.multiTreePicker .left.propertypane
{
   
width: 300px;
   
float: left;
   
clear:none;
   
margin-right:10px;
}
.multiTreePicker .right.propertypane
{
   
width: 300px;
   
float: left;
   
clear:right;
   
padding:5px;
}
.multiTreePicker .header
{
   
width:622px;
}
   
</style>

<div class="multiTreePicker">
   
<div class="header propertypane">
       
<div>Select items</div>
   
</div>
   
<div class="left propertypane">        
       
<umb:tree runat="server" ID="TreePickerControl"
           
CssClass="myTreePicker" Mode="Standard"
           
DialogMode="id" ShowContextMenu="false"
           
IsDialog="true" TreeType="content" />
   
</div>
   
<div class="right propertypane">
   
</div>
</div>

<asp:HiddenField runat="server" ID="PickedNodes" />

C# Code Behind


public partial class TreePicker :
   
System.Web.UI.UserControl, IUsercontrolDataEditor
{
       


   
#region IUsercontrolDataEditor Members

   
public object value
   
{
       
get
       
{
           
//put the node in umbraco
           
return PickedNodes.Value;
       
}
       
set
       
{
           
//get from umbraco
           
PickedNodes.Value = value.ToString();
       
}
   
}

   
#endregion
}

Need an Umbraco Master?

Here at Simon Antony, we have an in house certified Umbraco Grand Master available for hire. Got a problem with your site, need architecture advice, give us a call to speak to Simon directly and see how we can help

Contact Simon Today!