@(Html.DevExtreme().Chart()
.ID("chart")
.Title("Temperature in Toronto (2017)")
.DataSource(d => d.Array())
.DataSourceOptions(c => c
.Sort("date")
.Paginate(false)
)
.ZoomAndPan(z => z.ArgumentAxis(ChartZoomAndPanMode.Pan))
.ScrollBar(s => s.Visible(true))
.ArgumentAxis(a => a
.ArgumentType(ChartDataType.DateTime)
.WholeRange(w => w
.StartValue(new DateTime(2017, 1, 1))
.EndValue(new DateTime(2017, 12, 31))
)
.VisualRange(v => v
.StartValue(new DateTime(2017, 4, 1))
.Length(l => l.Weeks(2))
)
.VisualRangeUpdateMode(VisualRangeUpdateMode.Keep)
)
.ValueAxis(v => v.Add()
.Name("temperature")
.AllowDecimals(false)
.Title(t => t
.Text("Temperature, °C")
.Font(f => f.Color("#ff950c"))
)
.Label(l => l.Font(f => f.Color("#ff950c")))
)
.Series(s => s.Add()
.Color("#ff950c")
.Type(SeriesType.RangeArea)
.ArgumentField("date")
.RangeValue1Field("minTemp")
.RangeValue2Field("maxTemp")
.Name("Temperature range")
)
.OnOptionChanged(@<text>
function(e){
if(e.fullName === "argumentAxis.visualRange") {
onVisualRangeChanged(e.value, e.component);
}
}
</text>)
.Animation(a => a.Enabled(false))
.LoadingIndicator(l => l
.BackgroundColor("none")
.Font(f => f.Size(14))
)
.Legend(l => l.Visible(false))
)
<script>
var HALFDAY = 43200000;
var packetsLock = 0;
function onVisualRangeChanged(visualRange, component) {
var items = component.getDataSource().items();
if (!items.length ||
items[0].date - visualRange.startValue >= HALFDAY ||
visualRange.endValue - items[items.length - 1].date >= HALFDAY) {
uploadDataByVisualRange(visualRange, component);
}
}
function uploadDataByVisualRange(visualRange, component) {
var dataSource = component.getDataSource();
var storage = dataSource.items();
var ajaxArgs = {
startVisible: getDateString(visualRange.startValue),
endVisible: getDateString(visualRange.endValue),
startBound: getDateString(storage.length ? storage[0].date : null),
endBound: getDateString(storage.length ?
storage[storage.length - 1].date : null)
};
if (ajaxArgs.startVisible !== ajaxArgs.startBound &&
ajaxArgs.endVisible !== ajaxArgs.endBound && !packetsLock) {
packetsLock++;
component.showLoadingIndicator();
getDataFrame(ajaxArgs)
.then(function(dataFrame) {
packetsLock--;
var componentStorage = dataSource.store();
dataFrame.forEach(function(item) {
componentStorage.insert(item);
});
dataSource.reload();
var range = component.getArgumentAxis().visualRange();
onVisualRangeChanged(range, component);
},
function(reason) {
packetsLock--;
dataSource.reload();
});
}
}
function getDataFrame(args) {
var deferred = $.Deferred();
$.ajax({
url: "@Url.Content("~/api/TemperatureData")",
dataType: "json",
data: args,
success: function (result) {
deferred.resolve(result.map(function(i) {
return {
date: new Date(i.Date),
minTemp: i.MinTemp,
maxTemp: i.MaxTemp
};
}));
},
error: function () {
deferred.reject("Data Loading Error");
},
timeout: 2000
});
return deferred.promise();
}
function getDateString(dateTime) {
return dateTime ? dateTime.toLocaleDateString("en-US") : "";
}
</script>
using DevExtreme.AspNet.Data;
using DevExtreme.AspNet.Mvc;
using DevExtreme.NETCore.Demos.Models;
using DevExtreme.NETCore.Demos.Models.SampleData;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
namespace DevExtreme.NETCore.Demos.Controllers {
public class ChartsController : Controller {
public ActionResult LoadDataOnDemand() {
return View();
}
}
}
using DevExtreme.NETCore.Demos.Models;
using DevExtreme.NETCore.Demos.Models.SampleData;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
namespace DevExtreme.NETCore.Demos.Controllers.ApiControllers {
[Route("api/[controller]")]
public class TemperatureDataController : Controller {
[HttpGet]
public IEnumerable<TemperatureItem> Get(string startVisible = "01/01/2017", string endVisible = "12/31/2017", string startBound = "01/01/2017", string endBound = "12/31/2017") {
var totalList = SampleData.GetTemperatureRepository();
var startVisibleDate = DateTime.Parse(startVisible);
var endVisibleDate = DateTime.Parse(endVisible);
var startBoundDate = string.IsNullOrEmpty(startBound) ? DateTime.MaxValue : DateTime.Parse(startBound);
var endBoundDate = string.IsNullOrEmpty(endBound) ? DateTime.MinValue : DateTime.Parse(endBound);
var startDate = startBoundDate > startVisibleDate ? startVisibleDate.AddDays(-7) : endBoundDate.AddDays(1);
var endDate = endBoundDate < endVisibleDate ? endVisibleDate.AddDays(7) : startBoundDate.AddDays(-1);
return totalList.Where(t => t.Date >= startDate && t.Date <= endDate);
}
}
}
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using DevExpress.Utils;
namespace DevExtreme.NETCore.Demos.Models.SampleData {
public static partial class SampleData {
public static string RootPath;
static readonly object _temperatureItemsLock = new object();
static IEnumerable<TemperatureItem> _temperatureItems;
public static IEnumerable<TemperatureItem> GetTemperatureRepository() {
lock(_temperatureItemsLock) {
if(_temperatureItems == null) {
var xmlContent = File.ReadAllText(Path.Combine(RootPath, "SampleData/TemperatureData.xml"));
var xml = SafeXml.CreateXDocument(xmlContent);
_temperatureItems = xml.Root.Elements("dayItem")
.Select(d => new TemperatureItem {
Date = DateTime.Parse(d.Element("date").Value, null, DateTimeStyles.RoundtripKind),
MinTemp = double.Parse(d.Element("minTemp").Value, CultureInfo.InvariantCulture),
MaxTemp = double.Parse(d.Element("maxTemp").Value, CultureInfo.InvariantCulture)
})
.ToArray();
}
return _temperatureItems;
}
}
}
}
using System;
using System.ComponentModel.DataAnnotations;
namespace DevExtreme.NETCore.Demos.Models {
public class TemperatureItem {
[Key]
public DateTime Date { get; set; }
public double MinTemp { get; set; }
public double MaxTemp { get; set; }
}
}
#chart {
height: 440px;
}