Отображение HeatMap в DataGridView из списка<List <T >> в C#

У меня есть некоторые данные в Списке Трупов. Моя задача состоит в том, чтобы сделать тепловую карту из этого. Будучи новичком в C#, я искал в сети и нашел способ решения этой задачи, рисуя элементы в DataGridView, но я не понимаю, как это сделать. Итак, у меня есть список Touples:

 using SpikeDataPacket = List<Tuple<double, double>>;

Вот как я загружаю данные в сетку:

public HeatForm(List<SpikeDataPacket> list)
{
  SpikeList = list;
  InitializeComponent();
  var bindstim = new BindingList<SpikeDataPacket>(SpikeList);
  var stimsource = new BindingSource(bindstim, null);
  heatMap.DataSource = stimsource;
}

Но это отображает таблицу с "емкостью" и "счетчиком" внутри DataGridView, но не с данными. Также я нашел способ подсчитать цвет, но не знаю, как его применить:

private Color HeatMapColor(double value, double min, double max)
{
  Color firstColour = Color.RoyalBlue;
  Color secondColour = Color.LightSkyBlue;

  // Example: Take the RGB
  //135-206-250 // Light Sky Blue
  // 65-105-225 // Royal Blue
// 70-101-25 // Delta

int rOffset = Math.Max(firstColour.R, secondColour.R);
int gOffset = Math.Max(firstColour.G, secondColour.G);
int bOffset = Math.Max(firstColour.B, secondColour.B);

int deltaR = Math.Abs(firstColour.R - secondColour.R);
int deltaG = Math.Abs(firstColour.G - secondColour.G);
int deltaB = Math.Abs(firstColour.B - secondColour.B);

double val = (value - min) / (max - min);
int r = rOffset - Convert.ToByte(deltaR * (1 - val));
int g = gOffset - Convert.ToByte(deltaG * (1 - val));
int b = bOffset - Convert.ToByte(deltaB * (1 - val));        

  return Color.FromArgb(255, r, g, b);
}

Заранее спасибо!

2 ответа

Решение

Думаю, я бы решил проблему несколько иначе.

  • Я бы начал без использования DataBinding, Ни структура List of Tuple, ни отображение двойного на Color хорошо поддается DataBinding,
  • Я также не уверен в вашем алгоритме цветового картографирования.

Чтобы заполнить данные в DGV DataGridView, я использую простую процедуру, которая сначала подготавливает DGV, а затем раскрашивает ячейки:

void fillData()
{
    int maxRow = data.Count;
    int maxCol = data[0].Count;
    double factor = 1.0;

    DGV.RowHeadersVisible = false;
    DGV.ColumnHeadersVisible = false;
    DGV.AllowUserToAddRows = false;
    DGV.AllowUserToOrderColumns = false;
    DGV.CellBorderStyle = DataGridViewCellBorderStyle.None;
    //..

    int rowHeight = DGV.ClientSize.Height / maxRow - 1;
    int colWidth = DGV.ClientSize.Width / maxCol - 1;

    for (int c = 0; c < maxRow; c++) DGV.Columns.Add(c.ToString(), "");
    for (int c = 0; c < maxRow; c++) DGV.Columns[c].Width = colWidth;
    DGV.Rows.Add(maxRow);
    for (int r = 0; r < maxRow; r++) DGV.Rows[r].Height = rowHeight;

    List<Color> baseColors = new List<Color>();  // create a color list
    baseColors.Add(Color.RoyalBlue);
    baseColors.Add(Color.LightSkyBlue);
    baseColors.Add(Color.LightGreen);
    baseColors.Add(Color.Yellow);
    baseColors.Add(Color.Orange);
    baseColors.Add(Color.Red);
    List<Color> colors = interpolateColors(baseColors, 1000);

    for (int r = 0; r < maxRow; r++)
    {
        for (int c = 0; c < maxRow; c++)
        {
            DGV[r,c].Style.BackColor = 
                           colors[ Convert.ToInt16( data[r][c].Item2 * factor)];

        }
    }

}

Вам бы хотелось изменить несколько вещей, особенно базовые цвета и количество цветов, которые вы хотите получить, в зависимости от ваших значений, а также от двойного значения до целочисленного индекса!

Вот функция для создания списка интерполированных цветов. Он принимает несколько базовых цветов и длину N и возвращает N интерполированных цветов. Это делает отображение простым и гибким.

List<Color> interpolateColors(List<Color> stopColors, int count)
{
    SortedDictionary<float, Color> gradient = new SortedDictionary<float, Color>();
    for (int i = 0; i < stopColors.Count; i++) 
        gradient.Add(1f * i / (stopColors.Count-1), stopColors[i]);
    List<Color> ColorList = new List<Color>();

    using (Bitmap bmp = new Bitmap(count, 1))
    using (Graphics G = Graphics.FromImage(bmp))
    {
        Rectangle bmpCRect = new Rectangle(Point.Empty, bmp.Size);
        LinearGradientBrush br = new LinearGradientBrush
                                (bmpCRect, Color.Empty, Color.Empty, 0, false);
        ColorBlend cb = new ColorBlend();
        cb.Positions = new float[gradient.Count];
        for (int i = 0; i < gradient.Count; i++) 
            cb.Positions[i] = gradient.ElementAt(i).Key;
        cb.Colors = gradient.Values.ToArray();
        br.InterpolationColors = cb;
        G.FillRectangle(br, bmpCRect);
        for (int i = 0; i < count; i++) ColorList.Add(bmp.GetPixel(i, 0));
        br.Dispose();
    }
    return ColorList;
}

Мои тестовые данные были созданы так:

List<List<Tuple<double,double>>> data = new List<List<Tuple<double,double>>>();

Random R = new Random();

void createData(int maxRow, int maxCol)
{
    for (int c = 0; c < maxRow; c++)
    {
        data.Add(new List<Tuple<double, double>>());
        for (int r = 0; r < maxRow; r++)
        {
            data[c].Add(new Tuple<double, double>(c, Math.Min(999, R.Next(r*c))));
        }
    }
}

и я использовал это так:

private void Form1_Load(object sender, EventArgs e)
{
    createData(40, 40);
    fillData();
}

Вот (довольно скучный) скриншот:

Тепловая карта

После того, как вы получили дисплей, как вы хотите, вы можете выбрать решение с DataBinding, Я считаю, что вам нужно будет использовать OwnerDrawing Cells, Когда у вас есть значения в ячейках, вы можете использовать одно и то же отображение просто так:

private void DGV_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
{
   Color theColor = .....
   e.Graphics.Clear(colors[theColor]);
}

Вот простой способ раскрасить предметы:

Наш объект ссылки:

internal class Item
{
    public string Name { get; set; }
    public double Value { get; set; }
}

Использование:

  • вставьте код и пересоберите свой проект
  • перейдите на панель Источники данных и создайте новый источник данных
  • выберите этот тип объекта и нажмите Готово
  • перетащите созданный источник в ваш (он создает DataGridView)

Окраска клеток:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        // create some items
        itemBindingSource.Add(new Item {Name = "name1", Value = 0});
        itemBindingSource.Add(new Item {Name = "name2", Value = 0.5});
        itemBindingSource.Add(new Item {Name = "name3", Value = 1});

        // find column index to color
        string columnName = "Value";
        int columnIndex = -1;
        DataGridViewColumnCollection columns = itemDataGridView.Columns;
        for (int i = 0; i < columns.Count; i++)
        {
            DataGridViewColumn column = columns[i];
            if (column.DataPropertyName == columnName)
            {
                columnIndex = i;
                break;
            }
        }

        // color cells
        if (columnIndex >= 0)
        {
            foreach (DataGridViewRow row in itemDataGridView.Rows)
            {
                if (!row.IsNewRow)
                {
                    // get associated data
                    var item = (Item) row.DataBoundItem;
                    // build color from associated data
                    Color fromArgb = Color.FromArgb((int) (item.Value*255), 128, 128);
                    row.Cells[columnIndex].Style.BackColor = fromArgb;
                }
            }
        }
    }
}

Сверните свою собственную раскрашивающую логику,

Другие вопросы по тегам