Drag and Drop from RadGrid to RadTreeView: a better solution

2 posts, 0 answers
  1. neebs
    neebs avatar
    106 posts
    Member since:
    Apr 2007

    Posted 26 May 2012 Link to this post

    I am trying to implement this functionality, and have referred to the example shown here:

    http://www.telerik.com/community/code-library/aspnet-ajax/general/drag-and-drop-between-radgrid-and-radtreeview.aspx 

    This makes sense, however it is not a valid solution. I have modified the tree view population code to populate it like this:

    root1
    child1
    child2
    root2
    child1
    child2

    If I drop a row from the grid on root2, child2 it shows up in root1, child2. I have chosen unique values for the nodes, as well as adding unique identifiers as attributes, however the approach taken, using the node's text as the identifier of the node onto which the grid row is dropped, does not handle ambiguous node names. 

    This post was originally intended as a request for help, but in looking at the code, a simple solution is to give the nodes unique values, and using those to find the node, using FindNodeByValue. See the code snippets for a modified version of Default.aspx and Default.aspx.cs
    <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
     
    <%@ Register Assembly="Telerik.Web.UI" Namespace="Telerik.Web.UI" TagPrefix="telerik" %>
     
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <head runat="server">
        <title>Untitled Page</title>
    </head>
    <body>
        <script type="text/javascript">
            /* <![CDATA[ */
            var gridId = "<%= RadGrid1.ClientID %>";
                     
            //Handles the OnNodeDropping event of RadTreeView.
            function onNodeDropping(sender, args)
            {          
                var dest = args.get_destNode();
                 
                if (dest)
                {
                    //Handle dropping on a Node if required.
                }
                else
                {
                    dropOnHtmlElement(args);
                }
            }
             
            //Handles the case when a Node is dropped onto an HTML element.
            function dropOnHtmlElement(args)
            {                  
                if(droppedOnGrid(args))
                    return;                
            }
             
            //Checks whether a Node is being dropped onto the RadGrid with ID: 'gridId'.
            //If not, the OnClientDropping event is canceled.
            function droppedOnGrid(args)
            {
                var target = args.get_htmlElement();
                 
                while(target)
                {
                    if(target.id == gridId)
                    {
                        args.set_htmlElement(target);
                         
                        return;                                                                                
                    }
                     
                    target = target.parentNode;
                }
                 
                args.set_cancel(true);
            }
            /* ]]> */
        </script>
        <form id="form1" runat="server">
            <asp:ScriptManager runat="server" ID="ScriptManager1">
            </asp:ScriptManager>
            <telerik:RadAjaxManager runat="server" ID="RadAjaxManager1" OnAjaxRequest="RadAjaxManager1_AjaxRequest">
                <AjaxSettings>
                    <telerik:AjaxSetting AjaxControlID="RadAjaxManager1">
                        <UpdatedControls>
                            <telerik:AjaxUpdatedControl ControlID="RadTreeView1" />
                            <telerik:AjaxUpdatedControl ControlID="RadGrid1" />
                        </UpdatedControls>
                    </telerik:AjaxSetting>
                    <telerik:AjaxSetting AjaxControlID="RadTreeView1">
                        <UpdatedControls>
                            <telerik:AjaxUpdatedControl ControlID="RadTreeView1" />
                            <telerik:AjaxUpdatedControl ControlID="RadGrid1" />
                        </UpdatedControls>
                    </telerik:AjaxSetting>
                    <telerik:AjaxSetting AjaxControlID="RadGrid1">
                        <UpdatedControls>
                            <telerik:AjaxUpdatedControl ControlID="RadGrid1" />
                        </UpdatedControls>
                    </telerik:AjaxSetting>
                </AjaxSettings>
            </telerik:RadAjaxManager>
            <div>
                <div style="float: left; width: 20%;">
                    <telerik:RadTreeView
                        runat="server"
                        Skin="Vista"
                        ID="RadTreeView1"
                        Width="200px"
                        OnClientMouseOver="onNodeMouseOver"
                        OnClientMouseOut="onNodeMouseOut"
                        OnClientNodeDropping="onNodeDropping"
                        EnableDragAndDrop="True"
                        LoadingStatusPosition="BeforeNodeText" OnNodeDrop="RadTreeView1_NodeDrop">
                        <CollapseAnimation Duration="100" Type="OutQuint" />
                        <ExpandAnimation Duration="100" Type="OutQuart" />
                    </telerik:RadTreeView>
                </div>
                <div style="float: left; width: 80%;">
                    <telerik:RadGrid runat="server" Skin="Vista" ID="RadGrid1" OnItemCreated="RadGrid1_ItemCreated"
                        GridLines="None" AllowMultiRowSelection="True" OnNeedDataSource="RadGrid1_NeedDataSource">
                        <MasterTableView>
                            <RowIndicatorColumn Visible="False">
                                <HeaderStyle Width="20px" />
                            </RowIndicatorColumn>
                            <ExpandCollapseColumn Resizable="False" Visible="False">
                                <HeaderStyle Width="20px" />
                            </ExpandCollapseColumn>
                        </MasterTableView>
                        <ClientSettings>
                            <ClientEvents OnRowMouseOver="onRowMouseOver" />
                        </ClientSettings>
                    </telerik:RadGrid>
                </div>
                <telerik:RadScriptBlock runat="server" ID="RadScriptBlock1">
     
               <script type="text/javascript">
                    /* <![CDATA[ */
                    var currentDraggedRow = null;
                    var dataSetIndex = null;
                    var visualClue = null;
                    var currentNode = null;        
                     
                    function onNodeMouseOver(sender, args)
                    {
                        //gets the node upon mousing over the node
                        currentNode = args.get_node();
                    }
                     
                    function onNodeMouseOut(sender, args)
                    {
                        //resets the currentNode value upon mousing out
                        currentNode = null;
                    }
                     
                    function onRowMouseOver(sender, args)
                    {
                        //gets the grid to be draggaed from
                        grid = sender;
                    }
                     
                    function onMouseDown(e, element, dataIndex)
                    {
                        //Store the currently dragged row (TR)
                        currentDraggedRow = element;
                        //Store the data key value of the dragged data grid row
                        dataSetIndex = dataIndex;
         
                        //Attach events to support rendering the visual clue
                        $addHandler(document, "mousemove", mouseMove);
                        $addHandler(document, "mouseup", mouseUp);
                         
                        //Prevent selection of text while dragging
                         
                        //Internet Explorer
                        $addHandler(document, "selectstart", preventSelect);
                         
                        //Other browsers
                        if (e.preventDefault)
                            e.preventDefault();
                    }
                     
                    function preventSelect(e)
                    {
                        e.preventDefault();
                    }
                     
                    function createVisualClue()
                    {
                        var div = document.createElement("div");
                        div.style.position = "absolute";
                        div.className = $get("<%= RadGrid1.ClientID %>").className;
                        div.innerHTML = String.format("<table class='{0}'><tr>{1}</tr></table>",
                            $get("<%= RadGrid1.MasterTableView.ClientID %>").className,
                        currentDraggedRow.innerHTML);
                         
                        return div;
                    }
                     
                    function mouseMove(e)
                    {
                        if (!visualClue)
                        {
                            visualClue = createVisualClue();
                            document.body.insertBefore(visualClue, document.body.firstChild);
                        }
                         
                        visualClue.style.left = e.clientX + 10 + "px";
                        visualClue.style.top = e.clientY + 10 + "px";
                    }
                     
                    function mouseUp(e)
                    {
                        if (visualClue)
                        {
                            //Remove the visual clue
                            visualClue.parentNode.removeChild(visualClue);
                            visualClue = null;
                        }
                        //Detach the events supporting the visual clue
                        $removeHandler(document, "mousemove", mouseMove);
                        $removeHandler(document, "mouseup", mouseUp);
                         
                        //Detach the event preventing selection in Internet Explorer
                        $removeHandler(document, "selectstart", preventSelect);
                         
                        if (currentNode && currentDraggedRow)
                        {                  
                            //Store the value of the node on which the mouse is over
                            $get("<%=TreeNodeText.ClientID%>").value = currentNode.get_text();
                            $get("<%=TreeNodeValue.ClientID%>").value = currentNode.get_value();
                             
                            //Store the data key value of the dragged data grid row
                            $get("<%= DataSetIndex.ClientID %>").value = dataSetIndex;                 
                             
                             
                            //Initiate AJAX request to update the page                             
                            var ajaxManager = $find("<%= RadAjaxManager1.ClientID %>");
                            ajaxManager.ajaxRequest();                 
                        }
                    }                  
                    /* ]]> */
                </script>
     
                </telerik:RadScriptBlock>
                <asp:HiddenField runat="server" ID="DataSetIndex" />
                <asp:HiddenField runat="server" ID="TreeNodeValue" />
                <asp:HiddenField runat="server" ID="TreeNodeText" />
            </div>     
        </form>
    </body>
    </html>

    and

    using System;
    using System.Data;
    using System.Configuration;
    using System.Collections;
    using System.Web;
    using System.Web.Security;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.Web.UI.WebControls.WebParts;
    using System.Web.UI.HtmlControls;
    using Telerik.Web.UI;
     
    public partial class _Default : System.Web.UI.Page
    {
             
        //Creates a datatable and adds it to the session to be used later .
        //The datatable is added to the session as it is being modified during the Drag And Drop operations.
        //Therefore, the "data" object always contains the proper rows and columns.
        private DataTable DataSource
        {
            get
            {
                DataTable data = (DataTable)Session["DataSource"];
                if (data == null)
                {
                    data = new DataTable();
                    data.Columns.Add("Column1");
                    data.Columns.Add("Column2");
                    data.Rows.Add(new object[] { "item1", "value1" });
                    data.Rows.Add(new object[] { "item2", "value2" });
                    data.Rows.Add(new object[] { "item3", "value3" });
                    Session["DataSource"]  = data;
                }
                 
                return data;
            }
        }
     
     
        protected override void OnLoad(EventArgs e)
        {
            if (!Page.IsPostBack)
            {
                RadTreeNode node = new RadTreeNode("root1", "rootValue1");
                RadTreeView1.Nodes.Add(node);
     
                RadTreeNode childnode = new RadTreeNode("child1", "childValue1");
                childnode.Attributes.Add("ID", "1");
                node.Nodes.Add(childnode);
     
                childnode = new RadTreeNode("child2", "childValue2");
                childnode.Attributes.Add("ID", "2");
                node.Nodes.Add(childnode);
     
                node = new RadTreeNode("root2", "rootValue2");
                RadTreeView1.Nodes.Add(node);
     
                childnode = new RadTreeNode("child1", "childValue3");
                childnode.Attributes.Add("ID", "3");
                node.Nodes.Add(childnode);
     
                childnode = new RadTreeNode("child2", "childValue4");
                childnode.Attributes.Add("ID", "4");
                node.Nodes.Add(childnode);
            }
     
            base.OnLoad(e);
        }  
         
     
        protected void RadGrid1_ItemCreated(object sender, GridItemEventArgs e)
        {
            if (e.Item is GridDataItem)
            {
                GridDataItem dataItem = e.Item as GridDataItem;
                //Registers the onmousedown event to be fired upon selecting a grid's row and passes arguments to the event handler
                e.Item.Attributes["onmousedown"] = string.Format("onMouseDown(event, this,{0})", dataItem.DataSetIndex);
                e.Item.Style["cursor"] = "move";
            }
        }
     
        protected void RadAjaxManager1_AjaxRequest(object sender, AjaxRequestEventArgs e)
        {
            //This ajax request is fired upon dropping a grid's row onto a TreeNode
             
            //Finds the node, onto which the grid's row should be dropped, by text using the previously stored value of the "TreeNodeText" hidden field
            //RadTreeNode destinationNode = RadTreeView1.FindNodeByText(TreeNodeText.Value);
            RadTreeNode destinationNode = RadTreeView1.FindNodeByValue(TreeNodeValue.Value);
            DataRow sourceRow = DataSource.Rows[Convert.ToInt32(DataSetIndex.Value)];
     
            //Creates a new node using the respective grid's row information. The text of the TreeNode is set to be the value of the needed cell in the grid's datasource
            RadTreeNode sourceNode = new RadTreeNode((string)sourceRow["Column1"], (string)sourceRow["Column2"]);
     
            destinationNode.Nodes.Add(sourceNode);
            destinationNode.Expanded = true;
     
            //Removes the row that has been dropped onto the treeview
            DataSource.Rows.Remove(sourceRow);
     
            //Rebinds the grid to address the changes made to its datasource
            RadGrid1.Rebind();
        }
     
        protected void RadGrid1_NeedDataSource(object source, GridNeedDataSourceEventArgs e)
        {      
            RadGrid1.DataSource = DataSource;
        }
     
        /// <summary>
        /// Fires when a Node is dropped onto the <see cref="RadTreeView"/> 'RadTreeView1'.
        /// </summary>
        /// <param name="sender">The 'RadTreeView1' object.</param>
        /// <param name="e">Event arguments.</param>
        protected void RadTreeView1_NodeDrop(object sender, RadTreeNodeDragDropEventArgs e)
        {
            if (e.DestDragNode != null)
            {
                //Handle when dropped on another Node.
            }
            else if (e.HtmlElementID == RadGrid1.ClientID)
            {
                DataTable dt = (DataTable)Session["DataSource"];
     
                foreach (RadTreeNode node in e.DraggedNodes)
                {
                    AddRowToGrid(dt, node);
     
                    AddRowsToGrid(dt, node.Nodes);
     
                    node.Remove();
                }
            }       
        }
     
        /// <summary>
        /// Adds rows to the specified Data Table for
        /// every Node in the specified list of nodes and its child Nodes.
        /// </summary>
        /// <param name="dt">The <see cref="DataTable"/> object where rows are added.</param>
        /// <param name="nodes">A <see cref="RadTreeNodeCollection"/> for Nodes.</param>
        private void AddRowsToGrid(DataTable dt, RadTreeNodeCollection nodes)
        {
            foreach (RadTreeNode node in nodes)
            {
                AddRowToGrid(dt, node);
     
                AddRowsToGrid(dt, node.Nodes);
            }
        }
     
        /// <summary>
        /// Adds a new Row to the specified <see cref="DataTable"/> using data
        /// from the specified <see cref="RadTreeNode"/> object and
        /// binds the <see cref="RadGrid"/> 'RadGrid1'.
        /// </summary>
        /// <param name="dt"></param>
        /// <param name="node"></param>
        private void AddRowToGrid(DataTable dt, RadTreeNode node)
        {
            string[] values = { node.Text, node.Value };
            dt.Rows.Add(values);
     
            RadGrid1.DataSource = dt;
            RadGrid1.DataBind();
        }
    }

    Steve
  2. Maria Ilieva
    Admin
    Maria Ilieva avatar
    4017 posts

    Posted 31 May 2012 Link to this post

    Hello Steve,

    Thank you for sharing your approach for the specific case you are implementing. It looks totaly correct and we are sure it will be of a big help for other users trying to achieve the same functionality.

    Greetings,
    Maria Ilieva
    the Telerik team
    If you want to get updates on new releases, tips and tricks and sneak peeks at our product labs directly from the developers working on the RadControls for ASP.NET AJAX, subscribe to their blog feed now.
  3. UI for ASP.NET Ajax is Ready for VS 2017
Back to Top