PDA

View Full Version : Ext.NET 3 Grid row drag/drop and text selection



anup
Jan 19, 2015, 1:18 PM
Hi,

This is a follow up to this thread:

http://forums.ext.net/showthread.php?33251-Grid-row-drag-drop-and-text-selection

The previous thread was for Ext.NET 1

This follow up is to get the same thing working for Ext.NET 3

The requirements are the same:

A grid that has text selection allowed (to copy/paste grid cell text)
Grid has checkbox row selection (this may work with other row selection methods but I have not tested them)
Drag/drop enabled on the grid
Allowing text selection on selected rows while drag/drop enabled (not possible by default)


Ext JS 5.1 has many differences to Ext JS 3 which is what Ext.NET 1 is based on so the original solution I had of course won't work for the latest version.

It is a bit simpler in this latest version, once I found where I had to make the change.

In Ext JS 5.1, there is a DragZone component in Ext.view.DragZone

Its default implementation for the getDragData method is this:



getDragData: function (e) {
var view = this.view,
item = e.getTarget(view.getItemSelector())

if (item) {
return {
copy: view.copy || (view.allowCopy && e.ctrlKey),
event: e,
view: view,
ddel: this.ddel,
item: item,
records: view.getSelectionModel().getSelection(),
fromPosition: Ext.fly(item).getXY()
};
}
}


The above is saying if you find the row corresponding to the item's row selected make that row draggable.

In my case, while a row is selected, a user may not want to drag straight away, so wanting to select text, copy/paste etc is useful to preserve. The trade off is that I will only allow dragging if a particular part of the row is selected. In my case I have a row number column, so I will use that.

So, we extend the above as follows:



Ext.define('overrides.view.DragZone', {
override: 'Ext.view.DragZone',

getDragData: function (e) {
var view = this.view,
item = e.getTarget(view.getItemSelector()),
target = e.getTarget();

// ADDED: Allow text selection to continue if not dragging anything other than the row numberer column
if (target && target.className.indexOf('x-grid-cell-inner-row-numberer') == -1)
return false;

if (item) {
return {
copy: view.copy || (view.allowCopy && e.ctrlKey),
event: e,
view: view,
ddel: this.ddel,
item: item,
records: view.getSelectionModel().getSelection(),
fromPosition: Ext.fly(item).getXY()
};
}
}
});


All I've done is add a target variable, and check if the target DOM element has a class name corresponding to the row numberer (i.e. was it the row numberer cell for that row that was clicked to start the drag process). If not, then return false, and let other configured behaviour continue, else continue with the drag process.

So 3 things I've done to put it together:

Applied the override for getDragData]/var]
Configured the grid's View to allow text selection (using [var]EnableTextSelection="true")
(Optional) To make it a bit easier to know that a row can be dragged, inside the row numberer cell, I've added a bit of CSS to insert a drag handle (background image) when that row is selected.


Here is an example putting it together:



<%@ Page Language="C#" %>

<script runat="server">
private object[] TestData
{
get
{
DateTime now = DateTime.Now;

return new object[]
{
new object[] { "3m Co", 71.72, 0.02, 0.03, now },
new object[] { "Alcoa Inc", 29.01, 0.42, 1.47, now },
new object[] { "Altria Group Inc", 83.81, 0.28, 0.34, now },
new object[] { "American Express Company", 52.55, 0.01, 0.02, now },
new object[] { "American International Group, Inc.", 64.13, 0.31, 0.49, now },
new object[] { "AT&T Inc.", 31.61, -0.48, -1.54, now },
new object[] { "Boeing Co.", 75.43, 0.53, 0.71, now }
};
}
}

protected void Page_Load(object sender, EventArgs e)
{
if (!X.IsAjaxRequest)
{
Store1.DataSource = TestData;
Store1.DataBind();
}
}
</script>

<!DOCTYPE html>
<html>
<head runat="server">
<title>Drag and Drop GridPanel Example</title>

<style>
.x-grid-item-selected .x-grid-cell-row-numberer {
background: no-repeat left center;
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOAQMAAAA lhr+SAAAABlBMVEX///+Tk5OZ/c7wAAAAAXRSTlMAQObYZgAAABtJREFUeF59xrENAAAAgjAP5v9 ZjbsNAwramTFNygNhn4U+IQAAAABJRU5ErkJggg==') /*i/icons/drag-indicator.png*/;
cursor: move;
}
</style>

<ext:ResourcePlaceHolder Mode="ScriptFiles" />

<script>
Ext.define('overrides.view.DragZone', {
override: 'Ext.view.DragZone',

getDragData: function (e) {
var view = this.view,
item = e.getTarget(view.getItemSelector()),
target = e.getTarget();

// this if block added to allow text selection to continue if not dragging anything other than the checkbox column
if (target && target.className.indexOf('x-grid-cell-inner-row-numberer') == -1)
return false;

if (item) {
return {
copy: view.copy || (view.allowCopy && e.ctrlKey),
event: e,
view: view,
ddel: this.ddel,
item: item,
records: view.getSelectionModel().getSelection(),
fromPosition: Ext.fly(item).getXY()
};
}
}
});
</script>
</head>
<body>
<ext:ResourceManager runat="server" />

<ext:GridPanel
ID="GridPanel1"
runat="server"
Title="Array Grid"
EnableDragDrop="true"
Width="600"
Height="350">
<Store>
<ext:Store ID="Store1" runat="server">
<Model>
<ext:Model runat="server">
<Fields>
<ext:ModelField Name="company" />
<ext:ModelField Name="price" Type="Float" />
<ext:ModelField Name="change" Type="Float" />
<ext:ModelField Name="pctChange" Type="Float" />
<ext:ModelField Name="lastChange" Type="Date" DateFormat="M/d hh:mmtt" />
</Fields>
</ext:Model>
</Model>
</ext:Store>
</Store>
<ColumnModel>
<Columns>
<ext:RowNumbererColumn />
<ext:Column Text="Company" DataIndex="company" Flex="1" />
<ext:Column Text="Price" DataIndex="price">
<Renderer Format="UsMoney" />
</ext:Column>
<ext:Column Text="Change" DataIndex="change" />
<ext:Column Text="Change" DataIndex="pctChange" />
<ext:DateColumn Text="Last Updated" DataIndex="lastChange" />
</Columns>
</ColumnModel>
<SelectionModel>
<ext:CheckboxSelectionModel CheckOnly="true" InjectCheckbox="1" />
</SelectionModel>
<View>
<ext:GridView EnableTextSelection="true">
<Plugins>
<ext:GridDragDrop DDGroup="GridDDGroup" />
</Plugins>
</ext:GridView>
</View>
</ext:GridPanel>
</body>
</html>


With the example above

You can select rows, and any selected rows will show a row handle in the row numberer column
You can select text from cells of any rows, including any selected (checked) rows [previously you could only select text for non-selected rows]
If you initiate a drag from the row numberer column, you will be able to drag that row (for this example you can re-order the rows on the grid)
If you attempt to initiate a drag from another cell of a selected row, then you won't be able to (it will select text in most cases). This is the trade-off of this solution.


I hope that is useful. I've tested this in latest Firefox, latest Chrome and latest IE11. I've not tested IE10 or below though I think it should work.

Also, if this can be done in a more simpler way, please do let me know!