XY charts (also known as scatter plots) are used to show relationships between variables. Unlike the charts we introduced so far, in XY charts each point has two numeric values. By plotting one of the values against the X axis and one against the Y axis, the charts show the effect of one variable on the other.
Before we can create any charts, we need to generate the data that will be shown as a chart. Here is some code to create the data we need.
Note: There is nothing chart-specific in this code, this is just some generic data. We will use this data to create the Time Series and XY charts as well in the next topics.
•C#
TextBlock CreateTextBlock(string text, double fontSize, FontWeight fontWeight)
{
var tb = new TextBlock();
tb.Text = text;
tb.FontSize = fontSize;
tb.FontWeight = fontWeight;
return tb;
}
// Simple class to hold dummy sales data
public class SalesRecord
{
// Properties
public string Region { get; set; }
public string Product { get; set; }
public DateTime Date { get; set; }
public double Revenue { get; set; }
public double Expense { get; set; }
public double Profit { get { return Revenue - Expense; } }
// Constructor 1
public SalesRecord(string region, double revenue, double expense)
{
Region = region;
Revenue = revenue;
Expense = expense;
}
// Constructor 2
public SalesRecord(DateTime month, string product, double revenue, double expense)
{
Date = month;
Product = product;
Revenue = revenue;
Expense = expense;
}
}
// Return a list with one SalesRecord for each region
List<SalesRecord> GetSalesPerRegionData()
{
var data = new List<SalesRecord>();
Random rnd = new Random(0);
foreach (string region in "North,East,West,South".Split(','))
{
data.Add(new SalesRecord(region, 100 + rnd.Next(1500), rnd.Next(500)));
}
return data;
}
// Return a list with one SalesRecord for each product,// Over a period of 12 months
List<SalesRecord> GetSalesPerMonthData()
{
var data = new List<SalesRecord>();
Random rnd = new Random(0);
string[] products = new string[] {"Widgets", "Gadgets", "Sprockets" };
for (int i = 0; i < 12; i++)
{
foreach (string product in products)
{
data.Add(new SalesRecord(
DateTime.Today.AddMonths(i - 24),
product,
rnd.NextDouble() * 1000 * i,
rnd.NextDouble() * 1000 * i));
}
}
return data;
}
Note that the SalesData class is public. This is required for data-binding.
We will continue our C1Chart tour using the same data we created earlier, but this time we will create XY charts that show the relationship between revenues from two products. For example, we might want to determine whether high Widget revenues are linked to high Gadgets revenues (perhaps the products work well together), or whether high Widget revenues are linked to low Gadgets revenues (perhaps people who buy one of the products don't really need the other).
To do this, we will follow the same steps as before. The main differences are that this time we will add XYDataSeries objects to the chart's Data.Children collection instead of the simpler DataSeries objects. The Linq statement used to obtain the data is also a little more refined and interesting.
Step 1) Choose the chart type:
The code clears any
existing series, then sets the chart type:
•C#
public MainPage()
{
InitializeComponent();
// Clear current chart
c1Chart1.Reset(true);
// Set chart type
c1Chart1.ChartType = ChartType.XYPlot;
}
Step 2) Set up the axes:
Since we're now creating
XY series, we have two value axes (before we had a label axis and a value axis).
We will attach titles and formats to both axes as we did before. We will also
set the scale and annotation format as before. We will also use the AnnoAngle
property to rotate the annotation labels along the X axis so they don't
overlap:
•C#
// get axes
var yAxis = c1Chart1.View.AxisY;
var xAxis = c1Chart1.View.AxisX;
// configure Y axis
yAxis.Title = CreateTextBlock("Widget Revenues", 14, FontWeights.Bold);
yAxis.AnnoFormat = "#,##0 ";
yAxis.AutoMin = false;
yAxis.Min = 0;
yAxis.MajorUnit = 2000;
yAxis.AnnoAngle = 0;
// configure X axis
xAxis.Title = CreateTextBlock("Gadget Revenues", 14, FontWeights.Bold);
xAxis.AnnoFormat = "#,##0 ";
xAxis.AutoMin = false;
xAxis.Min = 0;
xAxis.MajorUnit = 2000;
xAxis.AnnoAngle = -90; // rotate annotations
Step 3) Add one or more data
series
Once again, we will use the second
data-provider method defined earlier:
•C#
// get the data
var data = GetSalesPerMonthData();
Next, we need to obtain XY pairs that correspond to the total revenues for Widgets and Gadgets at each date. We can use Linq to obtain this information directly from our data:
•C#
// group data by sales date
var dataGrouped = from r in data
group r by r.Date into g
select new
{
Date = g.Key, // group by date
Widgets = (from rp in g // add Widget revenues
where rp.Product == "Widgets"
select g.Sum(p => rp.Revenue)).Single(),
Gadgets = (from rp in g // add Gadget revenues
where rp.Product == "Gadgets"
select g.Sum(p => rp.Revenue)).Single(),
};
// sort data by widget sales
var dataSorted = from r in dataGrouped
orderby r.Gadgets
select r;
The first LINQ query starts by grouping the data by Date. Then, for each group it creates a record containing the Date and the sum of revenues within that date for each of the products we are interested in. The result is a list of objects with three properties: Date, Widgets, and Gadgets. This type of data grouping and aggregation is a powerful feature of LINQ.
The second Linq query simply sorts the data by Gadget revenue. These are the values that will be plotted on the X axis, and we want them to be in ascending order. Plotting unsorted values would look fine if we displayed only symbols (ChartType = XYPlot), but it would look messy if we chose other chart types such as Line or Area.
Once the data has been properly grouped, summarized, and sorted, all we need to do is create one single data series, and assign one set of values to the ValuesSource property and the to the XValuesSource property:
•C#
// create the new XYDataSeries
var ds = new XYDataSeries();
// set series label (displayed in a C1ChartLegend)
ds.Label = "Revenue:\r\nWidgets vs Gadgets";
// populate Y values
ds.ValuesSource = (
from r in dataSorted
select r.Widgets).ToArray();
// populate X values
ds.XValuesSource = (
from r in dataSorted
select r.Gadgets).ToArray();
// add the series to the chart
c1Chart1.Data.Children.Add(ds);
You can test it by running the program and changing the ChartType property to XYPlot, LineSymbols, or Area to create charts of different types. The most appropriate chart type in this case is the first, an XYPlot. The chart shows a positive correlation between Gadget and Widget revenues.
This concludes the basic charting topic. You already have the tools you need to create all types of common charts.