Example T885558
Visible to All Users

BI Dashboard for ASP.NET Core - Custom Properties

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:

  1. 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.
  2. 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.
  3. 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.
  4. 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

View Extension

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

View Extension

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

View Extension

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

View Extension

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

View Extension

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

View Extension

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

View Extension

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

Does this example address your development requirements/objectives?

(you will be redirected to DevExpress.com to submit your response)

Example Code

AspNetCoreDashboard/Pages/_Layout.cshtml
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>
AspNetCoreDashboard/Pages/Index.cshtml
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>
AspNetCoreDashboard/wwwroot/Content/Extensions/ChartScaleBreaksExtension.js
JavaScript
var 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; }());
AspNetCoreDashboard/wwwroot/Content/Extensions/ChartLineOptionsExtension.js
JavaScript
var 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 }());
AspNetCoreDashboard/wwwroot/Content/Extensions/DashboardDescriptionExtension.js
JavaScript
var 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; }());
AspNetCoreDashboard/wwwroot/Content/Extensions/ItemDescriptionExtension.js
JavaScript
var 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; }());
AspNetCoreDashboard/wwwroot/Content/Extensions/ChartAxisMaxValueExtension.js
JavaScript
var 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; }());
AspNetCoreDashboard/wwwroot/Content/Extensions/ChartConstantLinesExtension.js
JavaScript
var 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; }());
AspNetCoreDashboard/wwwroot/Content/Extensions/GridHeaderFilterExtension.js
JavaScript
var 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; }());

Disclaimer: The information provided on DevExpress.com and affiliated web properties (including the DevExpress Support Center) is provided "as is" without warranty of any kind. Developer Express Inc disclaims all warranties, either express or implied, including the warranties of merchantability and fitness for a particular purpose. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.

Confidential Information: Developer Express Inc does not wish to receive, will not act to procure, nor will it solicit, confidential or proprietary materials and information from you through the DevExpress Support Center or its web properties. Any and all materials or information divulged during chats, email communications, online discussions, Support Center tickets, or made available to Developer Express Inc in any manner will be deemed NOT to be confidential by Developer Express Inc. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.