Alternatively, you can manually draw such a heat map to the background of the Chart control.
Here is an example:
First, handle the event PrePaint.
chart.PrePaint += ChartPrePaint;
The method is then executed for deferent elements of the chart, such as Title, Legend, etc. Use the property ChartElement to filter the element of type ChartArea.
private void ChartPrePaint(object sender, ChartPaintEventArgs e)
{
var area = e.ChartElement as ChartArea;
if (area == null)
{
return;
}
}
Next step is to split the chart area into rectangular segments and fill each segment with its own color depending on the settings.
private void ChartPrePaint(object sender, ChartPaintEventArgs e)
{
var chart = sender as Chart;
if (chart == null)
{
return;
}
var width = chart.Width;
var height = chart.Height;
var area = e.ChartElement as ChartArea;
if (area == null)
{
return;
}
/*
* In this example we plot the average temperature of each period of a day (Y-axis) for each month (X-axis).
*/
foreach (var month in Observations)
{
for (var period = 0; period < month.Value.Length; period++)
{
/*
* Firstly, find where each segment begins and ends.
* In this example we split the X-axis proportionally to the number of months and find the edges of each month.
*/
var x1 = e.ChartGraphics.GetPositionFromAxis(area.Name, area.AxisX.AxisName, month.Key);
var x2 = e.ChartGraphics.GetPositionFromAxis(area.Name, area.AxisX.AxisName, month.Key + 1);
/*
* Similarly, split the Y-axis into 4 segments for each period of a day.
*/
var y1 = e.ChartGraphics.GetPositionFromAxis(area.Name, area.AxisY.AxisName, period);
var y2 = e.ChartGraphics.GetPositionFromAxis(area.Name, area.AxisY.AxisName, period + 1);
/*
* Convert the relative coordinates to absolute ones.
*/
var x1abs = (float)(x1 / 100 * width);
var x2abs = (float)((x2 - x1) / 100 * width);
var y1abs = (float)(y2 / 100 * height);
var y2abs = (float)((y1 - y2) / 100 * height);
/*
* Finally, pick the color of the segment.
*/
var temperature = month.Value[period];
var color = GetColor(temperature);
using (var brush = new SolidBrush(color))
{
/*
* Fill the segment with its color.
*/
e.ChartGraphics.Graphics.FillRectangle(brush, x1abs, y1abs, x2abs, y2abs);
}
}
}
}

Here is the full version:
using System.Collections.Generic;
using System.Drawing;
using System.Globalization;
using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;
namespace WindowsFormsAppHeatMap
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
var chart = new Chart { Dock = DockStyle.Fill };
chart.PrePaint += ChartPrePaint;
Controls.Add(chart);
var area = new ChartArea();
chart.ChartAreas.Add(area);
/* Add data to the chart. */
var series = new Series() { ChartType = SeriesChartType.Point };
series.MarkerSize = 0;
foreach (var month in Observations)
{
for (var i = 0; i < month.Value.Length; i++)
{
var index = series.Points.AddXY(month.Key + 0.5, i + 0.5);
series.Points[index].Label = month.Value[i].ToString();
}
}
chart.Series.Add(series);
/* Custom labels for the X-axis. */
area.AxisX.Minimum = 0;
area.AxisX.Maximum = 12;
area.AxisX.Interval = 1;
var ci = CultureInfo.InvariantCulture;
for (var i = 0; i < 12; i++)
{
var month = ci.DateTimeFormat.GetMonthName(i + 1).Substring(0, 3);
area.AxisX.CustomLabels.Add(i, i + 1, month);
}
/* Custom labels for the Y-axis. */
area.AxisY.Minimum = 0;
area.AxisY.Maximum = 4;
area.AxisY.Interval = 1;
area.AxisY.CustomLabels.Add(0, 1, "Morning");
area.AxisY.CustomLabels.Add(1, 2, "Afternoon");
area.AxisY.CustomLabels.Add(2, 3, "Evening");
area.AxisY.CustomLabels.Add(3, 4, "Night");
}
/// <summary>
/// Input data for each month and each period of a day (Morning, Afternoon, Evening, Night).
/// </summary>
private readonly Dictionary<int, int[]> Observations = new Dictionary<int, int[]>
{
{ 0, new [] { -15, -10, -10, -18 } },
{ 1, new [] { -18, -12, -13, -20 } },
{ 2, new [] { -14, -9, -8, -15 } },
{ 3, new [] { -5, -2, -4, -7 } },
{ 4, new [] { -1, 5, 2, -2 } },
{ 5, new [] { 14, 22, 20, 13 } },
{ 6, new [] { 18, 30, 24, 19 } },
{ 7, new [] { 17, 28, 22, 17 } },
{ 8 ,new [] { 10, 13, 11, 8 } },
{ 9, new [] { 5, 10, 6, 3 } },
{ 10, new [] { -2, 3, -1, -5 } },
{ 11, new [] { -7, -5, -8, -10 } }
};
/// <summary>
/// Colors map that states the color for each temperature range.
/// </summary>
private readonly Dictionary<int, Color> Colors = new Dictionary<int, Color>
{
{ -35, Color.FromArgb( 0, 0, 255) },
{ -30, Color.FromArgb(40, 67, 255) },
{ -25, Color.FromArgb(53, 115, 255) },
{ -20, Color.FromArgb(53, 168, 255) },
{ -15, Color.FromArgb(40, 220, 254) },
{ -10, Color.FromArgb(64, 255, 240) },
{ -5, Color.FromArgb(144, 244, 194) },
{ 0, Color.FromArgb(183, 254, 140) },
{ 5, Color.FromArgb(219, 254, 92) },
{ 10, Color.FromArgb(249, 255, 16) },
{ 15, Color.FromArgb(255, 225, 0) },
{ 20, Color.FromArgb(255, 178, 0) },
{ 25, Color.FromArgb(255, 133, 0) },
{ 30, Color.FromArgb(255, 80, 0) },
{ 99, Color.FromArgb(179, 0, 0) }
};
/// <summary>
/// Returns the color for the specified temperature.
/// </summary>
/// <param name="temperature">A temperature.</param>
/// <returns>A color.</returns>
private Color GetColor(int temperature)
{
foreach (var color in Colors)
{
if (temperature < color.Key)
{
return color.Value;
}
}
return Color.Transparent;
}
/// <summary>
/// Draws the colorful segments in the chart area.
/// </summary>
private void ChartPrePaint(object sender, ChartPaintEventArgs e)
{
var chart = sender as Chart;
if (chart == null)
{
return;
}
var width = chart.Width;
var height = chart.Height;
var area = e.ChartElement as ChartArea;
if (area == null)
{
return;
}
/*
* In this example we plot the average temperature of each period of a day (Y-axis) for each month (X-axis).
*/
foreach (var month in Observations)
{
for (var period = 0; period < month.Value.Length; period++)
{
/*
* Firstly, find where each segment begins and ends.
* In this example we split the X-axis proportionally to the number of months and find the edges of each month.
*/
var x1 = e.ChartGraphics.GetPositionFromAxis(area.Name, area.AxisX.AxisName, month.Key);
var x2 = e.ChartGraphics.GetPositionFromAxis(area.Name, area.AxisX.AxisName, month.Key + 1);
/*
* Similarly, split the Y-axis into 4 segments for each period of a day.
*/
var y1 = e.ChartGraphics.GetPositionFromAxis(area.Name, area.AxisY.AxisName, period);
var y2 = e.ChartGraphics.GetPositionFromAxis(area.Name, area.AxisY.AxisName, period + 1);
/*
* Convert the relative coordinates to absolute ones.
*/
var x1abs = (float)(x1 / 100 * width);
var x2abs = (float)((x2 - x1) / 100 * width);
var y1abs = (float)(y2 / 100 * height);
var y2abs = (float)((y1 - y2) / 100 * height);
/*
* Finally, pick the color of the segment.
*/
var temperature = month.Value[period];
var color = GetColor(temperature);
using (var brush = new SolidBrush(color))
{
/*
* Fill the segment with its color.
*/
e.ChartGraphics.Graphics.FillRectangle(brush, x1abs, y1abs, x2abs, y2abs);
}
}
}
}
}
}