Introduction
Actually, I just wanted to get some practice in coding user-controls. I started playing with Rectangles and Brushes and Pens in the onPaint
method until I got the idea to set up my own dot-matrix control. A dot-matrix is used to display text and signs in dot-matrix style like LC displays do. As a spin-off product of this matrix, I got an editor to build a char-set for the matrix.
Let's Get Started
The dot-matrix is built by 16 rows, each containing 16 dots. To be more precise, 16x16 rectangles. Each rectangle might be filled completely or just with circle shape. As I needed at least some properties for each of the dots (rectangles), I started with my own rectangle
-class. Let's call it RectangleXT
.
Using the Code
class RectangleXT
{
public RectangleXT()
{
}
private Rectangle _rect;
public Rectangle Rect
{
get { return _rect; }
set { _rect = value; }
}
private int _id;
public int Id
{
get { return _id; }
set { _id = value; }
}
private bool _enabled;
public bool Enabled
{
get { return _enabled; }
set { _enabled = value; }
}
private int _row;
public int Row
{
get { return _row; }
set { _row = value; }
}
private int _col;
public int Column
{
get { return _col; }
set { _col = value; }
}
private int _binVal;
public int BinaryValue
{
get { return _binVal; }
set { _binVal = value; }
}
private Color _color;
public Color Color
{
get { return _color; }
set { _color = value; }
}
}
Having this done, I could start building the matrix. The constructor of my Matrix16x16:Control is building a RectangleXT[]
array first.
int row = 0;
int column = 0;
for (int i = 0; i < 256; i++)
{
dotRects[i] = new RectangleXT();
dotRects[i].Id = i;
if (i % 16 == 0 && i != 0)
{
column = 0;
row++;
}
dotRects[i].Column = column++;
dotRects[i].Row = row;
dotRects[i].BinaryValue = 15 - dotRects[i].Column;
dotRects[i].Rect = new Rectangle(_dotRectWidth * dotRects[i].Column,
_dotRectHeight * dotRects[i].Row, _dotRectWidth, _dotRectHeight);
}
Now we're ready to get it painted...
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Graphics gr = e.Graphics;
Rectangle rect = ClientRectangle;
Brush brushEnabeld = new SolidBrush(ColorEnabled);
Brush brushDisabled = new SolidBrush(ColorDisabled);
int rw = rect.Width;
int refw = _dotRectWidth * 16;
for (int i = 0; i < 256; i++)
{
Rectangle rtmp = dotRects[i].Rect;
if (this.ShowBorders)
{
rtmp.Inflate(-1, -1);
}
if (dotRects[i].Enabled)
{
dotRects[i].Color = ColorEnabled;
switch (DisplayStyle)
{
case Style.Dots:
gr.FillEllipse(brushEnabeld, rtmp);
break;
case Style.Rectagles:
gr.FillRectangle(brushEnabeld, rtmp);
break;
default:
break;
}
}
else
{
dotRects[i].Color = ColorDisabled;
switch (DisplayStyle)
{
case Style.Dots:
gr.FillEllipse(brushDisabled, rtmp);
break;
case Style.Rectagles:
gr.FillRectangle(brushDisabled, rtmp);
break;
default:
break;
}
}
if (_teachMode && _showNumbers)
{
gr.DrawString((dotRects[i].Column + 1).ToString(),
this.Font, new SolidBrush(Color.Black),
dotRects[i].Rect.X + 5, dotRects[i].Rect.Y + 5);
}
}
onMatrixChanged();
}
Well, there are quite some things done in the onPaint
method. There are some properties of the rectangles we have to deal with.
Enabled
- Depending on this property, the dot will be drawn either in 'ColorEnabled
' or 'ColorDisabled
' color which both are properties of Matrix16x16.
DisplayStyle
- Decides whether dots or rectangles are used to draw the matrix.
ShowBorders
- Some border around each rectangle or not
TeachMode
&& ShowNumbers
- These will be explained later when we build the character set to be displayed with the matrix. And finally, the onMatrixChanged()
will notify anybody who wants to know, that some change to matrix has happened. Now that we have a matrix, it would be fine to display something. (Actually, the reason we build it) To get each dot addressed, it has a binary value. Starting with the leftmost dot with a value of 15, we count down to zero to rightmost dot. Having this, we're able to calculate the value of row. Let's do some binary math first.
The leftmost dot represents a value of 2^15 = 32768 and the rightmost dot a value of 2^0 = 1. So, if we want to display a row where the first and the last dot are enabled (lighted) the row-value is 2^15 + 2^0 = 32769. With an array of 16 integer values, we're now able to define all 16 rows of the matrix. This is done with:
public void setMatrix(int[] dots)
{
for (int i = 0; i < 256; i++) {
for (int j = 0; j < 16; j++) {
if (dotRects[i].Row == j)
{
if (dotRects[i].BinaryValue < 8) {
byte value = (byte)(Math.Pow(2, dotRects[i].BinaryValue));
byte mask = (byte)dots[j];
byte check = (byte)(value & mask);
dotRects[i].Enabled = (check == value);
}
else {
int value = (int)(Math.Pow(2, dotRects[i].BinaryValue)) >> 8;
int mask = dots[j] >> 8;
byte check = (byte)(value & mask);
dotRects[i].Enabled = (check == value);
}
}
}
}
this.Invalidate();
onMatrixChanged();
}
To display the letter "A
", we can just invoke setMatrix({ 0, 2016, 4080, 6168, 6168, 6168, 6168, 6168, 8184, 8184, 6168, 6168, 6168, 6168, 6168, 0 })
Quite easy, isn't it? But how about some more letters? OK, take some piece of paper and a pen and start calculating letter "B
". Way too much work, you say? You're right.
I wouldn't have it done this way either. That's why I implemented the 'TeachMode
'. In the demo app, you can enable TeachMode
with a checkbox. In TeachMode
, you can click on the dots in the Matrix to get them enabled or disabled. Keeping the left mouse button pressed, you can easily draw a line. With the right mouse button, you can disable several dots in row. The TextBox
below the matrix will show the int[16]
of your drawing. That's the way I set up a set of char
s to display.
class Chars
{
private Dictionary<string,> stdChars = new Dictionary<string,>();
public Chars()
{
buildStdCharset();
}
private void buildStdCharset()
{
stdChars.Add(" ", new int[] { 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0 });
stdChars.Add("A", new int[] { 0, 2016, 4080, 6168, 6168,
6168, 6168, 6168, 8184, 8184, 6168, 6168, 6168,
6168, 6168, 0 });
stdChars.Add("B", new int[] { 0, 8160, 8176, 6168, 6168,
6168, 6168, 8184, 8184, 6168, 6168, 6168, 6168,
8176, 8160, 0 });
stdChars.Add("C", new int[] { 0, 1984, 4064, 6160, 6144,
6144, 6144, 6144, 6144, 6144, 6144, 6144, 6160,
4064, 1984, 0 });
stdChars.Add("D", new int[] { 0, 8160, 8176, 6168, 6168,
6168, 6168, 6168, 6168, 6168, 6168, 6168, 6168,
8176, 8160, 0 });
stdChars.Add("E", new int[] { 0, 8184, 8184, 6144, 6144,
6144, 6144, 8160, 8160, 6144, 6144, 6144, 6144,
8184, 8184, 0 });
stdChars.Add("F", new int[] { 0, 8184, 8184, 6144, 6144,
6144, 6144, 8160, 8160, 6144, 6144, 6144, 6144,
6144, 6144, 0 });
[...]
}
public Dictionary<string,int[]> Standard
{
get { return stdChars; }
}
With the setChar()
method, each of these char
s can be displayed.
public void setChar(char character)
{
try
{
setMatrix(_characters.Standard[character.ToString()]);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
The next step I did was just putting some of these matrixes into a UserControl
.