Beispiel #1
0
        /// <summary>
        /// Creates a chart displaying a supported <see cref="ChartType"/> with the given data. Includes a chart and a table, and allows exporting the data to CSV.
        /// Assuming <paramref name="seriesCollection"/> has multiple elements, draws multiple sets of Y values on the same chart.
        /// </summary>
        /// <param name="setup">The setup object for the chart.</param>
        /// <param name="seriesCollection">The data series collection.</param>
        /// <param name="colors">The colors to use for the data series collection. Pass null for default colors. If you specify your own colors, the number of
        /// colors does not need to match the number of series. If you pass fewer colors than series, the chart will use random colors for the remaining series.
        /// </param>
        public Chart(ChartSetup setup, [NotNull] IEnumerable <DataSeries> seriesCollection, IEnumerable <Color> colors = null)
        {
            seriesCollection = seriesCollection.ToArray();

            var rand = new Random();

            colors = (colors ?? getDefaultColors()).Take(seriesCollection.Count())
                     .Pad(seriesCollection.Count(), () => Color.FromArgb(rand.Next(256), rand.Next(256), rand.Next(256)));

            this.setup = setup;

            CssClass = CssClass.ConcatenateWithSpace(CssElementCreator.CssClass);

            Func <DataSeries, Color, BaseDataset> datasetSelector;
            OptionsBase options;

            switch (setup.ChartType)
            {
            case ChartType.Line:
                datasetSelector = (series, color) => new Dataset(color, series.Values.TakeLast(setup.MaxXValues));
                options         = new LineOptions {
                    bezierCurve = false
                };
                break;

            case ChartType.Bar:
                datasetSelector = (series, color) => new BaseDataset(color, series.Values.TakeLast(setup.MaxXValues));
                // ReSharper disable once RedundantEmptyObjectOrCollectionInitializer
                options = new BarOptions {
                };
                break;

            default:
                throw new UnexpectedValueException(setup.ChartType);
            }

            var chartData = new ChartData(
                setup.Labels.TakeLast(setup.MaxXValues),
                seriesCollection.Zip(colors, (series, color) => datasetSelector(series, color)).ToArray());

            var canvas = new HtmlGenericControl("canvas");

            switch (setup.ChartType)
            {
            case ChartType.Line:
            case ChartType.Bar:
                canvas.Attributes.Add("height", "400");
                break;

            default:
                throw new UnexpectedValueException(setup.ChartType);
            }
            Controls.Add(canvas);

            if (seriesCollection.Count() > 1)
            {
                this.AddControlsReturnThis(
                    new Section(
                        "Key",
                        new LineList(
                            chartData.datasets.Select(
                                (dataset, i) => (LineListItem) new TrustedHtmlString(
                                    "<div style='display: inline-block; vertical-align: middle; width: 20px; height: 20px; background-color: {0}; border: 1px solid {1};'>&nbsp;</div> {2}"
                                    .FormatWith(dataset.fillColor, dataset.strokeColor, seriesCollection.ElementAt(i).Name)).ToComponent()
                                .ToComponentListItem())).ToCollection(),
                        style: SectionStyle.Box).ToCollection()
                    .GetControls());
            }

            // Remove this when ColumnPrimaryTable supports Excel export.
            var headers   = setup.XAxisTitle.ToCollection().Concat(seriesCollection.Select(v => v.Name));
            var tableData = new List <IEnumerable <object> >(seriesCollection.First().Values.Count());

            for (var i = 0; i < tableData.Capacity; i++)
            {
                var i1 = i;
                tableData.Add(setup.Labels.ElementAt(i1).ToCollection().Concat(seriesCollection.Select(v => v.Values.ElementAt(i1).ToString())));
            }
            var exportAction = getExportAction(headers, tableData);

            var table = ColumnPrimaryTable.Create(tableActions: exportAction.ToCollection(), firstDataFieldIndex: 1)
                        .AddItems(
                EwfTableItem.Create(setup.XAxisTitle.ToCollection().Concat(setup.Labels).Select(i => i.ToCell()).Materialize())
                .ToCollection()
                .Concat(
                    from series in seriesCollection
                    select EwfTableItem.Create(series.Name.ToCell().Concat(from i in series.Values select i.ToString().ToCell()).Materialize()))
                .Materialize());

            this.AddControlsReturnThis(table.ToCollection().GetControls());

            jsInitStatementGetter = () => {
                using (var writer = new StringWriter()) {
                    writer.WriteLine("var canvas = document.getElementById( '{0}' );".FormatWith(canvas.ClientID));
                    writer.WriteLine("canvas.width = $( canvas ).parent().width();");
                    writer.WriteLine(
                        "new Chart( canvas.getContext( '2d' ) ).{0}( {1}, {2} );".FormatWith(
                            setup.ChartType,
                            JsonOps.SerializeObject(chartData),
                            JsonOps.SerializeObject(options)));
                    return(writer.ToString());
                }
            };
        }
Beispiel #2
0
        /// <summary>
        /// Creates a chart displaying a supported <see cref="ChartType"/> with the given data. Includes a chart and a table, and allows exporting the data to
        /// Excel. Assuming <paramref name="dataSets"/> has multiple elements, draws multiple sets of Y values on the same chart.
        /// </summary>
        /// <param name="setup">The setup object for the chart.</param>
        /// <param name="dataSets">The data sets.</param>
        /// <param name="colors">The colors to use for the data sets. Pass null for default colors. If you specify your own colors, the number of colors does not
        /// need to match the number of data sets. If you pass fewer colors than data sets, the chart will use random colors for the remaining data sets.</param>
        public Chart(ChartSetup setup, [NotNull] IReadOnlyCollection <ChartDataSet> dataSets, IEnumerable <Color> colors = null)
        {
            var rand = new Random();

            colors = (colors ?? getDefaultColors()).Take(dataSets.Count)
                     .Pad(dataSets.Count, () => Color.FromArgb(rand.Next(256), rand.Next(256), rand.Next(256)));

            string chartType;

            string toRgbaString(Color color, string opacity) => "rgba( {0}, {1}, {2}, {3} )".FormatWith(color.R, color.G, color.B, opacity);

            Func <ChartDataSet, Color, JObject> datasetSelector;
            var yAxisTicksCallbackProperty = setup.YAxisLabelFormatOptions != null
                                                                 ? new JProperty(
                "callback",
                new JRaw(
                    "function( value, index, values ) {{ return new Intl.NumberFormat( '{0}', {1} ).format( value ); }}".FormatWith(
                        Cultures.EnglishUnitedStates.Name,
                        setup.YAxisLabelFormatOptions.ToString(Formatting.None)))).ToCollection()
                                                                 : Enumerable.Empty <JProperty>();
            JObject options;

            switch (setup.ChartType)
            {
            case ChartType.Line:
                chartType       = "line";
                datasetSelector = (set, color) => new JObject(
                    new JProperty("label", set.Label),
                    new JProperty("data", new JArray(set.Values.TakeLast(setup.MaxXValues))),
                    new JProperty("pointBackgroundColor", toRgbaString(color, "1")),
                    new JProperty("backgroundColor", toRgbaString(color, "0.25")),
                    new JProperty("borderColor", toRgbaString(color, "1")));
                options = new JObject(
                    new JProperty("aspectRatio", setup.AspectRatio),
                    new JProperty("legend", new JObject(new JProperty("display", dataSets.Count > 1))),
                    new JProperty(
                        "scales",
                        new JObject(
                            new JProperty(
                                "xAxes",
                                new JArray(
                                    new JObject(
                                        new JProperty(
                                            "scaleLabel",
                                            new JObject(new JProperty("display", setup.XAxisTitle.Any()), new JProperty("labelString", setup.XAxisTitle)))))),
                            new JProperty(
                                "yAxes",
                                new JArray(
                                    new JObject(
                                        new JProperty(
                                            "scaleLabel",
                                            new JObject(new JProperty("display", setup.YAxisTitle.Any()), new JProperty("labelString", setup.YAxisTitle))),
                                        new JProperty(
                                            "ticks",
                                            new JObject(new JProperty("beginAtZero", true).ToCollection().Concat(yAxisTicksCallbackProperty)))))))));
                break;

            case ChartType.Bar:
            case ChartType.StackedBar:
            case ChartType.HorizontalBar:
            case ChartType.HorizontalStackedBar:
                var horizontal = setup.ChartType == ChartType.HorizontalBar || setup.ChartType == ChartType.HorizontalStackedBar;
                chartType = horizontal ? "horizontalBar" : "bar";

                var stacked = setup.ChartType == ChartType.StackedBar || setup.ChartType == ChartType.HorizontalStackedBar;
                datasetSelector = (set, color) => new JObject(
                    new JProperty("label", set.Label).ToCollection()
                    .Append(new JProperty("data", new JArray(set.Values.TakeLast(setup.MaxXValues))))
                    .Append(new JProperty("backgroundColor", toRgbaString(color, "1")))
                    .Concat(stacked ? new JProperty("stack", set.StackedGroupName).ToCollection() : Enumerable.Empty <JProperty>()));

                var xAxis = new JObject(
                    new JProperty("stacked", stacked),
                    new JProperty(
                        "scaleLabel",
                        new JObject(new JProperty("display", setup.XAxisTitle.Any()), new JProperty("labelString", setup.XAxisTitle))));
                var yAxis = new JObject(
                    new JProperty("stacked", stacked),
                    new JProperty("scaleLabel", new JObject(new JProperty("display", setup.YAxisTitle.Any()), new JProperty("labelString", setup.YAxisTitle))),
                    new JProperty("ticks", new JObject(new JProperty("beginAtZero", true).ToCollection().Concat(yAxisTicksCallbackProperty))));
                options = new JObject(
                    new JProperty("aspectRatio", setup.AspectRatio),
                    new JProperty("legend", new JObject(new JProperty("display", dataSets.Count > 1))),
                    new JProperty(
                        "scales",
                        new JObject(
                            new JProperty("xAxes", new JArray(horizontal ? yAxis : xAxis)),
                            new JProperty("yAxes", new JArray(horizontal ? xAxis : yAxis)))));

                break;

            default:
                throw new UnexpectedValueException(setup.ChartType);
            }

            var canvas = new GenericFlowContainer(
                new ElementComponent(
                    context => new ElementData(
                        () => {
                var jsInitStatement = "new Chart( '{0}', {{ type: '{1}', data: {2}, options: {3} }} );".FormatWith(
                    context.Id,
                    chartType,
                    new JObject(
                        new JProperty("labels", new JArray(setup.Labels.TakeLast(setup.MaxXValues))),
                        new JProperty("datasets", new JArray(dataSets.Zip(colors, (set, color) => datasetSelector(set, color))))).ToString(
                        Formatting.None),
                    options.ToString(Formatting.None));

                return(new ElementLocalData(
                           "canvas",
                           focusDependentData: new ElementFocusDependentData(includeIdAttribute: true, jsInitStatements: jsInitStatement)));
            })).ToCollection());

            var table = setup.OmitTable
                                            ? Enumerable.Empty <FlowComponent>()
                                            : new FlowCheckbox(
                false,
                "Show underlying data".ToComponents(),
                setup: FlowCheckboxSetup.Create(
                    nestedContentGetter: () =>
                    ColumnPrimaryTable.Create(postBackIdBase: setup.PostBackIdBase, allowExportToExcel: true, firstDataFieldIndex: 1)
                    .AddItems(
                        (setup.XAxisTitle.Any() || setup.Labels.Any(i => i.Any())
                                                                                                      ? EwfTableItem.Create(setup.XAxisTitle.ToCollection().Concat(setup.Labels).Select(i => i.ToCell()).Materialize())
                         .ToCollection()
                                                                                                      : Enumerable.Empty <EwfTableItem>()).Concat(
                            dataSets.Select(
                                dataSet => EwfTableItem.Create(
                                    dataSet.Label.ToCell().Concat(from i in dataSet.Values select i.ToString().ToCell()).Materialize())))
                        .Materialize())
                    .ToCollection())).ToFormItem()
                        .ToComponentCollection();

            children = new GenericFlowContainer(canvas.Concat(table).Materialize(), classes: elementClass).ToCollection();
        }