Die Basis

Prinzipiell reden wir mal über folgendes Framework:

www.highcharts.com

Ich bin aber davon überzeugt, dass es auch bei anderen Frameworks genauso oder ähnlich funktioniert.

Wir haben uns für dieses Framework aus 2 Gründen entschieden:

  • Es funktioniert auch auf älteren Browsern der Marke Internet Explorer
  • Es reagiert responsive auf Veränderungen der Bildschirmgröße

Die Aufgabe war nun die Darstellung hierarchischer Datenreihen, wobei auf jedem Level durch Klicken auf die Balken eine Ebene tiefer gegangen werden sollte. Es werden also auf oberster Ebene allgemeine Daten angezeigt, und mit jedem Klick wird die Darstellung der Daten ausführlicher.

Die oberen Ebenen präsentieren jeweils Summen von Einträgen, die beim Click aufgedröselt werden. Dazu werden die Schlüssel in der Datenbank verwendet. Ein Click auf einen Balken führt also dazu, dass alle Elemente aus der Datenbank geholt werden, die als Parent jenen Schlüssel haben, der im Balken angezeigt wird.

Das Model

 

  public class Report : Comparison
  {
   
public class ItemData
    {
     
/// <summary>Item Data per series</summary>
     
public IList<decimal> Data { get; set; }
     
/// <summary>Item Data per series</summary>
     
public IList<Guid?> DrillDownIds { get; set; }
    }

    /// <summary>View for Report</summary>
   
public string ViewName { get; set; }

    /// <summary>Report Title</summary>
    public string Title { get; set; }
    /// <summary>Report Sub-Title</summary>
    public string Subtitle { get; set; }

    /// <summary>Report Series</summary>
    public IList<string> Series { get; set; }

    /// <summary>Report Series</summary>
    public IList<ItemData> Data { get; set; }

    /// <summary>Description und IDs of data</summary>
    public IList<string> Categories { get; set; }   
    public IList<Guid?> CategoryIDs { get; set; }

    /// <summary>Guid des Eintrags</summary>
    public string RootItemIDs { get; set; }

   public Report()
    {
      this.ViewName = „ViewReportColumn“;
    }
    public Report(Report src) : base(src)
    {
      this.ViewName = src.ViewName;
      this.RootItemIDs = src.RootItemIDs;
    }
  }

Das Model wird im Controller aus der Datenbank befüllt.

Die Categories sind die Texte, die als Beschriftungen auf den Achsen angezeigt werden, die IDs sind deren Primary Keys in der Datenbank.

Dabe gibt es jeweils eine bestimmte Anzahl an Serien, zum Beispiel 2 (dieses Jahr und letztes Jahr). Für jede Serie gibt es eine Datenreihe, die dann pro Serie in die Balken gerendert wird. Pro Balken gibt es eine Drilldown-ID, das ist jener Primary Key in der Datenbank, auf den ein Click auf den Balken führen soll.

Die View

Wir stellen nun die Serien, die im Model enthalten sind, als Balkendiagramm dar. Das Model hat in usnerem Beispiel 2 Serien, die so dargestellt werden:

Balkendiagramm

Die Anzeige der Datenreihen und der DrillDown in den HighCharts funktionieren in den HighCharts über JavaScript. Hier mal das komplette Script, wobei ich die „trivialen“ Teile wie Farben, etc. ausgelassen habe, damit es übersichtlicher ist. Diese können leicht aus den Demos ergänzt werden.

$(function () {
  $(„select, input“).uniform();
 
  $(‚#chart‘).highcharts({
 
  title: {
    text: @(Html.Raw(Json.Encode(Model.Title))),
   
    }
  },
  subtitle: {
    text: @(Html.Raw(Json.Encode(Model.Subtitle))),
   
    }
  },
  xAxis: {
    categories: [
      @for (int i=0;i<Model.Categories.Count;i++)
      {
        if (i != 0)
        {
          WriteLiteral(„, „);
        }
        <text>@(Html.Raw(Json.Encode(Model.Categories[i])))</text>
      }
    ],
   
  },
  tooltip: {
    pointFormat: ‚{point.name}<br/><b>{point.y:,.##f}</b> $ ‚000‘
  },
  series: [
      @for(int i = 0;i<Model.Series.Count;i++)
      {
        <text>{
          name: @(Html.Raw(Json.Encode(Model.Series[i]))),
          data: [
            @for (int j = 0; j < Model.Data[i].Data.Count; j++)
            {
              if (j != 0)
              {
                  WriteLiteral(„,“);
              }
              <text>{ y: @(Model.Data[i].Data[j].ToString(„F“, System.Globalization.CultureInfo.CreateSpecificCulture(„en-AU“))), myData: @(Model.Data[i].DrillDownIds[j]) }</text>
            }
            ] },
        </text>
      }
    ],
  plotOptions: {
    column: {
      cursor: ‚pointer‘,
      borderWidth: 0,
      shadow: true,
      point: {
        events: {
          click: function() {
            var myData = this.myData;
            if (myData) { // drill down
              var base = @(Html.Raw(Url.Action(„ReportStatement“, new YEApp.Models.Report(Model))));
              var url = base + ‚&NewRootItemID=‘ + myData;
              location.href = url;
            }
          }
        }
      }
    }
  }
  });
});

Der Trick besteht nun darin, die Informationen aus dem Model in die View einfließen zu lassen.

Den Titel oder Subtitel aus dem Model anzeigen zu lassen, ist noch recht einfach. Beachten Sie, dass Sie hier, damit auch alle Sonderzeichen wie zum Beispiel „&“ richtig angezeigt werden, Json.Encode() aufgerufen werden muss. Danach muss das Ganze noch als Html.Raw() ausgegeben werden, da sonst nichts rauskommt.

Für die Achsenbeschriftungen und die Datenreihen verfahren wir ähnlich, wobei wir hier eine JavaScript-Schleife bauen, deren Grenzen aus dem Model genommen werden und auch die Informationen entsprechend aus den Datenreihen genommen werden. Beachten Sie auch hier das korrekte Coding!

Wichtig für diesen Ansatz ist, dass die vergleichenden Serien sowie alle Listen immer gleich viele Einträge haben müssen. Dafür muss der Controller sorgen. Besteht eine Serie aus weniger Einträgen, stürzen die gezeigten Schleifen ab, weil auf nicht vorhandene Indizes zugegriffen wird.

Am spannendsten ist der generierte Drilldown. Hier wird dynamisch aus den Informationen in der Datenreihe ein ActionUrl zusammengebaut, der dann dynamisch über JavaScript Parameter dazubekommt. Der neue Balken bekommt auch wieder einen Schlüssel (Parent, wenn man so will), für den alle Einträge zusammengefasst werden sollen.

Beim Anlegen des ActionLinks wird ein neues Model erzeugt und der Copy Constructor aufgerufen. Dieser ist so gemacht, dass alle Informationen, die weitervererbt werden sollen, entsprechend kopiert werden. Die Informationen, die für jede Darstellung unique sind oder im Controller berechnet werden, werden nicht kopiert.

Dasselbe funktioniert auch für Liniendiagramme oder horizontale Balkendiagramme. In diesen Fällen ändern sich die Bezeichnungen im JavaScript teilweise, die Funktionalität bleibt die gleiche.

Wir freuen uns schon auf viele schöne Diagramme im Internet und auch auf viele Fragen. Bei dieser spannenden Materie unterstützen wir Sie gerne in der Umsetzung!

Ist doch cool, oder?

LG,
Sabine.