The image below shows the result of the extensions implementation.
// Creates and implements a custom SaveAsDashboardExtension class.
function SaveAsDashboardExtension(dashboardControl) {
var _this = this;
this._control = dashboardControl;
this._toolbox = this._control.findExtension("toolbox");
this._newDashboardExtension = this._control.findExtension("create-dashboard");
this._menuItem = {
id: "dashboard-save-as",
title: "Save As...",
template: "dx-save-as-form",
selected: ko.observable(true),
disabled: ko.computed(function () { return !dashboardControl.dashboard(); }),
index: 112,
data: _this
};
this.saveAs = function () {
if (this.isExtensionAvailable()) {
this._toolbox.menuVisible(false);
this._newDashboardExtension.performCreateDashboard(this.newName(), this._control.dashboard().getJSON());
}
};
this.newName = ko.observable("New Dashboard Name");
}
SaveAsDashboardExtension.prototype.isExtensionAvailable = function () {
return this._toolbox !== undefined && this._newDashboardExtension !== undefined;
}
SaveAsDashboardExtension.prototype.start = function () {
if (this.isExtensionAvailable())
this._toolbox.menuItems.push(this._menuItem);
};
SaveAsDashboardExtension.prototype.stop = function () {
if (this.isExtensionAvailable())
this._toolbox.menuItems.remove(this._menuItem);
}
// Creates and implements a custom DeleteDashboardExtension class.
function DeleteDashboardExtension(_wrapper) {
var _this = this;
this._wrapper = _wrapper;
this._control = _wrapper.GetDashboardControl();
this._toolbox = this._control.findExtension('toolbox');
this.name = "dxdde-delete-dashboard";
this.deleteDashboard = function () {
if (_this.isExtensionAvailable()) {
if (confirm("Delete this Dashboard?")) {
var dashboardid = _this._control.dashboardContainer().id;
var param = JSON.stringify({ DashboardID: dashboardid, ExtensionName: _this.name });
_this._toolbox.menuVisible(false);
_this._wrapper.PerformDataCallback(param, function () {
_this._control.close();
});
}
}
}
this._menuItem = {
id: this.name,
title: "Delete",
click: this.deleteDashboard,
selected: ko.observable(false),
disabled: ko.computed(function () { return !_this._control.dashboard(); }),
index: 113,
hasSeparator: true,
data: _this
};
}
DeleteDashboardExtension.prototype.isExtensionAvailable = function () {
return this._toolbox !== undefined;
}
DeleteDashboardExtension.prototype.start = function () {
if (this.isExtensionAvailable())
this._toolbox.menuItems.push(this._menuItem);
};
DeleteDashboardExtension.prototype.stop = function () {
if (this.isExtensionAvailable())
this._toolbox.menuItems.remove(this._menuItem);
};
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="AspDashboard_CustomExtension.Default" %>
<%@ Register Assembly="DevExpress.Dashboard.v17.1.Web, Version=17.1.8.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a"
Namespace="DevExpress.DashboardWeb" TagPrefix="dx" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<script type="text/javascript" src="Scripts/SaveAsExtension.js"></script>
<script type="text/javascript" src="Scripts/DeleteExtension.js"></script>
<!-- Defines the "Save As" extension template. -->
<script type="text/html" id="dx-save-as-form">
<div>Dashboard Name:</div>
<div style="margin: 10px 0" data-bind="dxTextBox: { value: newName }"></div>
<div data-bind="dxButton: { text: 'Save', onClick: saveAs }"></div>
</script>
<script type="text/javascript">
function onBeforeRender(sender) {
var control = sender.GetDashboardControl();
control.registerExtension(new SaveAsDashboardExtension(control));
control.registerExtension(new DeleteDashboardExtension(sender));
}
</script>
<form id="form1" runat="server">
<div style="position: absolute; top: 0; bottom: 0; left: 0; right: 0;">
<dx:ASPxDashboard ID="ASPxDashboard1" ClientInstanceName="dashboard" runat="server"
UseDashboardConfigurator="true"
OnCustomDataCallback="ASPxDashboard1_CustomDataCallback"
WorkingMode="Designer"
Width="100%" Height="100%">
<ClientSideEvents BeforeRender="onBeforeRender" />
</dx:ASPxDashboard>
</div>
</form>
</body>
</html>
using Storages;
using System;
using System.Collections.Generic;
using System.Web.Script.Serialization;
namespace AspDashboard_CustomExtension {
public partial class Default : System.Web.UI.Page {
protected void Page_Load(object sender, EventArgs e) {
}
protected void ASPxDashboard1_CustomDataCallback(object sender, DevExpress.Web.CustomDataCallbackEventArgs e) {
Dictionary<string, string> parameters = new JavaScriptSerializer().Deserialize<Dictionary<string, string>>(e.Parameter);
if (!parameters.ContainsKey("ExtensionName"))
return;
CustomDashboardFileStorage newDashboardStorage = new CustomDashboardFileStorage(@"~/App_Data/Dashboards");
if (parameters["ExtensionName"] == "dxdde-delete-dashboard" && parameters.ContainsKey("DashboardID"))
newDashboardStorage.DeleteDashboard(parameters["DashboardID"]);
}
}
}
using DevExpress.DashboardWeb;
using System.IO;
namespace Storages {
public class CustomDashboardFileStorage : DashboardFileStorage {
public CustomDashboardFileStorage(string workingDirectory)
: base(workingDirectory) {
}
public void DeleteDashboard(string dashboardID) {
var dashboardPath = base.ResolveFileName(dashboardID);
if (File.Exists(dashboardPath))
File.Delete(dashboardPath);
}
}
}
<%@ Page Language="vb" AutoEventWireup="true" CodeBehind="Default.aspx.vb" Inherits="AspDashboard_CustomExtension.Default" %>
<%@ Register Assembly="DevExpress.Dashboard.v17.1.Web, Version=17.1.8.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a"
Namespace="DevExpress.DashboardWeb" TagPrefix="dx" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<script type="text/javascript" src="Scripts/SaveAsExtension.js"></script>
<script type="text/javascript" src="Scripts/DeleteExtension.js"></script>
<!-- Defines the "Save As" extension template. -->
<script type="text/html" id="dx-save-as-form">
<div>Dashboard Name:</div>
<div style="margin: 10px 0" data-bind="dxTextBox: { value: newName }"></div>
<div data-bind="dxButton: { text: 'Save', onClick: saveAs }"></div>
</script>
<script type="text/javascript">
function onBeforeRender(sender) {
var control = sender.GetDashboardControl();
control.registerExtension(new SaveAsDashboardExtension(control));
control.registerExtension(new DeleteDashboardExtension(sender));
}
</script>
<form id="form1" runat="server">
<div style="position: absolute; top: 0; bottom: 0; left: 0; right: 0;">
<dx:ASPxDashboard ID="ASPxDashboard1" ClientInstanceName="dashboard" runat="server"
UseDashboardConfigurator="true"
OnCustomDataCallback="ASPxDashboard1_CustomDataCallback"
WorkingMode="Designer"
Width="100%" Height="100%">
<ClientSideEvents BeforeRender="onBeforeRender" />
</dx:ASPxDashboard>
</div>
</form>
</body>
</html>
Imports Storages
Imports System
Imports System.Collections.Generic
Imports System.Web.Script.Serialization
Namespace AspDashboard_CustomExtension
Partial Public Class [Default]
Inherits System.Web.UI.Page
Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs)
End Sub
Protected Sub ASPxDashboard1_CustomDataCallback(ByVal sender As Object, ByVal e As DevExpress.Web.CustomDataCallbackEventArgs)
Dim parameters As Dictionary(Of String, String) = (New JavaScriptSerializer()).Deserialize(Of Dictionary(Of String, String))(e.Parameter)
If Not parameters.ContainsKey("ExtensionName") Then
Return
End If
Dim newDashboardStorage As New CustomDashboardFileStorage("~/App_Data/Dashboards")
If parameters("ExtensionName") = "dxdde-delete-dashboard" AndAlso parameters.ContainsKey("DashboardID") Then
newDashboardStorage.DeleteDashboard(parameters("DashboardID"))
End If
End Sub
End Class
End Namespace
Imports DevExpress.DashboardWeb
Imports System.IO
Namespace Storages
Public Class CustomDashboardFileStorage
Inherits DashboardFileStorage
Public Sub New(ByVal workingDirectory As String)
MyBase.New(workingDirectory)
End Sub
Public Sub DeleteDashboard(ByVal dashboardID As String)
Dim dashboardPath = MyBase.ResolveFileName(dashboardID)
If File.Exists(dashboardPath) Then
File.Delete(dashboardPath)
End If
End Sub
End Class
End Namespace