Descrizione :
Esempio pratico di associazione dati e loro rappresentazione con il controllo Chart.
+ Articolo :
Anzitutto, dal momento che di "controlli chart" in giro ce ne sono davvero tanti, voglio precisare che l'argomento in esame riguarda esclusivamente il controllo incluso in Visual Studio ( in questo caso : 2010 ), e per l'esattezza quello in Forms :
System.Windows.Forms.DataVisualization.Charting.Chart().
Ho creato questo articolo per un duplice scopo :
1. Mostrare una tecnica valida per collegare un Chart con le sue varie Serie, ad una unica fonte dati, come un DataTable.
2. Illustrare 4 diversi modi per evidenziare ciascun dato, una volta che è rappresentato graficamente nel Chart.
Oltre a questo, si noterà come diversi controlli, come le ComboBox per la scelta di ciascun Punto-Dati, vengano automaticamente messe in relazione all'atto della loro associazione con una fonte dati comune.
Per replicare l'articolo occorre una semplice Form "FormMain", con i seguenti controlli essenziali :
--> Chart : chart1
--> ComboBox : cmb_x
--> ComboBox : cmb_serie2
--> ComboBox : cmb_serie3
Le Label sono a scopo descrittivo.
Un'immagine rende bene l'idea della struttura desiderata e del funzionamento ottenuto :
--> Codice completo FormMain :
using System; using System.Collections.Generic; using System.Data; using System.Drawing; using System.Windows.Forms; using System.Windows.Forms.DataVisualization.Charting; namespace DatiControlloChart { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private Font fontLab = new Font(FontFamily.GenericSansSerif, 14, FontStyle.Regular); private DataTable DT = new DataTable("TabellaDati"); private string percorsoBmp = Application.StartupPath + "\\"; private string nomeBmp = "arrow.bmp"; private int RandomInteger(int min, int max, int seed) { return (new Random(seed)).Next(min, max); } private void Form1_Load(object sender, EventArgs e) { //Formattazione DataTable Tabella Dati per contenere due serie //Asse X comune alle Serie Dati DT.Columns.Add("X", typeof(System.Int32)); DT.PrimaryKey = new DataColumn[] {DT.Columns["X"]}; //Asse Y Serie1 DT.Columns.Add("YSerie1", typeof(System.Single)); //Asse Y Serie2 DT.Columns.Add("YSerie2", typeof(System.Int32)); //Asse Y Serie3 DT.Columns.Add("YSerie3", typeof(System.Int32)); //Asse Y Serie4 DT.Columns.Add("YSerie4", typeof(System.Int32)); //Creazione Dati ( Pseudo-Random ) for (int i = 1; i <= 20; i++) { DT.Rows.Add( new object[] { i, RandomInteger(10, 25, Convert.ToInt32((Math.Pow(i, 2)))) * 1.2, RandomInteger(25, 50, Convert.ToInt32((Math.Pow(i, 3)))), RandomInteger(50, 75, Convert.ToInt32((Math.Pow(i, 4)))), RandomInteger(75, 100, Convert.ToInt32((Math.Pow(i, 5))))}); } //Creazione Serie chart1.Series.Clear(); chart1.DataSource = DT; //Serie1 Series serie1 = new Series("Serie1"); serie1.ChartType = SeriesChartType.Column; serie1.Color = Color.Goldenrod; serie1.BorderWidth = 1; serie1.BorderColor = Color.Black; serie1.XValueMember = DT.Columns["X"].ToString(); serie1.YValueMembers = DT.Columns["YSerie1"].ToString(); chart1.Series.Add(serie1); serie1.ToolTip = serie1.Name + " [ #VALX{F0} | #VALY{F2} ]"; //Serie2 Series serie2 = new Series("Serie2"); serie2.ChartType = SeriesChartType.Line; serie2.Color = Color.Red; serie2.BorderWidth = 2; serie2.MarkerSize = 12; serie2.MarkerBorderColor = Color.Black; serie2.MarkerBorderWidth = 2; serie2.MarkerStyle = MarkerStyle.Diamond; serie2.XValueMember = DT.Columns["X"].ToString(); serie2.YValueMembers = DT.Columns["YSerie2"].ToString(); chart1.Series.Add(serie2); //Serie3 Series serie3 = new Series("Serie3"); serie3.ChartType = SeriesChartType.Line; serie3.Color = Color.SlateBlue; serie3.BorderWidth = 2; serie3.MarkerSize = 10; serie3.MarkerBorderColor = Color.Blue; serie3.MarkerBorderWidth = 2; serie3.MarkerStyle = MarkerStyle.Circle; serie3.XValueMember = DT.Columns["X"].ToString(); serie3.YValueMembers = DT.Columns["YSerie3"].ToString(); chart1.Series.Add(serie3); //Serie4 Series serie4 = new Series("Serie4"); serie4.ChartType = SeriesChartType.Line; serie4.Color = Color.Green; serie4.BorderWidth = 2; serie4.MarkerSize = 8; serie4.MarkerBorderColor = Color.DarkGreen; serie4.MarkerBorderWidth = 2; serie4.MarkerStyle = MarkerStyle.Square; serie4.XValueMember = DT.Columns["X"].ToString(); serie4.YValueMembers = DT.Columns["YSerie4"].ToString(); chart1.Series.Add(serie4); cmb_x.DropDownStyle = ComboBoxStyle.DropDownList; cmb_x.DataSource = DT; cmb_x.ValueMember = DT.Columns["X"].ToString(); cmb_x.DisplayMember = DT.Columns["X"].ToString(); cmb_serie2.DropDownStyle = ComboBoxStyle.DropDownList; cmb_serie2.DataSource = DT; cmb_serie2.ValueMember = DT.Columns["X"].ToString(); cmb_serie2.DisplayMember = DT.Columns["YSerie2"].ToString(); cmb_serie3.DropDownStyle = ComboBoxStyle.DropDownList; cmb_serie3.DataSource = DT; cmb_serie3.ValueMember = DT.Columns["X"].ToString(); cmb_serie3.DisplayMember = DT.Columns["YSerie3"].ToString(); } private void cmb_serie2_SelectedIndexChanged(object sender, EventArgs e) { var s2 = chart1.Series["Serie2"]; if (s2.Points.Count == 0) { return; } for (int i = 0; i < s2.Points.Count; i++) { s2.Points[i].Label = string.Empty; s2.Points[i].MarkerStyle = MarkerStyle.Diamond; } var pts2 = chart1.Series["Serie2"].Points[cmb_serie2.SelectedIndex]; pts2.Font = fontLab; pts2.Label = "Serie2 - " + "X = " + cmb_serie2.SelectedValue + " | Y = " + cmb_serie2.Text; pts2.LabelBackColor = Color.Gold; pts2.LabelBorderColor = Color.Black; pts2.LabelBorderWidth = 1; pts2.LabelForeColor = Color.Black; pts2.MarkerStyle = MarkerStyle.Star10; } private void cmb_serie3_SelectedIndexChanged(object sender, EventArgs e) { var s3 = chart1.Series["Serie3"]; if (s3.Points.Count == 0) { return; } for (int i = 0; i < s3.Points.Count; i++) { s3.Points[i].MarkerImage = string.Empty; } var pts3 = chart1.Series["Serie3"].Points[cmb_serie3.SelectedIndex]; pts3.MarkerImage = percorsoBmp + nomeBmp; pts3.MarkerImageTransparentColor = Color.White; } private void chart1_MouseMove(object sender, MouseEventArgs e) { HitTestResult HTR = chart1.HitTest(e.X, e.Y); //Controllo sul fatto che sia un DataPoint valido if (!(HTR.ChartElementType == ChartElementType.DataPoint)) { return; } //Controllo sul fatto che sia la Serie desiderata ( Serie4 ) if (!(HTR.Series.Name == "Serie4")) { return; } var s4 = chart1.Series["Serie4"]; for (int i = 0; i < s4.Points.Count; i++) { s4.Points[i].Label = string.Empty; s4.Points[i].MarkerStyle = MarkerStyle.Square; } var pts4 = chart1.Series["Serie4"].Points[HTR.PointIndex]; pts4.Font = fontLab; pts4.Label = "Serie4 - " + "X = " + pts4.XValue + " | Y = " + pts4.YValues[0]; pts4.LabelBackColor = Color.YellowGreen; pts4.LabelBorderColor = Color.Black; pts4.LabelBorderWidth = 1; pts4.LabelForeColor = Color.Black; pts4.MarkerStyle = MarkerStyle.Star10; } } }
NOTE :
--> Il Chart, le Serie e anche le ComboBox puntano al medesimo DataTable, chiaramente ciascuno al suo o suoi campi di pertinenza.
--> Serie1 : evidenzia il dato al passaggio del Mouse come semplice ToolTip.
--> Serie2 : evidenzia il dato ( scelto in cmb_serie2 ) con una Label. A mio avviso la scelta più completa e flessibile.
--> Serie3 : evidenzia il dato ( scelto in cmb_serie3 ) con una bitmap caricata da disco. In questo caso sarà necessario predisporre un'immagine che abbia al suo centro il Marker, e uno o più indicatori esterni. La figura seguente mostra come ho creato la mia "arrow.bmp" in 3 semplici passi, aiutandomi semplicemente con le Forme di Word 2007 :
--> Serie4 : evidenzia il dato al passaggio del Mouse con una Label. Comportamento molto simile al ToolTip della Serie1, ma molto più flessibile nella gestione.
--> Come già accennato, una selezione su ciascuna delle 3 ComboBox si propaga alle altre in automatico e senza che sia necessario estendere gli Handles nelle routine di evento, o scrivere alcun codice aggiuntivo, perchè ciò accade solo grazie alle associazioni fatte in precedenza con i Campi del DataTable.
+ Fine Articolo.
0 commenti:
Posta un commento