Introduction

In any Application, Dashboards provide a summary of the Application using different Graphical objects. Dashboards are important for a quick overview of the whole application that’s why in every application, Dashboards are considered as one of the most important features of any Application.

Have you ever thought as a user of an Application that there are some objects you don’t want in your dashboard because you don’t actually need those charts or graphs?

What is Dynamic Dashboard?

If you have used Google Analytics, you can choose what segment you want to view in your dashboard by Adding new Segments. something like this.

Google Analytics Dashboard Example
Google Analytics Dashboard Example

This is just a simple example. There are many more related examples available but In this Tutorial, we are going to Create these kinds of User-Defined Dashboards using Asp.Net Core.

This Tutorial is going to be little lengthy. So, take a cup of Coffee with you before you start.

Quick look

Before start, we should know what we’ll get at the end. I would suggest you watch this short video of what we’re going to develop.

Now, I hope you have a clear idea what we’re going to develop.

Here’s what will be included in this Tutorial:

  1. Creating an ASP.NET Project
  2. Setting up UI Template in our Project
  3. Creating Database & Scaffolding in our Project
  4. Creating Dashboard Templates
  5. Creating Dashboard Elements

Let’s create an empty ASP.NET project

Creating an ASP.NET Project

I’m going to use dotnet cli for creating new project using the command below.

dotnet new empty

First of all, update your Startup.cs File with the below Code to ready your project for a Web Application.

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();
    }
    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        app.UseStaticFiles();
        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Dashboard}/{action=Index}/{id?}");
        });
    }
}

Now create new Folders as Models, Views, Controllers & wwwroot at the root directory of your project.

Setting up Template

First of all, you need to download the AdminLTE Template from

GitHub.

Clone or Download the complete

AdminLTE Source, as you can see in below image.

Download Template
Download Template

Unzip the Downloaded file & you’ll get this.

Template Folders
Template Folders

We just need to Copy these two folders bower_components & dist from the above & Paste in the wwwroot folder of your ASP.NET Project we have created at the start of this Article.

Now, create a new file inside your Views Folder as _ViewStart.cshtml & put this Code.

@{
    Layout = "~/Views/Shared/_Layout.cshtml";
}

Create a Folder as Shared & create 2 new Files inside this Folder as _Layout.cshtml & _MainMenu.cshtml

Here’s the Code for _Layout.cshtml

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <title>Dynamic Dashboards</title>
  <!-- Tell the browser to be responsive to screen width -->
  <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
  <link rel="stylesheet" href="~/bower_components/bootstrap/dist/css/bootstrap.min.css">
  <!-- Font Awesome -->
  <link rel="stylesheet" href="~/bower_components/font-awesome/css/font-awesome.min.css">
  <!-- Ionicons -->
  <link rel="stylesheet" href="~/bower_components/Ionicons/css/ionicons.min.css">
  <!-- Theme style -->
  <link rel="stylesheet" href="~/dist/css/AdminLTE.min.css">
  <link rel="stylesheet" href="~/dist/css/skins/skin-blue.min.css">
  <!-- jQuery 3 -->
  <script src="~/bower_components/jquery/dist/jquery.min.js"></script>
  <!--Load the AJAX API-->
  <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/Dropify/0.2.2/css/dropify.min.css">
  <!-- Google Font -->
  <link rel="stylesheet"
        href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700,300italic,400italic,600italic">
</head>
<body class="hold-transition skin-blue sidebar-mini">
<div class="wrapper">
  <!-- Main Header -->
  <header class="main-header">
    <!-- Logo -->
    <a href="index2.html" class="logo">
      <!-- mini logo for sidebar mini 50x50 pixels -->
      <span class="logo-mini"><b>D</b>D</span>
      <!-- logo for regular state and mobile devices -->
      <span class="logo-lg">Dynamic<b>Dashboards</b></span>
    </a>
    <!-- Header Navbar -->
    <nav class="navbar navbar-static-top" role="navigation">
      <!-- Sidebar toggle button-->
      <a href="#" class="sidebar-toggle" data-toggle="push-menu" role="button">
        <span class="sr-only">Main Menu</span>
      </a>
      <!-- Navbar Right Menu -->
      <div class="navbar-custom-menu">
        <ul class="nav navbar-nav">
          <!-- User Account Menu -->
          <li class="dropdown user user-menu">
            <!-- Menu Toggle Button -->
            <a href="#" class="dropdown-toggle" data-toggle="dropdown">
              <!-- The user image in the navbar-->
              <img src="~/dist/img/user2-160x160.jpg" class="user-image" alt="User Image">
              <!-- hidden-xs hides the username on small devices so only the image appears. -->
              <span class="hidden-xs">Shehryar khan</span>
            </a>
            <ul class="dropdown-menu">
              <!-- The user image in the menu -->
              <li class="user-header">
                <img src="~/dist/img/user2-160x160.jpg" class="img-circle" alt="User Image">
                <p>
                  Shehryar khan - Dot Tutorials
                  <small>Member since Nov. 2012</small>
                </p>
              </li>
              <!-- Menu Body -->
              <li class="user-body">
                <div class="row">
                  <div class="col-xs-4 text-center">
                    <a href="#">Account</a>
                  </div>
                </div>
                <!-- /.row -->
              </li>
              <!-- Menu Footer-->
              <li class="user-footer">
                <div class="pull-left">
                  <a href="#" class="btn btn-default btn-flat">Profile</a>
                </div>
                <div class="pull-right">
                  <a href="#" class="btn btn-default btn-flat">Sign out</a>
                </div>
              </li>
            </ul>
          </li>
        </ul>
      </div>
    </nav>
  </header>
  <!-- Left side column. contains the logo and sidebar -->
  <aside class="main-sidebar">
    <!-- sidebar: style can be found in sidebar.less -->
    <section class="sidebar">
      <!-- Sidebar user panel (optional) -->
      <div class="user-panel">
        <div class="pull-left image">
          <img src="~/dist/img/user2-160x160.jpg" class="img-circle" alt="User Image">
        </div>
        <div class="pull-left info">
          <p>Shehryar Khan</p>
          <!-- Status -->
          <a href="#"><i class="fa fa-circle text-success"></i> Online</a>
        </div>
      </div>
      <!-- search form (Optional) -->
      <form action="#" method="get" class="sidebar-form">
        <div class="input-group">
          <input type="text" name="q" class="form-control" placeholder="Search...">
          <span class="input-group-btn">
              <button type="submit" name="search" id="search-btn" class="btn btn-flat"><i class="fa fa-search"></i>
              </button>
            </span>
        </div>
      </form>
      <!-- /.search form -->
      <!-- Sidebar Menu -->
      @Html.Partial("_MainMenu")
      <!-- /.sidebar-menu -->
    </section>
    <!-- /.sidebar -->
  </aside>
  <!-- Content Wrapper. Contains page content -->
  <div class="content-wrapper">
    <!-- Content Header (Page header) -->
    @RenderBody()
    <!-- /.content -->
  </div>
  <!-- /.content-wrapper -->
  <!-- Main Footer -->
  <footer class="main-footer">
    <!-- Default to the left -->
    <strong>Copyright © 2019 <a href="https://dottutorials.net">Dot Tutorials</a>.</strong> All rights reserved.
  </footer>
</div>
<!-- ./wrapper -->
<!-- REQUIRED JS SCRIPTS -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/Dropify/0.2.2/js/dropify.min.js"></script>
<!-- Bootstrap 3.3.7 -->
<script src="~/bower_components/bootstrap/dist/js/bootstrap.min.js"></script>
<!-- AdminLTE App -->
<script src="~/dist/js/adminlte.min.js"></script>
</body>
</html>

We’ll Add Code for _MainMenu.cshtml at the end of this Tutorial.

Creating Database

Here’s the Database, I have created for creating Dynamic Dashboards

Database Schema
Database Schema

I have also shared .sql File at GitHub. you can Download & Import this Database.

Getting Dashboard Name

You need to Create an Interface for Dashboard Name Input.

Create a new Folder inside Views Folder as Dashboard & create a new File as Index.cshtml inside this Dashboard folder & add this Code.

<style>
   #dashboard {
  align: center;  
  width: auto; 
  padding-left: 60px;
  padding-right: 60px;
  padding-top: 60px;
  padding-bottom: 60px;
}
#b {
    height: 500px;
}
</style>

<div align="center"   class="row">
    <div class="col-lg-12">
        <div id="b" class="white-box m-t-30 center-block">
            <form id="dashboard" >
                <h1 class="" >Welcome to Dashboard Wizard</h1>
                <div class="form-group">
                    <label for="exampleInputkwh">Enter Dashboard Name</label>
                    <div class="input-group">
                        <div class="input-group-addon"><i class="fa fa-dashboard"></i></div>
                        <input type="text" class="form-control" id="dashboard-name" name="dashboard-name"/> 
                    </div>
                    <br>
                    <div align="center">
                        <button type="button" class="btn custom-btn waves-effect waves-light" data-dismiss="modal" data-toggle="modal" onclick="DashboardTemplates()">Next</button>
                    </div>
                </div>
            </form>
        </div>
    </div>
</div>

<script>
    function DashboardTemplates()
    {
        localStorage.setItem("dashboard_name", $("#dashboard-name").val());
        window.location = "/Dashboard/TemplateList/";
    }
</script>

It will create a view like this.

dynamic dashboard home

Creating Dashboard Templates

For Creating Dashboards Dynamically, we need some Templates. I have already created some Templates, here’s the example.

Dynamic Dashboard Templates
Dynamic Dashboard Templates

For creating your own Templates, create a new Folder in your Views->Dashboard Folder with the name as Templates & Create Separate template in a Separate file as Partial View.

Dynamic Dashboard Templates
Dynamic Dashboard Templates

TemplateList.cshtml is the UI for choosing your Template. In only shows all Templates as Partial Views in a list with radio buttons.

Here’s Code for TemplateList.cshtml

@{
    ViewBag.Title = "Dashboard's Template Selection";
}
<!-- Content Header (Page header) -->
<section class="content-header">
    <h1>
        Templates List
    </h1>
</section>

<!-- Main content -->
<section class="content container-fluid">
    <div class="row">
        <div class="col-xs-12">
            <div class="row">
                <br>
                <div class="col-sm-6">
                    <div class="col-sm-1">
                        <div>
                            <input id="1" type="radio" name="template" checked>
                        </div>
                    </div>
                    <div class="col-sm-11 text-right"> <img src="~/images/DashboardTemplates/t1.JPG" alt="image" class="img-thumbnail">
                        <p class="m-t-15 m-b-0"> <code>Template 1</code> </p>
                    </div>
                </div>
                <div class="col-sm-6">
                    <div class="col-sm-1">
                        <div>
                            <input id="2" type="radio" name="template">
                        </div>
                    </div>
                    <div class="col-sm-11 text-right"> <img src="~/images/DashboardTemplates/t2.JPG" alt="image" class="img-thumbnail">
                        <p class="m-t-15 m-b-0"> <code>Template 2</code> </p>
                    </div>
                </div>
            </div>
            <br><hr><br>
            <div class="row">
                <div class="col-sm-6">
                    <div class="col-sm-1">
                        <div>
                            <input id="3" type="radio" name="template">
                        </div>
                    </div>
                    <div class="col-sm-11 text-right"> <img src="~/images/DashboardTemplates/t3.JPG" alt="image" class="img-thumbnail">
                        <p class="m-t-15 m-b-0"> <code>Template 3</code> </p>
                    </div>
                </div>
                <div class="col-sm-6">
                    <div class="col-sm-1">
                        <div>
                            <input id="4" type="radio" name="template">
                        </div>
                    </div>
                    <div class="col-sm-11 text-right"> <img src="~/images/DashboardTemplates/t4.JPG" alt="image" class="img-thumbnail">
                        <p class="m-t-15 m-b-0"> <code>Template 4</code> </p>
                    </div>
                </div>
            </div>
            <br><hr><br>
            <div class="row">
                <div class="col-sm-6">
                    <div class="col-sm-1">
                        <div>
                            <input id="5" type="radio" name="template">
                        </div>
                    </div>
                    <div class="col-sm-11 text-right"> <img src="~/images/DashboardTemplates/t5.JPG" alt="image" class="img-thumbnail">
                        <p class="m-t-15 m-b-0"> <code>Template 5</code> </p>
                    </div>
                </div>
                <div class="col-sm-6">
                    <div class="col-sm-1">
                        <div>
                            <input id="6" type="radio" name="template">
                        </div>
                    </div>
                    <div class="col-sm-11 text-right"> <img src="~/images/DashboardTemplates/t6.JPG" alt="image" class="img-thumbnail">
                        <p class="m-t-15 m-b-0"> <code>Template 6</code> </p>
                    </div>
                </div>
            </div>
            <br><hr><br>
            <div class="row">
                <div class="col-sm-6">
                    <div class="col-sm-1">
                        <div>
                            <input id="7" type="radio" name="template">
                        </div>
                    </div>
                    <div class="col-sm-11 text-right"> <img src="~/images/DashboardTemplates/t7.JPG" alt="image" class="img-thumbnail">
                        <p class="m-t-15 m-b-0"> <code>Template 7</code> </p>
                    </div>
                </div>
                <div class="col-sm-6">
                    <div class="col-sm-1">
                        <div>
                            <input id="8" type="radio" name="template">
                        </div>
                    </div>
                    <div class="col-sm-11 text-right"> <img src="~/images/DashboardTemplates/t8.JPG" alt="image" class="img-thumbnail">
                        <p class="m-t-15 m-b-0"> <code>Template 8</code> </p>
                    </div>
                </div>
            </div>
            <br><hr>
            <div align="right">
                <button type="btn" class="btn custom-btn waves-effect waves-light m-r-10" onclick="Next()">Next</button>
            </div>
        </div>
    </div>
</section>

<script>

    function Next(){

        $.ajax(
        {
            type: "POST",
            url: '@Url.Action("Createdashboard", "Dashboard")',
            data: {
                Name: localStorage.getItem("dashboard_name"),
                TemplateId: $('input[type=radio][name=template]:checked').attr('id')
            },
            error: function (result) {
                alert("error");
            },
            success: function (result) {

                if (result != "False") {
                    window.location = "/Dashboard/Dashboard/"+result;
                }
                else {
                    alert("There is a Problem!");
                }
            }
        });
        
    }
</script>

Creating Dashboard Elements

Elements are Charts & Graphs for our Dashboards. We’ll create a Separate Partial View for an Element & Create an Element List for choosing Elements.

Here’s the Element List created by me.

Dynamic Dashboard Elements
Dynamic Dashboard Elements

I have Added Created Some Charts using Google Charts Library with dummy Data.

So, create a new Folder as Elements inside your Dashboards Folder Views->Dashboard & create partial views for Elements.

Same as Templates, I have also created Elements as Separate Views & each Element needs to be Inserted as a record in Database like this.

Dynamic Dashboard Elements
Dynamic Dashboard Elements

Dashboards Logic

Create a Controller as DashboardController.cs & Add the Below Code for All the Interfaces we have created above.

using System.Collections.Generic;
using System.Linq;
using Dynamic_User_Defined_Dashboards.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;

namespace EnergyAxis.Controllers
{
    public class DashboardController : Controller
    {
        private Dashboard_TutorialContext db = new Dashboard_TutorialContext();
        public IActionResult Index()
        {
            return View();
        }

        public IActionResult TemplateList(){
            return View("Templates/TemplateList");
        }

        public IActionResult Dashboard(int id)
        {
            DashboardsInfo dashboard = db.DashboardsInfo.Where(s => s.Id == id).FirstOrDefault();
            int elementsCount = (int)db.Templates.Where(s=>s.Id == dashboard.TemplateId).Select(s=>s.ElementsCount).FirstOrDefault();

            var linked_elements  = db.DashboardLinkedElements.Where(s=>s.DashboardId == id).ToList();
            for (int i = 1; i <= elementsCount; i++)
            {
                var element = linked_elements.Where(s=>s.Placement == i.ToString());
                if(element.Any()){
                    ViewData["Element"+i] = "../Elements/Element"+element.First().ElementId+".cshtml";
                }
                else{
                    ViewData["Element"+i] = "../Elements/Default.cshtml";
                }
            }

            ViewData["dashboardId"] = id;
            return View("Templates/Template"+dashboard.TemplateId);
        }

        public string Createdashboard(DashboardsInfo dashboard)
        {
            try
            {
                db.DashboardsInfo.Add(dashboard);
                db.SaveChanges();

                return dashboard.Id.ToString();
            }
            catch (System.Exception)
            {
                return "False";
            }
        }

     
        public IActionResult ElementList(int id){

            ViewData["dashboardId"] = id;
            return View("Elements/ElementList");
        }

        public string AddElement(DashboardLinkedElements element){

            var old = db.DashboardLinkedElements.Where(s=>s.DashboardId == element.DashboardId && s.Placement == element.Placement).ToList();
            foreach (var item in old)
            {
                db.DashboardLinkedElements.Remove(item);
            }
            db.SaveChanges();

            try
            {
                db.DashboardLinkedElements.Add(element);
                db.SaveChanges();
                return "True";
            }
            catch (System.Exception e)
            {
                return e.Message;
            }
        }

        public ActionResult GetDashboardsList()
        {
            return Ok(db.DashboardsInfo.ToList());
        }
    }
}

In the end, you need to create your Sidebar Menu from Database for displaying every Dashboard you create in your Menu.

So, here’s the Code for _MainMenu.cshtml you have created earlier insideViews->Shared

<ul class="sidebar-menu" data-widget="tree">
<li class="header">HEADER</li>
<!-- Optionally, you can add icons to the links -->
<li><a href="/Dashboard/Index"><i class="fa fa-dashboard"></i> <span>Create New</span></a></li>
<li class="treeview">
    <a href="#"><i class="fa fa-dashboard"></i> <span>Dashboards</span>
    <span class="pull-right-container">
        <i class="fa fa-angle-left pull-right"></i>
        </span>
    </a>
    <ul class="treeview-menu" id="menu">
    </ul>
</li>
</ul>

<script>

    $(function() {

        $.ajax(
        {
            type: "POST",
            url: '@Url.Action("GetDashboardsList", "Dashboard")',
            error: function (result) {
                alert(result);
            },
            success: function (result) {
                var menu = null;
                $.each( result, function( key, value ) {
                    menu += '<li><a href="/Dashboard/Dashboard/'+value.id+'">'+value.name+'</a></li>';
                });
                $("#menu").html(menu);
            }
        });
});
</script>

Run your Application now. I hope everything will work Fine.

I have also shared the complete project on Github. You just required .Net Core SDK 2.2 for running this Project.

Related Articles:

Best Windows Hosting to Host an ASP.NET Application

A Complete Guide to Secure your ASP.NET Web Application & API