This example shows how to create a custom list of dashboards similar to the Dashboard Panel.
Server
The DevExtreme ASP.NET Data package's API is used to prepare a list of dashboard names with their IDs. Identifiers are based on the Products
table from the Northwind
database and accessed through the Entity Framework Core. The DashboardPanelController.Dashboards
action method call returns this list.
You need to implement the custom dashboard storage to store dashboards. Call the IDashboardStorage.LoadDashboard method to return the corresponding dashboard from this storage. This example uses a single dashboard XML template (the DashboardTemplate.xml
file) and only modifies the dashboard's title to emulate different dashboards. In this particular usage scenario, you can store dashboard layouts in the database and load them from here (for example, see Dashboard for ASP.NET Core - How to load and save dashboards from/to a database).
Client
The dxList widget is used to load and display the list of dashboards. The searchEnabled option of this widget is enabled to allow search. You can use other widget options to enable the required functionality. For example, the itemDragging.allowReordering option allows end users to reorder items (see Item Dragging).
NOTE: This example uses the
ProductID
database field as a dashboard's ID. The field's type isnumber
while the DashboardInfo.ID property and the IDashboardStorage.LoadDashboard method's argument type isstring
. It is necessary to convert types. In this example, this is done in theNorthwindContext.OnModelCreating
method (see NorthwindContext.cs).
Files to Review
- Startup.cs
- DashboardPanelController.cs
- CustomDashboardStorage.cs
- Product.cs
- NorthwindContext.cs
- app.component.ts
- app.component.html
Quick Start
Server
Run the following command in the asp-net-core-server folder:
Codedotnet run
The server starts at http://localhost:5000
and the client gets data from http://localhost:5000/api/dashboard
. To debug the server, run the asp-net-core-server application in Visual Studio and change the client's serverUrl
property according to the listening port: https://localhost:44396/
.
See the following section for information on how to install NuGet packages from the DevExpress NuGet feed: Install DevExpress Controls Using NuGet Packages.
This server allows CORS requests from all origins with any scheme (http or https). This default configuration is insecure: any website can make cross-origin requests to the app. We recommend that you specify the client application's URL to prohibit other clients from accessing sensitive information stored on the server. Learn more: Cross-Origin Resource Sharing (CORS)
Client
In the dashboard-angular-app folder, run the following commands:
Codenpm install
npm start
Open http://localhost:4200/
in your browser to see the Web Dashboard application.
Documentation
More Examples
- Dashboard for ASP.NET Core - How to Implement a Custom Service and UI for Managing Dashboards List
- Dashboard for ASP.NET Core - How to load and save dashboards from/to a database
Does this example address your development requirements/objectives?
(you will be redirected to DevExpress.com to submit your response)
Example Code
C#using Microsoft.EntityFrameworkCore;
namespace AspNetCoreDashboardBackend {
public partial class NorthwindContext : DbContext {
public NorthwindContext(DbContextOptions<NorthwindContext> options)
: base(options) {
}
public virtual DbSet<Product> Products { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder) {
modelBuilder
.Entity<Product>()
.Property(e => e.ProductID)
.HasConversion<string>();
}
}
}
C#using System;
using DevExpress.AspNetCore;
using DevExpress.DashboardAspNetCore;
using DevExpress.DashboardCommon;
using DevExpress.DashboardWeb;
using DevExpress.DataAccess.Json;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
namespace AspNetCoreDashboardBackend {
public class Startup {
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public Startup(IConfiguration configuration, IWebHostEnvironment hostingEnvironment) {
Configuration = configuration;
FileProvider = hostingEnvironment.ContentRootFileProvider;
}
public IConfiguration Configuration { get; }
public IFileProvider FileProvider { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services) {
// Configures services to use the Web Dashboard Control.
services
.AddEntityFrameworkSqlite()
.AddDbContext<NorthwindContext>(options => options
.UseSqlite("Data Source=App_Data/nwind.db")
);
services
.AddCors(options => {
options.AddPolicy("CorsPolicy", builder => {
builder.AllowAnyOrigin();
builder.AllowAnyMethod();
builder.WithHeaders("Content-Type");
});
})
.AddDevExpressControls()
.AddControllersWithViews();
services.AddScoped<DashboardConfigurator>((IServiceProvider serviceProvider) => {
DashboardConfigurator configurator = new DashboardConfigurator();
configurator.SetDashboardStorage(
new CustomDashboardStorage(FileProvider.GetFileInfo("App_Data/Dashboards").PhysicalPath,
serviceProvider.GetService<NorthwindContext>()));
configurator.SetDataSourceStorage(CreateDataSourceStorage());
configurator.SetConnectionStringsProvider(new DashboardConnectionStringsProvider(Configuration));
configurator.ConfigureDataConnection += Configurator_ConfigureDataConnection;
return configurator;
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
// Registers the DevExpress middleware.
app.UseDevExpressControls();
app.UseRouting();
app.UseCors("CorsPolicy");
app.UseEndpoints(endpoints => {
// Maps the default controller/action to display the service info view.
endpoints.MapDefaultControllerRoute();
// Maps the dashboard route.
endpoints.MapDashboardRoute("api/dashboard", "DefaultDashboard");
// Requires CORS policies.
endpoints.MapControllers().RequireCors("CorsPolicy");
});
}
public DataSourceInMemoryStorage CreateDataSourceStorage() {
DataSourceInMemoryStorage dataSourceStorage = new DataSourceInMemoryStorage();
DashboardJsonDataSource jsonDataSourceSupport = new DashboardJsonDataSource("Support");
jsonDataSourceSupport.ConnectionName = "jsonSupport";
jsonDataSourceSupport.RootElement = "Employee";
dataSourceStorage.RegisterDataSource("jsonDataSourceSupport", jsonDataSourceSupport.SaveToXml());
return dataSourceStorage;
}
private void Configurator_ConfigureDataConnection(object sender, ConfigureDataConnectionWebEventArgs e) {
if (e.ConnectionName == "jsonSupport") {
Uri fileUri = new Uri(FileProvider.GetFileInfo("App_Data/Support.json").PhysicalPath, UriKind.RelativeOrAbsolute);
JsonSourceConnectionParameters jsonParams = new JsonSourceConnectionParameters();
jsonParams.JsonSource = new UriJsonSource(fileUri);
e.ConnectionParameters = jsonParams;
}
}
}
}
C#using System.Linq;
using System.Threading.Tasks;
using DevExtreme.AspNet.Data;
using Microsoft.AspNetCore.Mvc;
namespace AspNetCoreDashboardBackend {
[Route("dashboardpanel")]
public class DashboardPanelController: Controller {
private NorthwindContext nwindContext;
public DashboardPanelController(NorthwindContext nwindContext) {
this.nwindContext = nwindContext;
}
[HttpGet("dashboards")]
public async Task<IActionResult> Dashboards(DataSourceLoadOptions loadOptions) {
var source = nwindContext.Products.Select(item => new {
item.ProductID,
item.ProductName
});
loadOptions.PrimaryKey = new[] { "ProductID" };
loadOptions.PaginateViaPrimaryKey = true;
return Json(await DataSourceLoader.LoadAsync(source, loadOptions));
}
}
}
C#using DevExpress.DashboardCommon;
using DevExpress.DashboardWeb;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;
namespace AspNetCoreDashboardBackend {
public class CustomDashboardStorage : IDashboardStorage {
private string dashboardTemplateFolder;
private NorthwindContext nwindContext;
public CustomDashboardStorage(string dashboardTemplateFolder, NorthwindContext nwindContext) {
this.dashboardTemplateFolder = dashboardTemplateFolder;
this.nwindContext = nwindContext;
}
public IEnumerable<DashboardInfo> GetAvailableDashboardsInfo() {
var dashboards = nwindContext.Products.Select(item => new DashboardInfo() {
ID = item.ProductID,
Name = item.ProductName
});
return dashboards;
}
public XDocument LoadDashboard(string dashboardID) {
var dashboard = new Dashboard();
var product = nwindContext.Products.First(product => product.ProductID == dashboardID);
dashboard.LoadFromXml(Path.Combine(dashboardTemplateFolder, "DashboardTemplate.xml"));
dashboard.Title.Text = product.ProductName;
return dashboard.SaveToXDocument();
}
public void SaveDashboard(string dashboardID, XDocument dashboard) {
throw new NotImplementedException();
}
}
}
C#using System.ComponentModel.DataAnnotations;
namespace AspNetCoreDashboardBackend {
public class Product {
[Key]
public string ProductID { get; set; }
public string ProductName { get; set; }
public decimal? UnitPrice { get; set; }
public short? UnitsOnOrder { get; set; }
public int? CategoryID { get; set; }
}
}
TypeScriptimport { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterOutlet } from '@angular/router';
import { DxDashboardControlModule } from 'devexpress-dashboard-angular';
import { DevExtremeModule } from 'devextreme-angular';
import { createStore } from 'devextreme-aspnet-data-nojquery';
import CustomStore from 'devextreme/data/custom_store';
import DataSource from 'devextreme/data/data_source';
@Component({
selector: 'app-root',
standalone: true,
imports: [CommonModule, RouterOutlet, DxDashboardControlModule, DevExtremeModule],
templateUrl: './app.component.html',
styleUrl: './app.component.css'
})
export class AppComponent {
serverUrl: string = "https://localhost:5001";
/* To debug the server, change 'serverUrl' to https://localhost:44396 and run the server application in Visual Studio. */
store: CustomStore;
dataSource: DataSource;
constructor() {
this.store = createStore({
key: "productID",
loadUrl: this.serverUrl + "/dashboardpanel/dashboards"
});
this.dataSource = new DataSource({
store: this.store,
paginate: true,
pageSize: 20
});
}
}
HTML<div style="display: flex; height: 650px; border: 1px solid #ccc">
<div style="width: 250px; border-right: 1px solid #ccc">
<dx-list
#list
[dataSource]="dataSource"
keyExpr="productID"
displayExpr="productName"
pageLoadMode="scrollBottom"
[searchEnabled]="true"
searchExpr="productName"
selectionMode="single">
</dx-list>
</div>
<div style="flex-grow: 1">
<dx-dashboard-control
style="display: block; width: 100%; height: 100%;"
[endpoint]="serverUrl + '/api/dashboard'"
[dashboardId]="list.selectedItemKeys.toString()"
[loadDefaultDashboard]="false">
</dx-dashboard-control>
</div>
</div>