Feel free to share demo-related thoughts here.
If you have technical questions, please create a support ticket in the DevExpress Support Center.
Thank you for the feedback!
If you have technical questions, please create a support ticket in the DevExpress Support Center.
Backend API
@model IEnumerable<DevExtreme.MVC.Demos.Models.OrgItemPlain>
@(Html.DevExtreme().Diagram()
.ID("diagram")
.OnRequestLayoutUpdate("onRequestLayoutUpdate")
.CustomShapes(cs => {
cs.Add()
.Type("employee")
.Category("employee")
.BaseType("rectangle")
.Title("New Employee")
.DefaultWidth(1.5)
.DefaultHeight(1)
.ToolboxWidthToHeightRatio(2)
.MinWidth(1.5)
.MinHeight(1)
.MaxWidth(3)
.MaxHeight(2)
.AllowEditText(false);
})
.CustomShapeTemplate(@<text>
<svg class="template">
<text class="template-name" x="50%" y="20%"><%- dataItem ? dataItem.FullName : "Employee's Name" %></text>
<text class="template-title" x="50%" y="45%"><%- dataItem ? dataItem.Title : "Employee's Title" %></text>
<text class="template-button" x="40%" y="85%" onclick="editEmployee(<%- dataItem && JSON.stringify(dataItem) %>)">Edit</text>
<text class="template-button" x="62%" y="85%" onclick="deleteEmployee(<%- dataItem && JSON.stringify(dataItem) %>)">Delete</text>
</svg>
</text>)
.CustomShapeToolboxTemplate(@<text>
<svg class="template">
<text x="50%" y="40%">New</text>
<text x="50%" y="70%">Employee</text>
</svg>
</text>)
.Nodes(ns => ns
.DataSource(d => d
.Array()
.Key("ID")
.Data(Model)
.OnInserting("onNodeInserting")
)
.KeyExpr("ID")
.TypeExpr(new JS("itemTypeExpr"))
.CustomDataExpr(new JS("itemCustomDataExpr"))
.ParentKeyExpr("HeadID")
.AutoLayout(al => al
.Type(DiagramDataLayoutType.Tree)
)
)
.ContextToolbox(ct => ct
.ShapeIconsPerRow(1)
.Width(100)
)
.Toolbox(tb => tb
.Groups(g => {
g.Add().Category("employee").Title("Employee").Expanded(true);
})
.ShowSearch(false)
.ShapeIconsPerRow(1)
)
.PropertiesPanel(pp => pp
.Tabs(t => {
t.Add()
.Groups(g => {
g.Add()
.Title("Page Properties")
.Commands(new[] { DiagramCommand.PageSize, DiagramCommand.PageOrientation, DiagramCommand.PageColor });
});
})
)
)
@(Html.DevExtreme().Popup()
.ID("popup")
.Width(400)
.Height(480)
.ShowTitle(true)
.Title("Edit Employee")
.Visible(false)
.DragEnabled(false)
.ContentTemplate(new TemplateName("popup-content"))
)
@using(Html.DevExtreme().NamedTemplate("popup-content")) {
<div class="dx-fieldset">
<div class="dx-field">
<div class="dx-field-label">Name</div>
<div class="dx-field-value" data-field="FullName">
@(Html.DevExtreme().TextBox().InputAttr("aria-label", "Name"))
</div>
</div>
<div class="dx-field">
<div class="dx-field-label">Title</div>
<div class="dx-field-value" data-field="Title">
@(Html.DevExtreme().TextBox().InputAttr("aria-label", "Title"))
</div>
</div>
<div class="dx-field">
<div class="dx-field-label">City</div>
<div class="dx-field-value" data-field="City">
@(Html.DevExtreme().TextBox().InputAttr("aria-label", "City"))
</div>
</div>
<div class="dx-field">
<div class="dx-field-label">State</div>
<div class="dx-field-value" data-field="State">
@(Html.DevExtreme().TextBox().InputAttr("aria-label", "State"))
</div>
</div>
<div class="dx-field">
<div class="dx-field-label">Email</div>
<div class="dx-field-value" data-field="Email">
@(Html.DevExtreme().TextBox().InputAttr("aria-label", "Email"))
</div>
</div>
<div class="dx-field">
<div class="dx-field-label">Skype</div>
<div class="dx-field-value" data-field="Skype">
@(Html.DevExtreme().TextBox().InputAttr("aria-label", "Skype"))
</div>
</div>
<div class="dx-field">
<div class="dx-field-label">Phone</div>
<div class="dx-field-value" data-field="MobilePhone">
@(Html.DevExtreme().TextBox().InputAttr("aria-label", "Phone"))
</div>
</div>
</div>
<div class="dx-fieldset buttons">
@(Html.DevExtreme().Button()
.Text("Update")
.Type(ButtonType.Default)
.OnClick("updateEmployee")
)
@(Html.DevExtreme().Button()
.Text("Cancel")
.OnClick("cancelEditEmployee")
)
</div>
}
<script type="text/javascript">
var currentEmployee = {};
var generatedID = 100;
function onNodeInserting(values) {
values.ID = values.ID || generatedID++;
values.FullName = values.FullName || "Employee's Name";
values.Title = values.Title || "Employee's Title";
}
function itemTypeExpr(obj) {
return "employee";
}
function itemCustomDataExpr(obj, value) {
if(value === undefined) {
return {
"FullName": obj.FullName,
"Prefix": obj.Prefix,
"Title": obj.Title,
"City": obj.City,
"State": obj.State,
"Email": obj.Email,
"Skype": obj.Skype,
"MobilePhone": obj.MobilePhone
};
} else {
obj.FullName = value.FullName;
obj.Prefix = value.Prefix;
obj.Title = value.Title;
obj.City = value.City;
obj.State = value.State;
obj.Email = value.Email;
obj.Skype = value.Skype;
obj.MobilePhone = value.MobilePhone;
}
}
function onRequestLayoutUpdate(e) {
for(var i = 0; i < e.changes.length; i++) {
if(e.changes[i].type === 'remove')
e.allowed = true;
else if(e.changes[i].data.HeadID !== undefined && e.changes[i].data.HeadID !== null)
e.allowed = true;
}
}
function editEmployee(employee) {
currentEmployee = Object.assign({}, employee);
var popup = $("#popup").dxPopup("instance");
popup.show();
popup.content().find(".dx-field-value").each(function() {
var field = $(this).attr("data-field");
var edit = $(this).children().dxTextBox("instance");
edit.option({
value: currentEmployee[field],
onValueChanged: function(e) { handleChange(field, e.value); }
});
});
};
function deleteEmployee(employee) {
var diagram = $("#diagram").dxDiagram("instance");
diagram.getNodeDataSource().store().push([{ type: 'remove', key: employee.ID }]);
};
function updateEmployee() {
var diagram = $("#diagram").dxDiagram("instance");
diagram.getNodeDataSource().store().push([{
type: 'update',
key: currentEmployee.ID,
data: {
"FullName": currentEmployee.FullName,
"Title": currentEmployee.Title,
"City": currentEmployee.City,
"State": currentEmployee.State,
"Email": currentEmployee.Email,
"Skype": currentEmployee.Skype,
"MobilePhone": currentEmployee.MobilePhone
}
}]);
var popup = $("#popup").dxPopup("instance");
popup.hide();
};
function cancelEditEmployee() {
currentEmployee = {};
var popup = $("#popup").dxPopup("instance");
popup.hide();
}
function handleChange(field, value) {
currentEmployee[field] = value;
}
</script>
using System.Web.Mvc;
using DevExtreme.MVC.Demos.Models.SampleData;
namespace DevExtreme.MVC.Demos.Controllers {
public class DiagramController : Controller {
public ActionResult CustomShapesWithTemplatesWithEditing() {
return View(SampleData.OrgItemsPlain);
}
}
}
#diagram {
height: 725px;
}
#diagram .template .template-name {
font-weight: bold;
text-decoration: underline;
}
#diagram .template .template-title {
font-style: italic;
}
#diagram .template .template-button {
cursor: pointer;
font-size: 8pt;
fill: navy;
}
#diagram .template .template-button:hover {
text-decoration: underline;
}
.dx-popup-content {
padding: 0;
}
.dx-popup-content .dx-fieldset.buttons {
display: flex;
justify-content: flex-end;
}
.dx-popup-content .dx-fieldset.buttons > * {
margin-left: 8px;
}
The customDataExpr property links custom employee information from the data source to diagram nodes. Changes made to data are reflected in the diagram's history. Undo and redo actions (available within the control's UI) allow users to rollback/reapply changes.
The CustomShapeToolboxTemplate property specifies the template used for a shape within the toolbox.