Make sure to download the Microsoft
Chart Controls for Microsoft .NET Framework 3.5 because the Chart controls
required System.Web.DataVisualization.Design.dll and System.Web.DataVisualization.dll. I also included both the dlls
in the sample code. Some of the codes in this section are from Samples
Environment for Chart Controls. First, let create a method to bind the data
source to the Column Chart. This method accepts two parameters, start and end
range values. Then use LINQ to query the CarsList.xml
data source and find all the records where YearMonth between
start and end range values. Group the result by car brands, stores it in lstCarsnTotal and bind it to the chart. See listing 7.
Listing 7
void PopulateChart(int start, int end)
{
List<CarsList> lstCarsnTotal = new List<CarsList>();
XDocument xmlDoc = XDocument.Load(Server.MapPath(Utilities.Instance.CarsListXMLPath));
lstCarsnTotal = (from c in xmlDoc.Descendants("Car")
where (int)c.Attribute("YearMonth") >= GetRange(start)
&& (int)c.Attribute("YearMonth") <= GetRange(end)
group c by (string)c.Attribute("CarBrand") into g
select new CarsList
{
CarCount = g.Sum(c => (int)c.Attribute("Count")),
CarBrand = g.Key
}).ToList();
Chart1.Series["Default"].ChartType = SeriesChartType.Column;
Chart1.Series["Default"].Points.DataBindXY(lstCarsnTotal, "CarBrand",
lstCarsnTotal, "CarCount");
}
//return YearMonth
protected static int GetRange(int rn)
{
IEnumerable<Range> rangeText;
rangeText = lstSliderRange.Where(r => r.RowNumber == rn)
.Select(r => new Range
{
Year = r.Year,
Month = r.Month
});
return int.Parse(rangeText.First().Year + rangeText.First().Month);
}
Now, when the user click on the column chart, a GridView
will appears next to it. The PopulateGrid method
takes the car brand as an argument. Then use LINQ to query the SliderRange.xml data source where YearMonth
between the selected range values and CarBrand equal
to the selected car brand. See listing 8.
Listing 8
protected void Chart1_Click(object sender, ImageMapEventArgs e)
{
if (!GridView1.Visible)
{
GridView1.Visible = true;
}
//kept track of selected car type
ChartPostBackValue.Value = e.PostBackValue;
lblCarBrand.Text = "Car Brand: " + e.PostBackValue;
PopulateGrid(e.PostBackValue);
PopulateChart(int.Parse(rangeStart.Value), int.Parse(rangeEnd.Value));
}
void PopulateGrid(string strPostBavkVal)
{
List<CarsList> lstCarsnTotal = new List<CarsList>();
XDocument xmlDoc = XDocument.Load(Server.MapPath(Utilities.Instance.CarsListXMLPath));
lstCarsnTotal = (from c in xmlDoc.Descendants("Car")
where (int)c.Attribute("YearMonth") >=
GetRange(int.Parse(rangeStart.Value))
&& (int)c.Attribute("YearMonth") <=
GetRange(int.Parse(rangeEnd.Value)) && (string)c.Attribute("CarBrand") == strPostBavkVal
select new CarsList
{
CarCount = (int)c.Attribute("Count"),
CarBrand = (string)c.Attribute("CarBrand"),
Date = (string)c.Attribute("Date")
}).ToList();
GridView1.DataSource = lstCarsnTotal;
GridView1.DataBind();
}
Known Issue
On design time, we will see the error "MultiHandleSliderExtender could not be set on property MultiHandleSliderTargets" but code work fine at run
time. I have downloaded the example and latest version of Ajax Control Toolkit
from CodePlex but didn't solve the
problem. A workaround is adding the TagPrefix next to
the MultiHandleSliderTargets tag during design time
and remove it at run time. Hopefully someone can shed some light on this.
Point of Interest
The Default2.aspx in the sample code includes a master page.
If you use a master page, make sure to use the Control.ClientID.
For some reason the client __doPostBack function do
not work with master page, the work around was to call the button click event. See listing 9. Hopefully someone can shed some
light on this too.
Listing 9
<script type="text/javascript">
var isDragging = false;
function Drag(sender, args) {
GetSliderRange($get("<%= rangeStart.ClientID %>").value,
$get("<%= rangeEnd.ClientID%>").value);
}
function DragEnd(sender, args) {
//prevent postback on slider click
if ($get("<%= hdfTrackRangeStart.ClientID %>").value !==
$get("<%= rangeStart.ClientID %>").value) {
$get("<%= btnLoadChart.ClientID %>").click();
//__doPostBack("<%= btnLoadChart.ClientID %>", "");
}
if ($get("<%= hdfTrackRangeEnd.ClientID %>").value !==
$get("<%= rangeEnd.ClientID %>").value &&
$get("<%= hdfTrackRangeEnd.ClientID %>").value !== '0') {
$get("<%= btnLoadChart.ClientID %>").click();
//__doPostBack("<%= btnLoadChart.ClientID %>", "");
}
}
function GetSliderRange(startV, endV) {
PageMethods.SliderRange(startV, endV, this.callback);
}
function callback(result) {
var arrResult = result.split("--");
$get("<%= lblStartRange.ClientID %>").innerHTML = arrResult[0];
$get("<%= lblEndRange.ClientID %>").innerHTML = arrResult[1];
}
</script>
I have noticed that, clicking on the handle will trigger the
DragEnd function and cause unnecessary post back. To
remedy this problem, compare the old selected range value and the current selected
range value, if they are not equal then permit the call of client-side __doPostBack function. See listing 10.
Listing 10
function DragEnd(sender, args) {
//prevent postback on slider click
if ($get("hdfTrackRangeStart").value !== $get("rangeStart").value) {
__doPostBack("btnLoadChart", "");
}
if ($get("hdfTrackRangeEnd").value !== $get("rangeEnd").value &&
$get("hdfTrackRangeEnd").value !== '0') {
__doPostBack("btnLoadChart", "");
}
}
The chart displayed correctly on my local machine but it
displayed a sequence of strange characters in the hosting environment. After
doing some research, I discovered that I didn't set appropriate permissions on
the storage folder and EnableSessionState on the page
directive was set to false. The list of ASP.NET Charts storage mode is
available here.