datagridview-sparkline-chart ソースコード全文

DataGridView_SparklineChart.slnx

DataGridView_SparklineChart.slnx
<Solution>
<Project Path="DataGridView_SparklineChart/DataGridView_SparklineChart.csproj" />
</Solution>

DataGridView_SparklineChart/DataGridView_SparklineChart.csproj

DataGridView_SparklineChart.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net10.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<UseWindowsForms>true</UseWindowsForms>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
</Project>

DataGridView_SparklineChart/DataGridView_SparklineChart.csproj.user

DataGridView_SparklineChart.csproj.user
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Compile Update="Form1.cs">
<SubType>Form</SubType>
</Compile>
</ItemGroup>
</Project>

DataGridView_SparklineChart/Form1.cs

Form1.cs
using System.ComponentModel;
namespace DataGridView_SparklineChart
{
public partial class Form1 : Form
{
private BindingList<ProductSales> _products = new BindingList<ProductSales>();
public Form1()
{
InitializeComponent();
InitializeDataGridView();
}
private void InitializeDataGridView()
{
// テスト用データの準備(過去6ヶ月の売上推移データを含む)
_products.Add(new ProductSales("P001", "ノートパソコン", new List<int> { 10, 15, 8, 20, 25, 30 }));
_products.Add(new ProductSales("P002", "USBメモリ", new List<int> { 50, 45, 48, 30, 20, 10 }));
_products.Add(new ProductSales("P003", "周辺機器", new List<int> { 12, 12, 14, 13, 15, 14 }));
// グリッドにデータソースを設定
dataGridView1.DataSource = _products;
// グラフ描画用の非データバインド列を末尾に追加
var graphCol = new DataGridViewTextBoxColumn();
graphCol.Name = "SparklineCombo";
graphCol.HeaderText = "売上推移(6ヶ月)";
dataGridView1.Columns.Add(graphCol);
// グラフが見やすくなるように、行の高さと幅を調整
dataGridView1.RowTemplate.Height = 40;
dataGridView1.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
// セル描画イベントを購読
dataGridView1.CellPainting += DataGridView1_CellPainting;
}
/// <summary>
/// セルの描画が行われるときに発生するイベント
/// </summary>
private void DataGridView1_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
{
// e.ColumnIndex >= 0 の判定を追加し、行ヘッダー描画時のエラーを防ぎます
if (e.RowIndex >= 0 && e.ColumnIndex >= 0 && dataGridView1.Columns[e.ColumnIndex].Name == "SparklineCombo")
{
// 1. 背景や選択状態の枠線など、標準の背景を先に描画する
e.Paint(e.CellBounds, DataGridViewPaintParts.All & ~DataGridViewPaintParts.ContentForeground);
// 2. 行のデータプロパティからグラフ用の数値リストを取得
if (dataGridView1.Rows[e.RowIndex].DataBoundItem is ProductSales product && product.MonthlySales.Count > 1)
{
var salesData = product.MonthlySales;
// 3. 描画領域(セル内の余白を考慮したサイズ)の計算
int paddingX = 6;
int paddingY = 6;
int graphLeft = e.CellBounds.Left + paddingX;
int graphTop = e.CellBounds.Top + paddingY;
int graphWidth = e.CellBounds.Width - (paddingX * 2);
int graphHeight = e.CellBounds.Height - (paddingY * 2);
// 4. データ内の最大値と最小値を特定(セルの高さに収めるスケーリングのため)
int maxVal = salesData.Max();
int minVal = salesData.Min();
int valRange = maxVal - minVal;
if (valRange == 0) valRange = 1; // すべて同値の場合のゼロ除算防止
// 5. 各データ点に対応するセル内の座標(PointF)を計算
var points = new PointF[salesData.Count];
float xStep = (float)graphWidth / (salesData.Count - 1);
for (int i = 0; i < salesData.Count; i++)
{
float x = graphLeft + (i * xStep);
// 値が大きいほど画面上では「上(Y座標が小さい方向)」になるよう計算
float y = graphTop + graphHeight - ((float)(salesData[i] - minVal) / valRange * graphHeight);
points[i] = new PointF(x, y);
}
// 6. 計算した座標を結ぶ折れ線を描画(アンチエイリアスで線を滑らかにします)
var oldMode = e.Graphics.SmoothingMode;
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
// システム共通の青いペンを使用して描画
e.Graphics.DrawLines(Pens.DodgerBlue, points);
// 描画モードを元に戻す
e.Graphics.SmoothingMode = oldMode;
}
// 7. システム側の標準テキスト描画処理をスキップさせる
e.Handled = true;
}
}
}
/// <summary>
/// 各行のデータを表すモデルクラス
/// </summary>
public class ProductSales
{
public string Code { get; set; }
public string Name { get; set; }
// グラフの元データとなる数値リスト(グリッドには自動生成されないように指定)
[Browsable(false)]
public List<int> MonthlySales { get; set; }
public ProductSales(string code, string name, List<int> monthlySales)
{
Code = code;
Name = name;
MonthlySales = monthlySales;
}
}
}

DataGridView_SparklineChart/Form1.Designer.cs

Form1.Designer.cs
namespace DataGridView_SparklineChart
{
partial class Form1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
dataGridView1 = new DataGridView();
((System.ComponentModel.ISupportInitialize)dataGridView1).BeginInit();
SuspendLayout();
//
// dataGridView1
//
dataGridView1.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize;
dataGridView1.Dock = DockStyle.Fill;
dataGridView1.Location = new Point(0, 0);
dataGridView1.Name = "dataGridView1";
dataGridView1.RowHeadersWidth = 62;
dataGridView1.Size = new Size(800, 450);
dataGridView1.TabIndex = 0;
//
// Form1
//
AutoScaleDimensions = new SizeF(10F, 25F);
AutoScaleMode = AutoScaleMode.Font;
ClientSize = new Size(800, 450);
Controls.Add(dataGridView1);
Name = "Form1";
Text = "Form1";
((System.ComponentModel.ISupportInitialize)dataGridView1).EndInit();
ResumeLayout(false);
}
#endregion
private DataGridView dataGridView1;
}
}

DataGridView_SparklineChart/Form1.resx

Form1.resx
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

DataGridView_SparklineChart/Program.cs

Program.cs
namespace DataGridView_SparklineChart
{
internal static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
// To customize application configuration such as set high DPI settings or default font,
// see https://aka.ms/applicationconfiguration.
ApplicationConfiguration.Initialize();
Application.Run(new Form1());
}
}
}