Persisting the position of jQuery Draggables in ASP.NET

It was bound to happen - you knock up an article on jQuery draggables one day, and the next, someone like Jim ;-) comes along and asks about persisting the position of the dragged item across Postbacks, or even sessions. I suppose it's my fault - I mentioned using draggables in a previous life in the context of saving the position, so it's only fair I share how that's done.

For the purposes of this example, the markup for the page is extremely simple:

 

<form id="form1" runat="server"> 

<div> 

  <img src="images/alfie.jpg" alt="" id="d1" runat="server" />

</div> 

</form> 

 

This is a simple html image, that has been converted to an ASP.NET HtmlControl by adding runat="server". I need to do this, because later, I want to reference the image from Code-Behind. In the meantime, I'll get to the Javascript that makes the image draggable:

 

<script src="script/jquery-1.3.1.min.js" type="text/javascript"></script> 

<script src="script/ui.core.min.js" type="text/javascript"></script> 

<script src="script/ui.draggable.min.js" type="text/javascript"></script> 

<script type="text/javascript"> 

  $(function() {

    $("#d1").draggable(

    {

      drag: function(event, ui) {

        $("#d1").css("opacity", "0.6"); // Semi-transparent when dragging

      },

      stop: function(event, ui) {

        saveCoords(ui.absolutePosition.left, ui.absolutePosition.top, ui.helper.attr('id'));

        $("#d1").css("opacity", "1.0"); // Full opacity when stopped

      },

      cursor: "move"

    });

  });

</script> 

 

Most of this is familiar if you read the previous article, so I won't dwell on it. The major difference, however is that a call to a new function, saveCoords() is made within the stop callback. 3 arguments are supplied: the x coordinate, y coordinate and the id of the current draggable - ui.helper.attr('id'). Let's look at what that function does:

 

function saveCoords(x, y, el, id) {

  $.ajax({

    type: "POST",

    url: "Services/Coordinates.asmx/SaveCoords",

    data: "{x: '" + x + "', y: '" + y + "', element: '" + el + "', userid: '1'}",

    contentType: "application/json; charset=utf-8",

    dataType: "json",

    success: function(response) {

      if (response.d != '1') {

        alert('Not Saved!');

      }

    },

    error: function(response) {

      alert(response.responseText);

    }

  });

}

 

If you have seen my previous articles on Web Services with jQuery, you will immediately spot that this function makes a call to a Web Service called Coordinates, invoking its method, SaveCoords(). It passes in the x and y coordinates, the id of the element and a user ID (hardcoded for thos example, bit could be provided by a session variable, for instance). Here's the Web Service:

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Web;

using System.Web.Services;

using System.Web.Script.Services;

using System.Data.SqlClient;

using System.Data;

 

/// <summary>

/// Summary description for SaveCoords

/// </summary>

[WebService(Namespace = "http://tempuri.org/")]

[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]

// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.

 

[ScriptService]

public class Coordinates : WebService {

 

    [WebMethod]

    public int SaveCoords(int x, int y, string element, int userid)

    {

      string connect = "Server=MyServer;Database=Tests;Trusted_Connection=True;";

      int result = 0;

      using (SqlConnection conn = new SqlConnection(connect))

      {

        string query = "UPDATE Coords SET xPos = @xPos, yPos = @yPos WHERE Element = @Element AND UserID = @UserID";

        using (SqlCommand cmd = new SqlCommand(query, conn))

        {

          cmd.Parameters.AddWithValue("xPos", x);

          cmd.Parameters.AddWithValue("yPos", y);

          cmd.Parameters.AddWithValue("Element", element);

          cmd.Parameters.AddWithValue("UserID", userid);

          conn.Open();

          result = (int)cmd.ExecuteNonQuery();

        }

      }

      return result;

    }

}

 

The WebMethod simply takes the arguments passed to it and saves them to the database. It returns the number of rows affected for error checking. Great. So the position of the element is saved. How does that get translated to a draggable being positioned where it was left when the page is next requested by the user with the ID of 1? We need another Web Method:

 

[WebMethod]

public DataTable GetSavedCoords(int userid)

{

  DataTable dt = new DataTable();

  string connect = "Server=MyServer;Database=Tests;Trusted_Connection=True;";

  using (SqlConnection conn = new SqlConnection(connect))

  {

    string query = "SELECT xPos, yPos, Element FROM Coords WHERE UserID = @UserID";

    using (SqlCommand cmd = new SqlCommand(query, conn))

    {

      cmd.Parameters.AddWithValue("UserID", userid);

      SqlDataAdapter da = new SqlDataAdapter(cmd);

      da.Fill(dt);

      return dt;

    }

  }

}

 

Taking the User ID as an argument, this method returns a DataTable filled with the positions of all elements saved against the user, and it is invoked in the Page_Load() event in the Code Behind:

 

public partial class PersistDraggable : Page

{

    protected void Page_Load(object sender, EventArgs e)

    {

      Coordinates coords = new Coordinates();

      DataTable dt = coords.GetSavedCoords(1);

      foreach (DataRow row in dt.Rows)

      {

        HtmlControl ctl = (HtmlControl)this.FindControl(row["element"].ToString());

        if (ctl != null)

        {

          ctl.Style.Add("left", row["xPos"].ToString() + "px");

          ctl.Style.Add("top", row["yPos"].ToString() + "px");

        }

      }

    }

}

 

The Web Service class is instantiated and its GetSavedCoords() method is called. For each row in the returned DataTable, an element on the page is sought with the same id as one saved in the database. If it is found, it has a dash of CSS applied to it through Attributes.Add(). These set the CSS top and left values, and Voila! When the page is rendered again, the image is exactly where it was left last time.

 

Date Posted: Wednesday, February 4, 2009 10:29 PM
Last Updated: Sunday, April 26, 2009 8:45 PM
Posted by: Mikesdotnetting
Total Views to date: 33442

16 Comments

Monday, March 30, 2009 8:08 PM - jamie

excellent! i done this a few years ago, i remember the pain i went through to get it working! no its all in one area!

Tuesday, March 31, 2009 9:01 PM - Bob

Thanks Mike. What was probably simple for you would have taken me days if not weeks!

Friday, April 24, 2009 6:48 AM - Sharjeel

Is it necessary to have a WebService for this, or i can do something in me cs page itself also?

Friday, April 24, 2009 8:01 PM - Mike

@Sharjeel

No - you can use a Page Method, or you can simply put the code in a codebehind of a blank aspx file.

Friday, April 24, 2009 9:03 PM - Tim

Any chance you can post up a download for this? I like the work you did and you explained it well, but am still trying to figure out how all the webservice stuff works.

Thanks

Sunday, April 26, 2009 12:40 PM - Liammcmullen

Great bit of code. saved me a load of work many thanks.
Should this be given away free?

Sunday, April 26, 2009 9:00 PM - Mike

@Tim

I don't do downloads, I'm afraid. But you don't need a web service for this. You can use a Page Method instead. Have a look here for how to so that:
http://encosia.com/2008/05/29/using-jquery-to-directly-call-aspnet-ajax-page-methods/

Sunday, April 26, 2009 9:01 PM - Mike

@Liam,

I am occasionally in Belfast. You are permitted to buy me a beer when I am next there :-)

Tuesday, April 28, 2009 7:45 AM - Sharjeel

thanks mike for the answer, i actually want to save the coords to a session variable or something for easy access.

Tuesday, April 28, 2009 10:03 AM - Daren

Mike, that's fanstic.

I am hoping to create a restricted set of div containers (regions) on my page where the users can drag and drop stuff, but I don't them to lay stuff on top of each other, I just want the the widgets to sit nicely next to or on top of each other, and then to record their position as an index perhaps?

Any ideas on how this would best be accomplished?

Thursday, May 21, 2009 7:03 AM - janlie mcdovish

hey mike, great sample you have there. it helps in our project a lot :)

Friday, March 5, 2010 4:53 PM - Harvey

Thanks for this great tutorial Mike.
Is there any chance you could explain the best way to approach adding multiple divs on a page all of which are draggable and updateable? I am going to use your web service Methods, but I need to have approx. 6 x divs on my page. many thanks

Monday, January 28, 2013 3:42 AM - Frank

I have an issue with your Page Load code...
HtmlControl ctl = (HtmlControl)this.FindControl(row["element"].ToString());
if (ctl != null)
{
ctl.Style.Add("left", row["xPos"].ToString() + "px");
ctl.Style.Add("top", row["yPos"].ToString() + "px");
}

I have a Div that has margines, etc that contains my elements...
But for some reason, the more elements I have in the div, the worse it is.
- 1 object, the coordinates is off by 20px,20px after a refresh (even though in the database it shows it to be the correct coodinates)
- 2 objects, the coordinates is off by 50px,20px after a refresh (even though in the database it shows it to be the correct coodinates)
- 3 objects, the coordinates is off by 80px,20px after a refresh (even though in the database it shows it to be the correct coodinates)

etc etc. any clues?

Friday, October 11, 2013 5:20 PM - Paul

Hi,
If the item is dragged and dropped somewhere, I can't move the same item from there to anywhere else without reloading the page. That means the item can be moved only once. Is it possible to make it movable multiple times to multiple locations?
Is it possible to download the sample code files?
Thanks,
Paul

Wednesday, April 2, 2014 11:42 AM - praveen

What does "Coords" table contains..iam using sql server..can you please send me the create and insert data sql scripts

Wednesday, April 2, 2014 1:35 PM - Mike

@prvaeen,

The "Coords" table can contain anything your application needs it to contain. At the very least, it needs to be capable of storing the x and y coordinates of the draggable element. You can save those as two separate values or as one, separated with a comma. Up to you...
Add your comment

If you have any comments to make about this article, please use this form to do so. Make sure that your comment relates specifically to the article above. More general comments can be posted through the form on the Contact page.

Please note, all comments are moderated, and some may not be published. The kind of things that will ensure your comment is deleted without ever seeing the light of day are as follows:

  • Not relevant to the article
  • Gratuitous links to your own site or product
  • Anything abusive or libellous
  • Spam
  • Anything in a language I don't understand including gibberish.

I do not pass email addresses on to spammers, so a valid one will assist me in responding to you personally if required.

Recent Comments

Allen Michaels 12/17/2014 4:37 PM
In response to Cascading DropDownLists with jQuery and ASP.NET
Fantastic thank you so much!...

Emily 12/17/2014 12:36 PM
In response to Parameterized IN clauses with ADO.NET and LINQ
Thanks, very helpful!!!! Can i use this for multiple in's ????? IN (.....) and IN(...) and IN...

sss 12/16/2014 3:06 PM
In response to Solving the Operation Must Use An Updateable Query error
good...

Gjuro 12/15/2014 10:30 PM
In response to Examining the Edit Methods and Edit View
You have one fromr (and it should be from, I suppose). :-)...

Gjuro 12/15/2014 10:27 PM
In response to Adding Search
Hi, thnx for all this C#->VB translations. Yet, the following code block is (slightly) in error it a...

Scot 12/14/2014 1:39 PM
In response to Entity Framework 6 Recipe - Alphabetical Paging In ASP.NET MVC
Thanks,Mike I found solution....

Gjuro 12/13/2014 10:52 PM
In response to Accessing Your Model's Data from a Controller
The article mentions "Creating an Entity Framework Data Model for an ASP.NET MVC Application" (at is...

Samuel 12/13/2014 8:40 AM
In response to Displaying The First n Characters Of Text
I have failed to use the extension because it throws an error that it doesn't recognise the chop be...

Ignas 12/12/2014 5:11 PM
In response to Cleaner Conditional HTML Attributes In Razor Web Pages
Any suggestions for Html Helper elements with HtmlAttributes, when you need to conditionally set it...

Gautam 12/11/2014 8:50 PM
In response to Validation In Razor Web Pages 2
Hi Mike Is this required for V3, non html helper input...