Introduction
ASP.NET Gridview by default does not provide a way to drag and drop rows. This can be achived using Jquery TableDnD plugin https://github.com/isocra/TableDnD/tree/master/stable
Background
While Jquery TableDnD plugin is fantastic way to allow end users to rearrange data in GridView the issue arise when that rearranged data needs to be sent back to server. As all the manipulation on the GridView is done using JavaScript server will not have a clue about rearranged data/rows.
This tip describes a way to overcome this issue so you can pass the reordered values back to the server and preserve the changes done by user on the client side.
Using the code
1.Create ASP.NET Web Application project in Visual Studio. First of all, we will create a DTO class to hold some data that we can display in a gridview. For this demo, I have created a DTO class of outstanding orders that contains some properties and some dummy data.
[Serializable]
public class Outstanding
{
public int SequenceId { get; set; }
public int RecordId { get; set; }
public string Item { get; set; }
public string Order { get; set; }
public int Line { get; set; }
public int Status { get; set; }
public string ToLocation { get; set; }
public decimal Qty { get; set; }
public DateTime RegDate { get; set; }
public string Location { get; set; }
public decimal AllocQty { get; set; }
public List<outstanding> GetOutstanding()
{
List<outstanding> lstOrders = new List<outstanding>();
lstOrders.Add(new Outstanding() { SequenceId=1, RecordId = 5000, Item = "CocaCola", Order = "000101", Line = 1, Status = 20, ToLocation = "Sydney", Qty = 2000, RegDate = new DateTime(2014, 1, 1), Location = "USA", AllocQty = 100 });
lstOrders.Add(new Outstanding() { SequenceId = 3, RecordId = 5001,Item = "BubbleGum", Order = "000101", Line = 1, Status = 20, ToLocation = "Sydney", Qty = 2500, RegDate = new DateTime(2014, 1, 11), Location = "USA", AllocQty = 300 });
lstOrders.Add(new Outstanding() { SequenceId = 4, RecordId = 5002, Item = "Coffee", Order = "000111", Line = 1, Status = 50, ToLocation = "Melbourne", Qty = 2500, RegDate = new DateTime(2014, 1, 10), Location = "USA", AllocQty = 100 });
lstOrders.Add(new Outstanding() { SequenceId = 5, RecordId = 5003, Item = "Sugar", Order = "000112", Line = 1, Status = 50, ToLocation = "Melbourne", Qty = 2300, RegDate = new DateTime(2014, 1, 10), Location = "NZ", AllocQty = 300 });
lstOrders.Add(new Outstanding() { SequenceId = 8, RecordId = 5004, Item = "Milk", Order = "000112", Line = 1, Status = 50, ToLocation = "Melbourne", Qty = 2300, RegDate = new DateTime(2014, 1, 10), Location = "NZ", AllocQty = 200 });
lstOrders.Add(new Outstanding() { SequenceId = 9, RecordId = 5005, Item = "Green Tea", Order = "000112", Line = 1, Status = 20, ToLocation = "Melbourne", Qty = 300, RegDate = new DateTime(2014, 1, 10), Location = "NZ", AllocQty = 220 });
lstOrders.Add(new Outstanding() { SequenceId = 10, RecordId = 5006, Item = "Biscuit", Order = "000131", Line = 1, Status = 70, ToLocation = "Perth", Qty = 200, RegDate = new DateTime(2014, 1, 12), Location = "IND", AllocQty = 10 });
lstOrders.Add(new Outstanding() { SequenceId = 11, RecordId = 5007, Item = "Wrap", Order = "000131", Line = 1, Status = 20, ToLocation = "Perth", Qty = 2100, RegDate = new DateTime(2014, 1, 12), Location = "IND", AllocQty = 110 });
return lstOrders;
}
}
2.In Default.aspx page in the header section add a reference to JavaScript libraries Jquery and TableDnD plugin
<script src="Scripts/jquery-1.7.1.min.js" type="text/javascript"></script>
<script src="Scripts/jquery.tablednd.js" type="text/javascript"></script>
3. Add a Grdiview to the Default.aspx page inside an UpdatePanle and create BoundColumns to display Outstanding Order data.
<asp:UpdatePanel ID="upnlOutstanding" runat="server" ChildrenAsTriggers="true" UpdateMode="Conditional">
<ContentTemplate>
<h4>
Outstanding Orders</h4>
<asp:GridView ID="grdViewOutstanding" runat="server" AutoGenerateColumns="False"
BackColor="White" BorderColor="#999999" BorderStyle="Solid" CellPadding="5" ForeColor="Black"
GridLines="Both" AllowPaging="True" CellSpacing="1" EmptyDataText="No outstanding orders found"
CssClass="Grid" PageSize="10" AllowSorting="true" OnPageIndexChanging="grdViewOutstanding_PageIndexChanging"
>
<FooterStyle BackColor="#CCCCCC" />
<Columns>
<asp:BoundField DataField="SequenceId" HeaderText="SequenceId" />
<asp:BoundField DataField="Item" HeaderText="Item" />
<asp:BoundField DataField="Order" HeaderText="Order" />
<asp:BoundField DataField="Line" HeaderText="Line" />
<asp:BoundField DataField="Status" HeaderText="Status" />
<asp:BoundField DataField="ToLocation" HeaderText="ToLocation" />
<asp:BoundField DataField="Qty" HeaderText="Qty" />
<asp:BoundField DataField="RegDate" HeaderText="RegDate" DataFormatString="{0:dd/MM/yyyy}" />
<asp:BoundField DataField="Location" HeaderText="Location" />
<asp:BoundField DataField="AllocQty" HeaderText="AllocQty" />
<asp:BoundField DataField="RecordId" HeaderText="RecordId" />
</Columns>
<PagerStyle HorizontalAlign="Center" CssClass="pgr" />
<SelectedRowStyle BackColor="#000099" Font-Bold="True" ForeColor="White" />
</asp:GridView>
</ContentTemplate>
</asp:UpdatePanel>
4.Now in the Page_Load event, we will bind gridview to the dummy data. I have kept data in ViewState for this demo.
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
Outstanding objOutstanding = new Outstanding();
List<outstanding> lstOutstandingOrders = new List<outstanding>();
lstOutstandingOrders = objOutstanding.GetOutstanding();
grdViewOutstanding.DataSource = lstOutstandingOrders;
grdViewOutstanding.DataBind();
ViewState["lstOutstandingOrders"] = lstOutstandingOrders;
upnlOutstanding.Update();
}
}
5.In the Default.aspx page add two hidden fields ( For the purpose of the demo I have made these input fields as text fields so you can view what is going on with the fields. To hide the fields from display change its type from text to hidden). These fields will be used to track which rows are being dragged and the new sequence number for the items. As there is no direct way to let server know that row order for GridView has changed theses two fields will keep a track of changes and that information will be passed to the server on postback.
<label for="eleNewSeq">New Sequence </label> <input id="eleNewSeq" runat="server" style="width:100%" type="text" value="" />
<label for="eleDragged">Record Dragged </label> <input id="eleDragged" runat="server" style="width:100%" type="text" value="" />
6.Now add a script tag in Default.aspx page and apply tableDnD to the GridView in document ready jquery function $(); When you start dragging the row it will apply "DragTableRow" css class to the row that is being dragged. As you can notice there is a onDrop function which will get called when you drop the row. In this function we get the row that was being dragged and get the value of RecordId from it. This value is than gets added to the two hidden fields. I have used ":" as a seperator for fields so I can use one input element to keep track of all the recordIds that are being resequenced. You can call an Ajax function here and update the server instantly but to prevent multiple calls to the serve I have added a button to the Default.aspx page and on its click all the information will go back to the server and application will do the resequencing of data.
$(document).ready(function () {
$("#<%= grdViewOutstanding.ClientID %>").tableDnD({
onDragClass: "DragTableRow",
onDrop: function (table, row) {
var rows = table.tBodies[0].rows, newOrder = '', dragged = '';
dragged = $('#<%= eleDragged.ClientID %>').val();
dragged += row.cells[10].innerText == undefined ? row.cells[10].textContent : row.cells[10].innerText + ":";
for (var i = 1; i < rows.length; i++) {
newOrder += rows[i].cells[10].innerText == undefined ? rows[i].cells[10].textContent : rows[i].cells[10].innerText + ":";
}
$('#<%= eleNewSeq.ClientID %>').val(newOrder);
$('#<%= eleDragged.ClientID %>').val(dragged);
}
});
});
7.Add logic to do the resounding on the server on "Update Order Sequence" button's click event
protected void btnUpdateOrderSequence_Click(object sender, EventArgs e)
{
int recordId = 0;
int seq = 0;
Dictionary<int, int=""> recordIdSeq = new Dictionary<int, int="">();
List<int> recordIds = new List<int>();
List<int> sequences = new List<int>();
foreach (GridViewRow row in grdViewOutstanding.Rows)
{
seq = int.Parse(row.Cells[0].Text.Trim());
sequences.Add(seq); }
if (!string.IsNullOrEmpty(eleNewSeq.Value)) {
string[] allNewOrderdRecordId = eleNewSeq.Value.Trim().Split(':'); for (int i = 0; i < allNewOrderdRecordId.Length; i++)
{
if (int.TryParse(allNewOrderdRecordId[i], out recordId))
{
recordIds.Add(recordId);
}
}
}
if (recordIds.Count > 0)
{
sequences.Sort();
for (int i = 0; i < recordIds.Count; i++)
{
recordIdSeq.Add(recordIds[i], sequences[i]); }
}
if (ViewState["lstOutstandingOrders"] != null)
{
List<outstanding> lstOutstandingOrders = (List<outstanding>)ViewState["lstOutstandingOrders"];
List<outstanding> lstOutStandingResequenced = new List<outstanding>();
int i = 0;
foreach (Outstanding objTemp in lstOutstandingOrders)
{
objTemp.SequenceId = recordIdSeq[objTemp.RecordId]; i++;
lstOutStandingResequenced.Add(objTemp);
}
if (lstOutStandingResequenced.Count > 0)
{
lstOutStandingResequenced = lstOutStandingResequenced.OrderBy(x => x.SequenceId).ToList();
ViewState["lstOutstandingOrders"] = lstOutStandingResequenced;
grdViewOutstanding.DataSource = lstOutStandingResequenced;
grdViewOutstanding.DataBind();
upnlOutstanding.Update();
}
}
}
Points of Interest
This is one way of passing re-sequenced GridView rows back to the server.