For many applications, the out-of-the-box Reporting UI may be too complex and require too much training. It is helpful to tailor the UI to the user's level of expertise to enhance the user experience and streamline workflows.

Live Demo

Test this code live in the Drag and Drop Fields sample that is part of the Technical Demos.

Launch Demo

Drag and Drop

A very common technical support question is whether it is possible to drag and drop merge fields from a sidebar or tree view. The JavaScript API provides full access to the functionality, including adding merge fields, that allows you to implement such a sidebar.

Drag and drop merge fields

For this example, we will use the following simple data object:

public class SimpleMergeData {
public string firstname { get; set; }
public string name { get; set; }
public string company { get; set; }
public string street { get; set; }
public string zip { get; set; }
}
view raw test.cs hosted with ❤ by GitHub

In the Razor view code, a list is created based on the given data object:

<ul class="list-group">
@{
Type type = typeof(SimpleMergeData);
PropertyInfo[] properties = type.GetProperties();
foreach (PropertyInfo property in properties) {
<li class="drag list-group-item">@property.Name</li>
}
}
</ul>
view raw test.cshtml hosted with ❤ by GitHub

Event Handler

When the page loads, the following JavaScript code adds the draggable attribute to the elements and attaches an event handler:

document.querySelectorAll(".drag")
.forEach((function (x) {
x.setAttribute("draggable", true);
x.addEventListener("dragstart", function (event) { drag(event); });
}))
view raw test.js hosted with ❤ by GitHub

This results in the following HTML:

<ul class="list-group">
<li class="drag list-group-item" draggable="true">firstname</li>
<li class="drag list-group-item" draggable="true">name</li>
<li class="drag list-group-item" draggable="true">company</li>
<li class="drag list-group-item" draggable="true">street</li>
<li class="drag list-group-item" draggable="true">zip</li>
</ul>
view raw test.html hosted with ❤ by GitHub

Transfer Data

The attached event handler drag sets the data transfer data for the drop event in the Text Control editor.

function drag(ev) {
ev.dataTransfer.setData("fieldname", ev.target.innerText);
}
view raw test.js hosted with ❤ by GitHub

Input Position

While dragging over the editor, the input position is actively changed, so that the field can be added at the new input position when the drop event is fired.

document.getElementById("mainCanvas").addEventListener("dragover", function (event) {
event.preventDefault();
event.stopPropagation();
var posX = (event.offsetX + _txtViewLoc.x) * 15;
var posY = (event.offsetY + _txtViewLoc.y) * 15;
TXTextControl.setInputPositionByLocation({ x: posX, y: posY })
TXTextControl.focus();
});
view raw test.js hosted with ❤ by GitHub

The variable _txtViewLoc is updated in the textViewLocationChanged event to provide the scroll offset position to calculate the correct input position.

var _txtViewLoc = { x: 0, y: 0 };
function textViewLocationChangedHandler(e) {
_txtViewLoc = e.location;
}
function window_load() {
TXTextControl.addEventListener("textControlLoaded", function () {
TXTextControl.addEventListener("textViewLocationChanged", textViewLocationChangedHandler);
});
}
window.addEventListener("load", window_load);
view raw test.js hosted with ❤ by GitHub

New Merge Field

When the field is finally dropped, a new merge field is created with the name of the data transfer data and is inserted at the current input position.

document.getElementById("mainCanvas").addEventListener("drop", function (event) {
event.preventDefault();
event.stopPropagation();
var fieldName = event.dataTransfer.getData("fieldname");
var mergeField = new TXTextControl.MergeField;
mergeField.name = fieldName;
mergeField.text = "«" + fieldName + "»";
TXTextControl.addMergeField(mergeField);
});
view raw test.js hosted with ❤ by GitHub

The complete JavaScript is listed below.

var _txtViewLoc = { x: 0, y: 0 };
function textViewLocationChangedHandler(e) {
_txtViewLoc = e.location;
}
function window_load() {
TXTextControl.addEventListener("textControlLoaded", function () {
TXTextControl.addEventListener("textViewLocationChanged", textViewLocationChangedHandler);
});
}
window.addEventListener("load", window_load);
document.querySelectorAll(".drag")
.forEach((function (x) {
x.setAttribute("draggable", true);
x.addEventListener("dragstart", function (event) { drag(event); });
}))
TXTextControl.addEventListener("ribbonTabsLoaded", function () {
TXTextControl.ribbon.selectedTab = "tabReports";
document.getElementById("mainCanvas").addEventListener("drop", function (event) {
event.preventDefault();
event.stopPropagation();
var fieldName = event.dataTransfer.getData("fieldname");
var mergeField = new TXTextControl.MergeField;
mergeField.name = fieldName;
mergeField.text = "«" + fieldName + "»";
TXTextControl.addMergeField(mergeField);
});
document.getElementById("mainCanvas").addEventListener("dragover", function (event) {
event.preventDefault();
event.stopPropagation();
var posX = (event.offsetX + _txtViewLoc.x) * 15;
var posY = (event.offsetY + _txtViewLoc.y) * 15;
TXTextControl.setInputPositionByLocation({ x: posX, y: posY })
TXTextControl.focus();
});
})
function drag(ev) {
ev.dataTransfer.setData("fieldname", ev.target.innerText);
}
view raw test.js hosted with ❤ by GitHub