Press "Enter" to skip to content

Bitmap, GDI+,Collezioni,Classi – Simulare un display a Led

Una versione diversa della piccola applicazione per la costruzione di un display a Led già spiegata da Alberto per costruire una serie di classi che ci permettono di generare dei bitmap, disegnarvi delle forme all’interno usando colori e pennelli messi a disposizione dalla GDI+

Introduzione

Anche questo progettino, che avevo preparato credo quasi un anno or sono, ma per il quale non avevo mai avuto occasione di scrivere un articolo, deriva da un articolo di Alberto, precisamente quello per la costruzione di un simulatore di display a Led.

In questo caso, non ci concentriamo sulla funzionalità del display a Led ma sull’uso delle funzionalità grafiche fornite dalla GDI+ alle winforms per generare dei Bitmap, disegnarvi delle forme e colorarle come ci pare.

Ciò che vogliamo ottenere è il seguente risultato:

Il modello base, simile a quello di Alberto:

sc_dnwledscs01

Un esempio di modifica usando i 3 bottoni di scelta dei colori:

sc_dnwledscs02

Per ottenere tutto questo, abbiamo predisposto una Soluzione che contiene 2 progetti:

DnwLedsCs -> una libreria di classi che contiene gli oggetti che forniscono i disegni.

DnwLedsTest -> una applicazione che usa la nostra classe per produrre la form sopra disegnata.

Le classi

Per questo progetto creeremo le seguenti classi:

  • Letter = Classe dati contiene le informazioni per generare una lettera.
  • Letters = Collezione di oggetti Letter, ci permette di memorizzare una parola o una frase.
  • LetterBuilder = Classe Statica per la creazione di tutte le lettere dell’alfabeto.
  • LedDisplay = Classe per generare il bitmap relativo ad una lettera.
  • FrmDisplay = Form per il test delle classi generate.

Letter

E’ una classe molto semplice che contiene solo due campi, una stringa in cui inserire la lettera rappresentata e un array di Boolean ove inserire la rappresentazione dei punti accesi o spenti che rappresentano la lettera stessa.

{

	private bool[] mLeds;
 

	private string mLettera;
 

	public Letter()
	{
		this.mLettera = string.Empty;
		this.mLeds = new bool[35];
	}
 

	public bool[] Leds
	{
		get
		{
			return mLeds;
		}
		set
		{
			mLeds = value;
		}
	}
 
	public string Lettera
	{
		get
		{
			return mLettera;
		}
		set
		{
			mLettera = value;
		}
	}
 
}

Letters

Un’altra semplicissima classe derivata da una Generic List che contiene una collezione di classi Letter, verrà usata come contenitore per generare tutte le lettere dell’alfabeto e la punteggiatura.

public class Letters : List<Letter>
{
	#region Public Indexers
 
	///<summary>
	/// Indexer per lettera
	///</summary>
	public Letter this[string letter]
	{
		get
		{
			return this.FirstOrDefault(x => x.Lettera == letter);
		}
	}
 
	#endregion Public Indexers
}

Questa collezione, ereditata come detto da Generic List, contiene una sola implementazione, un indexer che permette di recuperare un elemento della collezione conoscendo il contenuto del campo Letter quindi la lettera che contiene.
Faccio notare come entrambe le classi siano state implementate con forma minimale, senza inserire controlli di alcun tipo (ad esempio che letter non possa essere più lungo di un carattere ed altri controlli che si poteva effettuare proprio perché lo scopo di questo progettino è illustrare la semplicità d’uso delle funzioni grafiche e non le entità e le collezioni che sono invece spiegate in più di qualcuno degli articoli già pubblicati.)

LetterBuilder

Questa è una classe Statica che genera e popola una collezione di tutte le lettere dell’alfabeto rendendole disponibili al programma che la usa per poter poi generare i caratteri a Led.

public static class LetterBuilder
{
	private static Letters mLettere;
 
	static LetterBuilder()
	{
		mLettere = new Letters();
		string[] rows = Properties.Resources.txtLetters.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
		for (int i = 0; i < rows.Length; i++)
		{
			string[] letter = rows[i].Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
			Letter ltt = new Letter();
			if (letter.Length > 0)
			{
				ltt.Lettera = letter[0];
				for (int j = 1; j < letter.Length; j++)
				{
					int val = int.Parse(letter[j]);
					ltt.Leds[val] = true;
				}
			}
			else
			{
				ltt.Lettera = " ";
			}
			mLettere.Add(ltt);
		}
	}

	public static Letters Lettere
	{
		get
		{
			return mLettere;
		}
	}
 
}

Utilizziamo la stringa contenuta nelle risorse fornita da Alberto nel suo progetto che contiene gli indici di tutti i led accesi per ognuna delle lettere dell’alfabeto.

LedDisplay

Questa classe contiene i metodi necessari a produrre ogni singolo Led e l’intera lettera.

public static class LedDisplay
{
	public static Bitmap GetBackgroundBitmap(SolidBrush pBackGround)
	{
		Bitmap bgBmp = new Bitmap(16, 16);
		Graphics grfx = Graphics.FromImage(bgBmp);
		grfx.FillRectangle(pBackGround, 0, 0, 16, 16);
		return (bgBmp);
	}
 
	public static Bitmap GetDot(SolidBrush pBackground, SolidBrush pForeground)
	{
		Bitmap bgBmp = GetBackgroundBitmap(pBackground);
		Graphics grfx = Graphics.FromImage(bgBmp);
		grfx.FillEllipse(pForeground, 1, 1, 13, 13);
		return (bgBmp);
	}
 
	public static Bitmap GetLetter(bool[] pDots, SolidBrush pBackGround, SolidBrush pForegroundOff, SolidBrush pForegroundOn)
	{
		Bitmap bgLetter = new Bitmap(80, 112);
		Graphics grfx = Graphics.FromImage(bgLetter);
		int x = 0;
		int y = 0;
		for (int i = 0; i < 35; i++)
		{
			if (pDots[i])
			{
				grfx.DrawImage((Image)GetDot(pBackGround, pForegroundOn), new Point(x, y));
			}
			else
			{
				grfx.DrawImage((Image)GetDot(pBackGround, pForegroundOff), new Point(x, y));
			}
			x += 16;
			if (x > 64)
			{
				x = 0;
				y += 16;
			}
		}
		return (bgLetter);
	}
 
}

In particolare, i due metodi primari di questa classe sono:
GetDot, che mostra come creare un Bitmap di 16×16 pixel e disegnarvi all’interno un cerchio.

GetLetter, che compone i Dots su un bitmap rettangolare. Utilizzando due dei molti metodi messi a disposizione da Graphics, DrawEllipse e DrawImage, la prima per disegnare il cerchio, la seconda per disegnare il bitmap con il cerchio sul “canvas” del bitmap più grande.

Invitiamo chi vuole scoprire le funzionalità della GDI+ a curiosare un po’ in graphics, per scoprire tutto ciò che si può fare usando Brushes, Pens, Ellipses, Rectangles, Gradients, anche perché gli stessi termini tornano simili anche nelle classi per il disegno vettoriale di WPF.

FrmDisplay

La form per testare il nostro disegnatore di led ha il seguente aspetto a design:

sc_dnwledscs03

E contiene i seguenti controlli:

  • 12 PictureBox chiamate pcx1 -> pcx12
  • 1 Textbox chiamata txtScrivi
  • 4 Buttons chiamati: btnScrivi, btnBackground, btnOff, btnOn
  • 3 Label chiamate: lblBg, lblOff, lblOn
  • 1 Color dialog: clr

Property modificate:

  • pcx1 -> pcx12
    • Size: 80;112
  • txtScrivi
    • MaxLength: 12
  • btnScrivi
    • Text: Scrivi testo
    • Size: 75;23
  • btnBackground
    • Text: Sfondo
    • Size: 75;23
  • btnOff
    • Text: Colore off
    • Size: 75;23
  • btnOn
    • Text: Colore on
    • Size: 75;23
  • lblBg
    • Backcolor: Navy
    • Size: 16;16
  • lblOff
    • Backcolor: DarkGreen
    • Size: 16;16
  • lblOn
    • Backcolor: Lime
    • Size: 16;16

I tre bottoni btnBackground, btnOff, btnOn permettono tramite la Color Dialog di scegliere un colore che viene assegnato alla Label corrispondente ed utilizzato per disegnare lo sfondo o il led spento o acceso.

private void btnBackground_Click(object sender, EventArgs e)
{
	this.mBackground = GetColorBrush(mBackground);
	this.lblBg.BackColor = this.mBackground.Color;
}
 
private void btnOff_Click(object sender, EventArgs e)
{
	this.mForegroundOff = GetColorBrush(mForegroundOff);
	this.lblOff.BackColor = this.mForegroundOff.Color;
}
 
private void btnOn_Click(object sender, EventArgs e)
{
	this.mForegroundOn = GetColorBrush(mForegroundOn);
	this.lblOn.BackColor = this.mForegroundOn.Color;
}

Il bottone btnScrivi, utilizza i colori selezionati sulle tre label e il testo scritto nella textbox per creare i Led tramite i due metodi qui riportati:

private void CleanLetters()
{
	foreach (Control ctrl in this.Controls)
	{
		PictureBox pcx = ctrl as PictureBox;
		if (pcx != null)
		{
			pcx.Image = LedDisplay.GetLetter(LetterBuilder.Lettere[" "].Leds, mBackground,
					mForegroundOff, mForegroundOn);
		}
	}
}

private void WriteLetters()
{
	try
	{
		CleanLetters();
		if (this.txtScrivi.Text.Trim().Length > 0)
		{
			for (int i = 0; i < this.txtScrivi.Text.Length; i++)
			{
				string name = string.Format("pcx{0}", i + 1);
				PictureBox pcx = this.Controls[name] as PictureBox;
				string lettera = this.txtScrivi.Text.Substring(i, 1);
				pcx.Image = LedDisplay.GetLetter(LetterBuilder.Lettere[lettera].Leds, mBackground,
					mForegroundOff, mForegroundOn);
				this.Refresh();
				Thread.Sleep(100);
			}
		}
	}
	catch (Exception ex)
	{
		MessageBox.Show(ex.Message);
	}
}

 

Ho volutamente usato due metodi diversi per accedere alle 12 PictureBox visto che spesso sui forum viene richiesto come creare e come usare array di controlli e perché non esistono i veri e propri array come era in VB6.

Nel metodo Clear, che spegne i led di tutti e 12 i display usiamo un ciclo sulla collezione Controls della form ed esaminiamo tutte le PictureBox.

Nel metodo Write, che invece aggiorna la scritta utilizziamo il nome delle immagini per accedervi tramite la collection Controls della form. Ci sono ovviamente molti metodi diversi, ad esempio avremmo potuto semplicemente costruire una collezione di reference alle PictureBox e usarla, ma lasciamo ovviamente a chi legge il compito di usare la propria fantasia.

Potete scaricare il progetto al link seguente:

Per qualsiasi domanda, curiosità approfondimento, o se trovate un errore usate pure i l link al modulo di contatto in cima alla pagina.

ADDENDUM 07/02/2017

Spostando i post storici dal vecchio blog a questo nuovo blog, ho deciso di aggiornare e rendere utilizzabili quelli più interessanti, in questo caso, il progetto windows forms che usa la GDI+ è interessante per alcune cose che fa, pertanto l’ho aggiornato al framework 4.5.2 prima di effettuarne l’upload in modo da mostrare come le cose fatte con il framework 2.0 come era l’originale, funzionano ancora perfettamente.