|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionThis is a small project for Windows applications. It creates a control which simulates a LED light. The LED is round and colorful and it draws itself from the overridden Construct itCreate a new project as C# Window Control Library. Before anything else, change the inheritance from
Now, you may build the project and add the control in the Toolbox. At this stage, the control just comes on the palette with no functionality. Add in its constructor the following lines: private Timer tick;
public Led():base() {
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.DoubleBuffer, true);
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.ResizeRedraw, true);
Width = 17;
Height = 17;
Paint += new System.Windows.Forms.PaintEventHandler(this._Paint);
tick = new Timer();
tick.Enabled = false;
tick.Tick += new System.EventHandler(this._Tick);
}
Timer at this moment, but keep it disabled for the moment. Now, we have to add the two referred handlers - _Pant, which will draw the control, and _Tick, which will handle the change of color in the Flash mode.
Trick: to be able to build each phase, just add empty handlers for private void _Paint(object sender, System.Windows.Forms.PaintEventArgs e) {
}
private void _Tick(object sender, System.EventArgs e) {
}
PaintAdd a new property to be able to activate the LED, and two more to be able to specify the colors of the two - active/inactive - states: private bool _Active = true; [Category("Behavior"), DefaultValue(true)] public bool Active { get { return _Active; } set { _Active = value; Invalidate(); } } private Color _ColorOn = Color.Red; [Category("Appearance")] public Color ColorOn { get { return _ColorOn; } set { _ColorOn = value; Invalidate(); } } private Color _ColorOff = SystemColors.Control; [Category("Appearance")] public Color ColorOff { get { return _ColorOff; } set { _ColorOff = value; Invalidate(); } } Note the The private void _Paint(object sender, System.Windows.Forms.PaintEventArgs e) {
e.Graphics.Clear(BackColor);
if (Enabled) {
if (Active) {
e.Graphics.FillEllipse(new SolidBrush(ColorOn),1,1,Width-3,Height-3);
e.Graphics.DrawArc(new Pen(FadeColor(ColorOn,
Color.White,1,2),2),3,3,Width-7,
Height-7,-90.0F,-90.0F);
e.Graphics.DrawEllipse(new Pen(FadeColor(ColorOn,
Color.Black),1),1,1,Width-3,Height-3);
}
else {
e.Graphics.FillEllipse(new SolidBrush(ColorOff),1,1,Width-3,Height-3);
e.Graphics.DrawArc(new Pen(FadeColor(ColorOff,
Color.Black,2,1),2),3,3,Width-7,Height-7,0.0F,90.0F);
e.Graphics.DrawEllipse(new Pen(FadeColor(ColorOff,
Color.Black),1),1,1,Width-3,Height-3);
}
}
else e.Graphics.DrawEllipse(new
Pen(System.Drawing.SystemColors.ControlDark,1),
1,1,Width-3,Height-3);
}
To better manipulate colors, I have introduced a helper function - #region helper color functions
public static Color FadeColor(Color c1, Color c2, int i1, int i2) {
int r=(i1*c1.R+i2*c2.R)/(i1+i2);
int g=(i1*c1.G+i2*c2.G)/(i1+i2);
int b=(i1*c1.B+i2*c2.B)/(i1+i2);
return Color.FromArgb(r,g,b);
}
public static Color FadeColor(Color c1, Color c2) {
return FadeColor(c1,c2,1,1);
}
#endregion
The function works by splitting the two colors in You may now compile the solution, drag a LED on the test form, change its colors and switch it On/Off directly there in the design mode!
Now Flash ItNow here is the deal: I want to be able to add more that one flash interval, and - why not - more than a set of two On/Off colors. In fact, I want to be able to add as many colors as I want and fire them in sequence like a smooth gradient or an invasive Red-Yellow-Magenta warning. The way to preset this behavior through properties should be easy and error free, while being able to program lengthy sequences by code. To achieve this, I'll add two properties: private string _FlashIntervals="250";
public int [] flashIntervals = new int[1] {250};
[Category("Appearance"),
DefaultValue("250")]
public string FlashIntervals {
get { return _FlashIntervals; }
set {
_FlashIntervals = value;
string [] fi = _FlashIntervals.Split(new char[] {',','/','|',' ','\n'});
flashIntervals = new int[fi.Length];
for (int i=0; i<fi.Length; i++)
try {
flashIntervals[i] = int.Parse(fi[i]);
} catch {
flashIntervals[i] = 25;
}
}
}
private string _FlashColors=string.Empty;
public Color [] flashColors;
[Category("Appearance"),
DefaultValue("")]
public string FlashColors {
get { return _FlashColors; }
set {
_FlashColors = value;
if (_FlashColors==string.Empty) {
flashColors=null;
} else {
string [] fc = _FlashColors.Split(new char[] {',','/','|',' ','\n'});
flashColors = new Color[fc.Length];
for (int i=0; i<fc.Length; i++)
try {
flashColors[i] = (fc[i]!="")?Color.FromName(fc[i]):Color.Empty;
} catch {
flashColors[i] = Color.Empty;
}
}
}
}
There is no need to have the two arrays with the same length: when the colors table is bigger, the extra items will be ignored, while if there are fewer colors than intervals, extra intervals will switch the LED On and Off. The same for empty colors. public int tickIndex;
private void _Tick(object sender, System.EventArgs e) {
tickIndex=(++tickIndex)%(flashIntervals.Length);
tick.Interval=flashIntervals[tickIndex];
try {
if ((flashColors==null)
||(flashColors.Length<tickIndex)
||(flashColors[tickIndex]==Color.Empty))
Active = !Active;
else {
ColorOn = flashColors[tickIndex];
Active=true;
}
} catch {
Active = !Active;
}
}
This would conclude our flash feature if we had a private bool _Flash = false;
[Category("Behavior"),
DefaultValue(false)]
public bool Flash {
get { return _Flash; }
set {
_Flash = value && (flashIntervals.Length>0);
tickIndex = 0;
tick.Interval = flashIntervals[tickIndex];
tick.Enabled = _Flash;
Active = true;
}
}
Example how to operate
I would have finished here, but a couple of details forced me to continue, in fact with a lot more. TransparencyIs our control really transparent outside the babble? Let's find out! Let's go in the test form and draw a line from corner to corner: private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) {
e.Graphics.DrawLine(new Pen(Color.White,2),0,0,
ClientRectangle.Width,ClientRectangle.Height);
}
private void Form1_SizeChanged(object sender, System.EventArgs e) {
Invalidate();
}
(First of all, we learn how to address the coordinates inside the form area using the
What? - Windows has invalidated only areas that have been added to the original form, and through where the mouse has moved. So, put back the
Surprise: the control is not transparent at all! Let's add the following lines to the LED's constructor. (Disable also the big one, just to see how SetStyle(ControlStyles.SupportsTransparentBackColor, true);
BackColor = Color.Transparent;
More likely... Bonus: External EffectsAt this point, our control is quite done. However, let's see what else we could achieve from outside the control code. To do that we have to connect some code to the LED events, and the first event we need to attach code to is Let's delete the reference to the protected override void OnPaint(PaintEventArgs e) {
if (Paint!=null) Paint(this,e);
else {
base.OnPaint(e);
// ... the same as above _Paint method
Trying to compile this, we find out that the original public new event PaintEventHandler Paint;
And now, we can replace the internal rendering of the control with a completely new one. Let's start drawing an exclamation point inside a triangle - the universal sign of "Attention!". We'll use private void led5_Paint(object sender, System.Windows.Forms.PaintEventArgs e) {
TH.WinComponents.Led l = sender as TH.WinComponents.Led;
if (l.Enabled && l.Active) {
int cx,cy,w,h,d;
d = (l.tickIndex<20)?l.tickIndex:39-l.tickIndex;
cx = l.Width/2; cy = l.Height/2;
w = 2*d+8; h = 2*d+8;
try {
// Triangle
Point startPoint = new Point(cx,cy-h/2);
e.Graphics.FillPolygon(new SolidBrush(l.ColorOn),
new Point[] {startPoint,
new Point(cx-w/2,cy+h/2),
new Point(cx+w/2,cy+h/2),
startPoint });
// Exclamation mark
e.Graphics.DrawLine(new Pen(Color.Red,4),cx,cy-h/2+9,cx,cy+h/2-11);
e.Graphics.DrawLine(new Pen(Color.Red,6),cx,cy-h/2+11,cx,cy);
e.Graphics.DrawLine(new Pen(Color.Red,4),cx,cy+h/2-7,cx,cy+h/2-3);
} catch {}
}
}
This exclamation point is not very nice especially when it is small, but at that time, I'll dissolve it in the triangle becoming Red. Also, some error may occur when the shape goes too small, but the empty
Another challenge: using external bitmap: create a small bitmap file by right-clicking the LED project and adding a new item as a bitmap file. Let's draw a heart and name it that way. I want this heart to beat with double pulses like a real heart. To do that, prepare the Bitmap hart;
private void led6_Paint(object sender, System.Windows.Forms.PaintEventArgs e) {
TH.WinComponents.Led l = sender as TH.WinComponents.Led;
Rectangle r = new Rectangle(0,0,40,40);
switch (l.tickIndex) {
case 0 : r = new Rectangle(0,0,47,47);
hart.RotateFlip(RotateFlipType.Rotate90FlipNone);
break;
case 1 : r = new Rectangle(3,3,40,40); break;
case 2 : r = new Rectangle(1,1,46,46); break;
case 3 : r = new Rectangle(3,3,40,40); break;
}
e.Graphics.DrawImage(hart,r);
}
To make this code work, we have to load and prepare the heart bitmap somewhere. Let's do that in the constructor of the form: hart = new Bitmap(@"C:\CS\WinComponents\Led\Hart.bmp");
hart.MakeTransparent(Color.White);
Is it not nice to refer and deliver a file with an executable file, so let's include it in the resources of the assembly. Click on the Hart.bmp file from the test program and, in the Properties panel, change the Build Action to Embedded Resource. Now modify the initialization code: //hart = new Bitmap(@"C:\CS\WinComponents\Led\Hart.bmp");
Stream s =
Assembly.GetCallingAssembly().GetManifestResourceStream("Led.Hart.bmp");
hart = new Bitmap(s);
Note: Led from the resource name Led.Hart.bmp stands for the assembly name of the test program.
ConclusionI have presented a small project which creates a control simulating a LED light. The LED control draws itself from its
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||