The example shows how to create custom properties for the Web Dashboard.
Files to Review
Overview
Custom properties are stored in the CustomProperties collection in a structured format. Each custom property in this collection contains the custom property's metadata.
To apply custom property values to a dashboard, you need to create an extension. The extension is a JavaScript module that you can integrate into your application. Every extension that provides custom property can be divided into the following parts:
- Model.
The model is an object that contains the property name, type, and default value. It also specifies on which level the property is created (dashboard, dashboard item or data item container). Use the Model.registerCustomProperty property to register the custom property definition. - Viewer
In this part you modify the viewer part according to the saved custom property value. You can use the client methods and events to change the displayed elements. - Designer
This part contains designer settings. Add editors and control elements to configure and change the custom property's values in the UI. This part is not required if you use the extension in Viewer mode. - Event Subscription
This part contains event subscriptions.
Registration
To register an extension, attach the extension script before the control is rendered and call the registerExtension
method:
HTML<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>Dashboard Web Application</title>
<link href="css/site.min.css" rel="stylesheet" />
<script type="text/javascript">
function onBeforeRender(control) {
control.registerExtension(new DevExpress.Dashboard.DashboardPanelExtension(control));
control.registerExtension(new ChartScaleBreaksExtension(control));
control.registerExtension(new ChartLineOptionsExtension(control));
control.registerExtension(new ChartAxisMaxValueExtension(control));
control.registerExtension(new ChartConstantLinesExtension(control));
control.registerExtension(new ItemDescriptionExtension(control));
control.registerExtension(new DashboardDescriptionExtension(control));
control.registerExtension(new GridHeaderFilterExtension(control));
}
</script>
</head>
<body>
@RenderBody()
<script src="js/site.min.js"></script>
<script src="~/Content/Extensions/ChartConstantLinesExtension.js"></script>
<script src="~/Content/Extensions/ChartLineOptionsExtension.js"></script>
<script src="~/Content/Extensions/ChartScaleBreaksExtension.js"></script>
<script src="~/Content/Extensions/ItemDescriptionExtension.js"></script>
<script src="~/Content/Extensions/DashboardDescriptionExtension.js"></script>
<script src="~/Content/Extensions/ChartAxisMaxValueExtension.js"></script>
<script src="~/Content/Extensions/GridHeaderFilterExtension.js"></script>
</body>
</html>
Example structure
The following example contains a set of custom properties that demonstrate different capabilities. Below you find a detailed description for every extension.
ChartScaleBreaksExtension
This extension enables or disables scale breaks for the Chart dashboard item.
Overview:
- Adds a custom Boolean property for a specific dashboard item (Chart).
- Integrates a Scale breaks (Custom) section into the Options menu with the CheckBox widget as an editor.
ChartLineOptionsExtension
This extension changes the dash style of each series line in the Chart dashboard item.
Overview:
- Adds a string custom property for a specific data item container (Chart's series).
- Integrates a Line Options (Custom) section into the data item menu with the SelectBox widget as an editor.
DashboardDescriptionExtension
This extension enables you to set a dashboard's description in the dashboard menu. The dashboard description is displayed when you hover over the info button in the dashboard title.
Overview:
- Adds a custom string property for a dashboard.
- Shows how to add a new item to the ToolBox. In this example, a new item is added to the dashboard menu.
- Demonstrates how to create complex editors using templates. In this example, it is the widgets with the TextArea and Button widgets inside.
ItemDescriptionExtension
This extension enables you to set a description for each dashboard item. The dashboard item description is displayed when you hover over the info button in the item's caption.
Overview:
- Adds a custom string property for each dashboard item.
- Integrates a Description (Custom) section into the Options menu with the predefined buttonGroup template.
- Shows how to enable or disable editors depending on a custom property's value.
ChartAxisMaxValueExtension
This extension allows you to change the maximum value of the Y-axis in the Chart item.
Overview:
- Adds a set of custom properties with different types (number, boolean, and string) for a specific dashboard item (Chart).
- Demonstrates how to bind a custom property to a list of data items.
- Shows how to enable or disable editors depending on a custom property's value.
ChartConstantLinesExtension
This extension draws constant lines for the Chart dashboard item.
Overview:
- Adds a complex custom property for a specific dashboard item (Chart).
- Shows how to work with complex custom values that are saved as an array.
- Demonstrates how to bind a custom property to a list of data items.
- Customizes export to display the result in the exported document.
GridHeaderFilterExtension
This extension adds Header Filter buttons to the Grid dashboard item.
Overview:
- Adds a custom property for a specific dashboard item (Grid).
- Integrates a Header Filter (Custom) section, which contains the ButtonGroup widget as an editor, into the Options menu.
Documentation
More Examples
- ASP.NET Web Forms Dashboard Control - Custom Properties
- ASP.NET MVC Dashboard Control - Custom Properties
- Dashboard Component for Angular - Custom Properties
- WinForms Dashboard Designer - Custom Properties
- WPF Dashboard Viewer - Custom Properties
- Multiplatform Example - Constant Lines
Does this example address your development requirements/objectives?
(you will be redirected to DevExpress.com to submit your response)
Example Code
Razor<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>Dashboard Web Application</title>
<link href="css/site.min.css" rel="stylesheet" />
<script type="text/javascript">
function onBeforeRender(control) {
control.registerExtension(new DevExpress.Dashboard.DashboardPanelExtension(control));
control.registerExtension(new ChartScaleBreaksExtension(control));
control.registerExtension(new ChartLineOptionsExtension(control));
control.registerExtension(new ChartAxisMaxValueExtension(control));
control.registerExtension(new ChartConstantLinesExtension(control));
control.registerExtension(new ItemDescriptionExtension(control));
control.registerExtension(new DashboardDescriptionExtension(control));
control.registerExtension(new GridHeaderFilterExtension(control));
}
</script>
</head>
<body>
@RenderBody()
<script src="js/site.min.js"></script>
<script src="~/Content/Extensions/ChartConstantLinesExtension.js"></script>
<script src="~/Content/Extensions/ChartLineOptionsExtension.js"></script>
<script src="~/Content/Extensions/ChartScaleBreaksExtension.js"></script>
<script src="~/Content/Extensions/ItemDescriptionExtension.js"></script>
<script src="~/Content/Extensions/DashboardDescriptionExtension.js"></script>
<script src="~/Content/Extensions/ChartAxisMaxValueExtension.js"></script>
<script src="~/Content/Extensions/GridHeaderFilterExtension.js"></script>
</body>
</html>
Razor@page
<div style="position: absolute; left: 0; top: 0; right: 0; bottom: 0;">
@(Html.DevExpress().Dashboard("dashboardControl1")
.Width("100%")
.Height("100%")
.ControllerName("DefaultDashboard")
.WorkingMode(DevExpress.DashboardWeb.WorkingMode.Viewer)
.OnBeforeRender("onBeforeRender")
)
</div>
JavaScriptvar ChartScaleBreaksExtension = (function() {
var Model = DevExpress.Dashboard.Model;
// 1. Model
var autoScaleBreaksProperty = {
ownerType: Model.ChartItem,
propertyName: 'ScaleBreaks',
defaultValue: false,
valueType: 'boolean'
};
Model.registerCustomProperty(autoScaleBreaksProperty);
// 2. Viewer
function onItemWidgetOptionsPrepared(args) {
var scaleBreaks = args.dashboardItem.customProperties.getValue(autoScaleBreaksProperty.propertyName);
if (scaleBreaks) {
var valueAxisOptions = args.options["valueAxis"];
if (valueAxisOptions && valueAxisOptions[0]) {
valueAxisOptions[0].autoBreaksEnabled = true;
}
}
};
// 3. Designer
function onCustomizeSections(args) {
args.addSection({
title: "Scale breaks (Custom)",
items: [
{
dataField: autoScaleBreaksProperty.propertyName,
editorType: "dxCheckBox",
label: {
visible: false
},
editorOptions: {
text: "Enable Auto Scale breaks"
}
}
]
});
};
// 4. Event Subscription
function ChartScaleBreaksExtension(dashboardControl) {
this.name = "ScaleBreaks",
this.start = function () {
var viewerApiExtension = dashboardControl.findExtension('viewerApi');
if (viewerApiExtension) {
viewerApiExtension.on('itemWidgetOptionsPrepared', onItemWidgetOptionsPrepared);
}
var optionsPanelExtension = dashboardControl.findExtension("itemOptionsPanel");
if (optionsPanelExtension) {
optionsPanelExtension.on('customizeSections', onCustomizeSections);
}
},
this.stop = function () {
var viewerApiExtension = dashboardControl.findExtension('viewerApi');
if (viewerApiExtension) {
viewerApiExtension.off('itemWidgetOptionsPrepared', onItemWidgetOptionsPrepared);
}
var optionsPanelExtension = dashboardControl.findExtension("itemOptionsPanel");
if (optionsPanelExtension) {
optionsPanelExtension.off('customizeSections', onCustomizeSections);
}
}
}
return ChartScaleBreaksExtension;
}());
JavaScriptvar ChartLineOptionsExtension = (function() {
var Model = DevExpress.Dashboard.Model;
// 1. Model
const dashStyleProperty = {
ownerType: Model.SimpleSeries,
propertyName: "DashStyleProperty",
defaultValue: "solid",
valueType: 'string'
};
Model.registerCustomProperty(dashStyleProperty);
// 2. Viewer
function onItemWidgetOptionsPrepared(args) {
if (args.dashboardItem instanceof Model.ChartItem) {
var seriesOptionArray = args.options['series'] || [];
seriesOptionArray.forEach(function(seriesOption) {
if (seriesOption.type === "line") {
var series = args.chartContext.getDashboardItemSeries(seriesOption);
if (series) {
var dashStyle = series.customProperties.getValue(dashStyleProperty.propertyName);
seriesOption.dashStyle = dashStyle;
}
}
});
}
};
// 3. Designer
function onCustomizeSections(args) {
var simpleSeries = args.dataItemContainer;
if (simpleSeries instanceof Model.SimpleSeries
&& ['Line', 'FullStackedLine', 'StackedLine', 'StepLine', 'Spline'].indexOf(simpleSeries.seriesType()) !== -1
) {
args.addSection({
title: "Line Options (Custom)",
items: [
{
dataField: dashStyleProperty.propertyName,
editorType: "dxSelectBox",
label: {
text: 'Dash style'
},
editorOptions: {
items: [
{ value: 'dash', displayValue: "Dashes" },
{ value: 'dot', displayValue: "Dots" },
{ value: 'longDash', displayValue: "Long Dashes" },
{ value: 'solid', displayValue: "Solid Line" },
{ value: 'dashdot', displayValue: "Dash-Dots" }
],
displayExpr: "displayValue",
valueExpr: "value"
}
}
]
});
}
};
// 4. Event Subscription
function ChartLineOptionsExtension(dashboardControl) {
this.name = 'ChartLineOptions',
this.start = function () {
let viewerApiExtension = dashboardControl.findExtension('viewerApi');
if (viewerApiExtension) {
viewerApiExtension.on('itemWidgetOptionsPrepared', onItemWidgetOptionsPrepared);
}
let bindingPanelExtension = dashboardControl.findExtension("itemBindingPanel");
if (bindingPanelExtension) {
bindingPanelExtension.on('customizeDataItemContainerSections', onCustomizeSections);
}
},
this.stop = function () {
let viewerApiExtension = dashboardControl.findExtension('viewerApi');
if (viewerApiExtension) {
viewerApiExtension.off('itemWidgetOptionsPrepared', onItemWidgetOptionsPrepared);
}
let bindingPanelExtension = dashboardControl.findExtension("itemBindingPanel");
if (bindingPanelExtension) {
bindingPanelExtension.off('customizeDataItemContainerSections', onCustomizeSections);
}
}
}
return ChartLineOptionsExtension
}());
JavaScriptvar DashboardDescriptionExtension = (function () {
var Model = DevExpress.Dashboard.Model;
var Designer = DevExpress.Dashboard.Designer;
var svgIcon = '<svg version="1.1" id="iconDescription" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve"><style type="text/css">.dx-dashboard-icon {fill:currentColor;}</style><path class="dx-dashboard-icon" d="M12,3c-5,0-9,4-9,9s4,9,9,9s9-4,9-9S17,3,12,3z M12,19c-3.9,0-7-3.1-7-7s3.1-7,7-7s7,3.1,7,7S15.9,19,12,19z M12,17L12,17c-0.6,0-1-0.4-1-1v-5c0-0.6,0.4-1,1-1l0,0c0.6,0,1,0.4,1,1v5C13,16.6,12.6,17,12,17zM12,9L12,9c-0.6,0-1-0.4-1-1l0,0c0-0.6,0.4-1,1-1l0,0c0.6,0,1,0.4,1,1l0,0C13,8.6,12.6,9,12,9z"/></svg>';
DevExpress.Dashboard.ResourceManager.registerIcon(svgIcon);
// 1. Model
var dashboardDescriptionProperty = {
ownerType: Model.Dashboard,
propertyName: "DashboardDescription",
defaultValue: "",
valueType: 'string'
};
Model.registerCustomProperty(dashboardDescriptionProperty);
// 2. Viewer
function onDashboardTitleToolbarUpdated(args) {
var customProperties = args.dashboard.customProperties;
var description = customProperties.getValue(dashboardDescriptionProperty.propertyName);
if (description) {
args.options.actionItems.push({
type: "button",
icon: "iconDescription",
tooltip: description
});
}
};
// 3. Designer
function showPopup(dashboardControl) {
var div = document.createElement('div');
document.body.appendChild(div);
var dxForm = null
var popup = new DevExpress.ui.dxPopup(div, {
title: "Dashboard Description",
width: '400px',
height: '300px',
contentTemplate: function (contentElement) {
dxForm = new DevExpress.ui.dxForm(contentElement, {
labelLocation: "top",
formData: {
"description": dashboardControl.dashboard().customProperties.getValue(dashboardDescriptionProperty.propertyName)
},
items: [{
dataField: "description",
label: { visible: false },
editorType: "dxTextArea",
editorOptions: {
height: 150,
}
}]
})
},
onHidden: function (e) {
e.component.dispose();
document.body.removeChild(div);
},
toolbarItems: [{
widget: "dxButton",
toolbar: "bottom",
options: {
text: "Save",
onClick: function () {
var description = dxForm.option("formData")["description"];
dashboardControl.dashboard().customProperties.setValue(dashboardDescriptionProperty.propertyName, description);
var viewerApiExtension = dashboardControl.findExtension('viewerApi');
if (viewerApiExtension) {
viewerApiExtension.updateDashboardTitleToolbar();
}
popup.hide();
}
}
},
{
widget: "dxButton",
toolbar: "bottom",
options: {
text: "Close",
onClick: function () {
popup.hide();
}
}
}]
});
popup.show();
}
// 4. Event Subscription
function DashboardDescriptionExtension(dashboardControl) {
var menuItem = createMenuItem();
this.name = "DashboardDescription",
this.start = function () {
var viewerApiExtension = dashboardControl.findExtension('viewerApi');
if (viewerApiExtension) {
viewerApiExtension.on('dashboardTitleToolbarUpdated', onDashboardTitleToolbarUpdated);
}
var toolboxExtension = dashboardControl.findExtension("toolbox");
if (toolboxExtension) {
toolboxExtension.menuItems.push(menuItem);
}
},
this.stop = function () {
var viewerApiExtension = dashboardControl.findExtension('viewerApi');
if (viewerApiExtension) {
viewerApiExtension.off('dashboardTitleToolbarUpdated', onDashboardTitleToolbarUpdated);
}
var toolboxExtension = dashboardControl.findExtension("toolbox");
if (toolboxExtension) {
toolboxExtension.menuItems.remove(menuItem);
}
};
function createMenuItem() {
var menuItem = new Designer.DashboardMenuItem(this.name, "Description (Custom)", 9999, 68);
menuItem.disabled = ko.computed(function () { return !dashboardControl.dashboard() });
menuItem.click = function () { showPopup(dashboardControl) };
return menuItem;
}
}
return DashboardDescriptionExtension;
}());
JavaScriptvar ItemDescriptionExtension = (function () {
var Dashboard = DevExpress.Dashboard;
var Model = DevExpress.Dashboard.Model;
var Designer = DevExpress.Dashboard.Designer;
var svgIcon = '<svg version="1.1" id="iconDescription" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve"><style type="text/css">.dx-dashboard-icon {fill:currentColor;}</style><path class="dx-dashboard-icon" d="M12,3c-5,0-9,4-9,9s4,9,9,9s9-4,9-9S17,3,12,3z M12,19c-3.9,0-7-3.1-7-7s3.1-7,7-7s7,3.1,7,7S15.9,19,12,19z M12,17L12,17c-0.6,0-1-0.4-1-1v-5c0-0.6,0.4-1,1-1l0,0c0.6,0,1,0.4,1,1v5C13,16.6,12.6,17,12,17zM12,9L12,9c-0.6,0-1-0.4-1-1l0,0c0-0.6,0.4-1,1-1l0,0c0.6,0,1,0.4,1,1l0,0C13,8.6,12.6,9,12,9z"/></svg>';
Dashboard.ResourceManager.registerIcon(svgIcon);
// 1. Model
var enabledProperty = {
ownerType: Model.DashboardItem,
propertyName: "descriptionEnabled",
defaultValue: false,
valueType: 'boolean'
};
var textProperty = {
ownerType: Model.DashboardItem,
propertyName: "description",
defaultValue: "",
valueType: 'string'
};
var displayModeProperty = {
ownerType: Model.DashboardItem,
propertyName: "descriptionDisplayMode",
defaultValue: 'Always',
valueType: 'string'
};
Model.registerCustomProperty(enabledProperty);
Model.registerCustomProperty(displayModeProperty);
Model.registerCustomProperty(textProperty);
// 2. Viewer
function onItemCaptionToolbarUpdated(args) {
var descriptionVisible = args.dashboardItem.customProperties.getValue(enabledProperty.propertyName);
if (descriptionVisible) {
var description = args.dashboardItem.customProperties.getValue(textProperty.propertyName);
var displayMode = args.dashboardItem.customProperties.getValue(displayModeProperty.propertyName);
var array = displayMode === 'OnHover' ? args.options.actionItems : args.options.stateItems;
array.push({
type: "button",
icon: "iconDescription",
tooltip: !!description ? description.toString() : undefined
});
}
};
// 3. Designer
function isDescriptionDisabled(dashboardItem) {
return !dashboardItem.customProperties.getValue(enabledProperty.propertyName);
}
function changeDisabledState(dxForm, fieldName, isDisabled) {
let itemOptions = dxForm.itemOption(fieldName)
if(itemOptions) {
let editorOptions = itemOptions.editorOptions || {}
editorOptions.disabled = isDisabled
dxForm.itemOption(fieldName, "editorOptions", editorOptions)
}
}
function onCustomizeSections(args) {
args.addSection({
title: "Description (Custom)",
onFieldDataChanged: function (e) {
e.component.beginUpdate();
changeDisabledState(e.component, textProperty.propertyName, isDescriptionDisabled(args.dashboardItem));
changeDisabledState(e.component, displayModeProperty.propertyName, isDescriptionDisabled(args.dashboardItem));
e.component.endUpdate();
},
items: [
{
dataField: enabledProperty.propertyName,
label: {
text: "Visible"
},
template: Designer.FormItemTemplates.buttonGroup,
editorOptions: {
keyExpr: "value",
items: [{
value: true,
text: "Visible"
}, {
value: false,
text: "Hidden"
}]
}
},
{
dataField: displayModeProperty.propertyName,
label: {
text: "Display Mode"
},
template: Designer.FormItemTemplates.buttonGroup,
editorOptions: {
keyExpr: "value",
items: [{
value: "OnHover",
text: "On hover"
}, {
value: "Always",
text: "Always"
}],
disabled: isDescriptionDisabled(args.dashboardItem)
}
},
{
dataField: textProperty.propertyName,
editorType: "dxTextArea",
label: {
text: "Description"
},
editorOptions: {
height: 90,
disabled: isDescriptionDisabled(args.dashboardItem)
}
}
]
});
};
// 4. Event Subscription
function ItemDescriptionExtension(dashboardControl) {
this.name = "ItemDescription";
this.start = function () {
var viewerApiExtension = dashboardControl.findExtension('viewerApi');
if (viewerApiExtension) {
viewerApiExtension.on('itemCaptionToolbarUpdated', onItemCaptionToolbarUpdated);
}
var optionsPanelExtension = dashboardControl.findExtension("itemOptionsPanel");
if (optionsPanelExtension) {
optionsPanelExtension.on('customizeSections', onCustomizeSections);
}
};
this.stop = function () {
var viewerApiExtension = dashboardControl.findExtension('viewerApi');
if (viewerApiExtension) {
viewerApiExtension.off('itemCaptionToolbarUpdated', onItemCaptionToolbarUpdated);
}
var optionsPanelExtension = dashboardControl.findExtension("itemOptionsPanel");
if (optionsPanelExtension) {
optionsPanelExtension.off('customizeSections', onCustomizeSections);
}
};
}
return ItemDescriptionExtension;
}());
JavaScriptvar ChartAxisMaxValueExtension = (function () {
var Model = DevExpress.Dashboard.Model;
var Designer = DevExpress.Dashboard.Designer;
// 1. Model
const axisMaxValueEnabledProperty = {
ownerType: Model.ChartItem,
propertyName: "AxisMaxValueEnabled",
defaultValue: false,
valueType: 'boolean'
};
const axisMaxValueIsBoundProperty = {
ownerType: Model.ChartItem,
propertyName: "AxisMaxValueIsBound",
defaultValue: false,
valueType: 'boolean'
};
const axisMaxValueConstantProperty = {
ownerType: Model.ChartItem,
propertyName: "AxisMaxValueConstant",
defaultValue: 100,
valueType: 'number'
};
const axisMaxValueDataItemProperty = {
ownerType: Model.ChartItem,
propertyName: "AxisMaxValueDataItem",
defaultValue: '',
valueType: 'string'
};
Model.registerCustomProperty(axisMaxValueEnabledProperty);
Model.registerCustomProperty(axisMaxValueIsBoundProperty);
Model.registerCustomProperty(axisMaxValueConstantProperty);
Model.registerCustomProperty(axisMaxValueDataItemProperty);
// 2. Viewer
function onItemWidgetOptionsPrepared(args) {
if (args.dashboardItem.customProperties.getValue(axisMaxValueEnabledProperty.propertyName)) {
var value = args.dashboardItem.customProperties.getValue(axisMaxValueConstantProperty.propertyName)
if (args.dashboardItem.customProperties.getValue(axisMaxValueIsBoundProperty.propertyName)) {
var measureId = args.dashboardItem.customProperties.getValue(axisMaxValueDataItemProperty.propertyName)
value = args.itemData.getMeasureValue(measureId).getValue()
}
args.options.valueAxis[0].visualRange = [null, value]
}
}
// 3. Designer
function isAxisMaxValueDisabled(dashboardItem) {
return !dashboardItem.customProperties.getValue(axisMaxValueEnabledProperty.propertyName);
}
function isBoundMode(dashboardItem) {
return dashboardItem.customProperties.getValue(axisMaxValueIsBoundProperty.propertyName);
}
function changeDisabledState(dxForm, fieldName, isDisabled) {
let itemOptions = dxForm.itemOption(fieldName)
if (itemOptions) {
let editorOptions = itemOptions.editorOptions || {}
editorOptions.disabled = isDisabled
dxForm.itemOption(fieldName, "editorOptions", editorOptions)
}
}
function changeVisibleState(dxForm, fieldName, isVisible) {
let itemOptions = dxForm.itemOption(fieldName, "visible", isVisible)
}
function updateFormState(dxForm, dashboardItem) {
dxForm.beginUpdate();
changeDisabledState(dxForm, axisMaxValueIsBoundProperty.propertyName, isAxisMaxValueDisabled(dashboardItem))
changeDisabledState(dxForm, axisMaxValueConstantProperty.propertyName, isAxisMaxValueDisabled(dashboardItem))
changeDisabledState(dxForm, axisMaxValueDataItemProperty.propertyName, isAxisMaxValueDisabled(dashboardItem))
changeVisibleState(dxForm, axisMaxValueConstantProperty.propertyName, !isBoundMode(dashboardItem))
changeVisibleState(dxForm, axisMaxValueDataItemProperty.propertyName, isBoundMode(dashboardItem))
dxForm.endUpdate();
}
function onCustomizeSections(args) {
if (args.dashboardItem instanceof Model.ChartItem) {
args.addSection({
title: "Primary Axis Max Value (Custom)",
onFieldDataChanged: function (e) {
updateFormState(e.component, args.dashboardItem);
},
onInitialized: function (e) {
updateFormState(e.component, args.dashboardItem);
},
items: [
{
dataField: axisMaxValueEnabledProperty.propertyName,
editorType: "dxCheckBox",
label: { visible: false },
editorOptions: {
text: "Enabled",
}
},
{
dataField: axisMaxValueIsBoundProperty.propertyName,
label: {
text: "Mode"
},
template: Designer.FormItemTemplates.buttonGroup,
editorOptions: {
keyExpr: "value",
items: [{
value: true,
text: "Bound"
}, {
value: false,
text: "Value"
}]
}
},
{
dataField: axisMaxValueConstantProperty.propertyName,
editorType: "dxTextBox",
label: {
text: "Value",
}
},
{
dataField: axisMaxValueDataItemProperty.propertyName,
editorType: "dxSelectBox",
label: {
text: "DataItem",
},
editorOptions: {
displayExpr: "text",
valueExpr: "value",
items: args.dashboardItem.hiddenMeasures().map(function (measure) {
return { text: measure.name() || measure.dataMember(), value: measure.uniqueName() }
}),
}
}
]
})
}
}
// 4. Event Subscription
function ChartAxisMaxValueExtension(dashboardControl) {
this.name = "ChartAxisMaxValueExtension";
this.start = function () {
let viewerApiExtension = dashboardControl.findExtension('viewerApi');
if (viewerApiExtension) {
viewerApiExtension.on('itemWidgetOptionsPrepared', onItemWidgetOptionsPrepared);
}
let bindingPanelExtension = dashboardControl.findExtension("itemOptionsPanel");
if (bindingPanelExtension) {
bindingPanelExtension.on('customizeSections', onCustomizeSections);
}
};
this.stop = function () {
let viewerApiExtension = dashboardControl.findExtension('viewerApi');
if (viewerApiExtension) {
viewerApiExtension.off('itemWidgetOptionsPrepared', onItemWidgetOptionsPrepared);
}
let bindingPanelExtension = dashboardControl.findExtension("itemOptionsPanel");
if (bindingPanelExtension) {
bindingPanelExtension.off('customizeSections', onCustomizeSections);
}
}
}
return ChartAxisMaxValueExtension;
}());
JavaScriptvar ChartConstantLinesExtension = (function() {
var Model = DevExpress.Dashboard.Model;
var dxButton = DevExpress.ui.dxButton;
var dxPopup = DevExpress.ui.dxPopup;
var dxForm = DevExpress.ui.dxForm;
var dxList = DevExpress.ui.dxList;
var dxToolbar = DevExpress.ui.dxToolbar;
var DataSource = DevExpress.data.DataSource;
// 1. Model
var chartConstantLinesProperty = {
ownerType: Model.ChartItem,
propertyName: 'ConstantLineSettings',
defaultValue: "[]",
valueType: 'string'
};
Model.registerCustomProperty(chartConstantLinesProperty);
// 2. Viewer
function onItemWidgetOptionsPrepared(args) {
if(args.dashboardItem instanceof Model.ChartItem) {
var serializedConstantLines = args.dashboardItem.customProperties.getValue(chartConstantLinesProperty.propertyName);
var constantLines = JSON.parse(serializedConstantLines);
var valueAxisOptions = args.options["valueAxis"] || [];
constantLines.forEach(function(line) {
var axisOptions = valueAxisOptions[0];
if(axisOptions) {
var value = line.value
if(line.isBound) {
value = args.itemData.getMeasureValue(line.measureId).getValue()
}
axisOptions.constantLines = axisOptions.constantLines || [];
axisOptions.constantLines.push({
value: value,
color: line.color,
dashStyle: 'longDash',
width: 2,
label: {
text: line.labelText
}
});
}
});
}
}
// 3. Designer
function onCustomizeSections(args) {
var chartItem = args.dashboardItem;
if(chartItem instanceof Model.ChartItem) {
args.addSection({
title: "Constant Lines (custom)",
items: [
{
dataField: chartConstantLinesProperty.propertyName,
template: function(args, element) {
var buttonContainer = document.createElement('div');
new dxButton(buttonContainer, {
text: 'Edit',
onClick: function() {
showPopup(chartItem)
}
})
return buttonContainer;
},
label: {
visible: false,
}
}
]
});
}
}
function showPopup(chartItem) {
var popupContainer = document.createElement('div');
document.body.appendChild(popupContainer);
var popupOptions = {
width : '800px',
height : 'auto',
closeOnOutsideClick: false,
contentTemplate: function(contentContainer) {
var formContainer = document.createElement('div');
var formOptions = getFormOptions(chartItem);
this._form = new dxForm(formContainer, formOptions);
return formContainer;
},
onHidden: function() {
document.body.removeChild(popupContainer)
},
title: 'Constant Lines',
};
var popup = new dxPopup(popupContainer, popupOptions);
popup.show();
}
function getValue(chartItem) {
return JSON.parse(chartItem.customProperties.getValue(chartConstantLinesProperty.propertyName))
}
function setValue(chartItem, value) {
return chartItem.customProperties.setValue(chartConstantLinesProperty.propertyName, JSON.stringify(value))
}
function createListAndToolbar(form, chartItem) {
var element = document.createElement('div');
var toolbarContainer = document.createElement('div');
element.appendChild(toolbarContainer);
var editButton = null;
var removeButton = null;
var list = null;
var toolbarOptions = {
items: [{
location: 'before',
widget: 'dxButton',
options: {
icon: 'add',
stylingMode: 'text',
onClick: function (e) {
var constantLines = getValue(chartItem);
var key = constantLines.reduce(function(acc, item) { return acc < item.key ? item.key : acc }, 0) + 1
var newConstLine = {
key: key,
name: 'Constant Line' + key,
isBound: false,
measureId: '',
value: 0,
color: '#000000',
labelText: ''
};
form.option('formData', newConstLine);
var itemInDataSource = constantLines.filter(function(item) { return item.key === newConstLine.key} )[0];
if(!itemInDataSource) {
constantLines.push(newConstLine);
} else {
var index = constantLines.indexOf(itemInDataSource);
constantLines[index] = newConstLine;
}
setValue(chartItem, constantLines);
list.reload();
list.option('selectedItem', constantLines[constantLines.length - 1]);
},
}
},
{
location: 'before',
widget: 'dxButton',
options: {
icon: 'remove',
stylingMode: 'text',
onInitialized: function (e) { removeButton = e.component },
onClick: function() {
var constantLines = getValue(chartItem);
var selectedKey = list.option('selectedItem').key;
var index = constantLines.indexOf(constantLines.filter(function(line) { return line.key === selectedKey })[0]);
if(index >= 0) {
constantLines.splice(index, 1);
setValue(chartItem, constantLines);
list.reload();
list.option('selectedItem', constantLines[constantLines.length - 1]);
}
},
}
}]
};
var updateToolbarState = function (hasSelectedItem) {
editButton && editButton.option('disabled', !hasSelectedItem);
removeButton && removeButton.option('disabled', !hasSelectedItem);
}
var toolbar = new dxToolbar(toolbarContainer, toolbarOptions);
var listOptions = {
dataSource: new DataSource({ load: function() { return getValue(chartItem)} }),
displayExpr: 'name',
height: '200px',
keyExpr: 'key',
noDataText: 'Add a Constant Line',
selectionMode: 'single',
onContentReady: function (e) { updateToolbarState(!!e.component.option('selectedItem')) },
onSelectionChanged: function (e) {
updateToolbarState(!!e.component.option('selectedItem'));
form.option('formData', e.component.option('selectedItem'));
}
};
var listContainer = document.createElement('div');
element.appendChild(listContainer);
list = new dxList(listContainer, listOptions);
return element;
}
function getFormOptions(chartItem) {
var updateFormState = function (form) {
var isBound = form.option('formData')['isBound'];
var valueEditor = form.getEditor('value');
valueEditor && valueEditor.option('disabled', isBound);
var measureIdEditor = form.getEditor('measureId');
measureIdEditor && measureIdEditor.option('disabled', !isBound);
};
return {
formData: getValue(chartItem)[0] || null,
colCount: 2,
items: [
{
itemType: 'group',
template : function (args, element) { return createListAndToolbar(args.component, chartItem) },
},
{
itemType: 'group',
items : [
{
dataField: 'name',
editorType: 'dxTextBox',
},
{
dataField: 'isBound',
editorType: "dxCheckBox",
label: {
text: 'IsBound',
},
},
{
dataField: 'value',
editorType: 'dxNumberBox',
label: {
text: 'Value',
},
editorOptions: {
showSpinButtons: true,
}
},
{
dataField: 'measureId',
editorType: 'dxSelectBox',
label: {
text: 'Hidden measures',
},
editorOptions: {
displayExpr: 'text',
valueExpr: 'value',
items: chartItem.hiddenMeasures().map(function(measure) {
return {
text: measure.name() || measure.dataMember(),
value: measure.uniqueName()
}
}),
}
},
{
dataField: 'color',
editorType: 'dxColorBox',
label: {
text: 'Color',
}
},
{
dataField: 'labelText',
editorType: 'dxTextBox',
}
]
}
],
onContentReady: function(e) { updateFormState(e.component) },
onFieldDataChanged: function(e) {
var formData = e.component.option("formData");
var constantLines = getValue(chartItem);
var editedConstantLine = constantLines.filter(function(line) { return line.key === formData.key })[0];
constantLines[constantLines.indexOf(editedConstantLine)] = formData;
setValue(chartItem, constantLines);
updateFormState(e.component)
},
};
}
// 4. Event Subscription
function ChartConstantLinesExtension(dashboardControl) {
this.name = 'ChartConstantLines';
this.start = function() {
var viewerApiExtension = dashboardControl.findExtension('viewerApi');
if(viewerApiExtension) {
viewerApiExtension.on('itemWidgetOptionsPrepared', onItemWidgetOptionsPrepared)
}
var optionsPanelExtension = dashboardControl.findExtension("itemOptionsPanel")
if(optionsPanelExtension) {
optionsPanelExtension.on('customizeSections', onCustomizeSections)
}
}
this.stop = function (){
var viewerApiExtension = dashboardControl.findExtension('viewerApi');
if(viewerApiExtension) {
viewerApiExtension.off('itemWidgetOptionsPrepared', onItemWidgetOptionsPrepared)
}
var optionsPanelExtension = dashboardControl.findExtension("itemOptionsPanel")
if(optionsPanelExtension) {
optionsPanelExtension.off('customizeSections', onCustomizeSections)
}
}
}
return ChartConstantLinesExtension;
}());
JavaScriptvar GridHeaderFilterExtension = (function () {
var Model = DevExpress.Dashboard.Model;
var Designer = DevExpress.Dashboard.Designer;
// 1. Model
var enabledHeaderFilterProperty = {
ownerType: Model.GridItem,
propertyName: 'headerFilterEnabled',
defaultValue: false,
valueType: 'boolean'
};
Model.registerCustomProperty(enabledHeaderFilterProperty);
// 2. Viewer
function onItemWidgetOptionsPrepared(args) {
if (args.dashboardItem instanceof Model.GridItem) {
args.options['headerFilter'] = {
visible: args.dashboardItem.customProperties.getValue(enabledHeaderFilterProperty.propertyName)
};
}
};
// 3. Designer
function onCustomizeSections(args) {
args.addSection({
title: 'Header Filter (Custom)',
items: [{
dataField: enabledHeaderFilterProperty.propertyName,
label: {
text: 'Header Filter'
},
template: Designer.FormItemTemplates.buttonGroup,
editorOptions: {
keyExpr: 'value',
items: [{
value: true,
text: 'On'
}, {
value: false,
text: 'Off'
}]
}
}]
});
};
// 4. Event Subscription
function GridHeaderFilterExtension(dashboardControl) {
this.name = "GridHeaderFilter",
this.start = function () {
var viewerApiExtension = dashboardControl.findExtension('viewerApi');
if (viewerApiExtension) {
viewerApiExtension.on('itemWidgetOptionsPrepared', onItemWidgetOptionsPrepared);
}
var optionsPanelExtension = dashboardControl.findExtension("itemOptionsPanel");
if (optionsPanelExtension) {
optionsPanelExtension.on('customizeSections', onCustomizeSections);
}
},
this.stop = function () {
var viewerApiExtension = dashboardControl.findExtension('viewerApi');
if (viewerApiExtension) {
viewerApiExtension.off('itemWidgetOptionsPrepared', onItemWidgetOptionsPrepared);
}
var optionsPanelExtension = dashboardControl.findExtension("itemOptionsPanel");
if (optionsPanelExtension) {
optionsPanelExtension.off('customizeSections', onCustomizeSections);
}
}
}
return GridHeaderFilterExtension;
}());