Krypton-Toolkit-Suite-Extended-NET-5.470
Krypton-Toolkit-Suite-Extended-NET-5.470 copied to clipboard
Status Strip Feature Request
Please add the ability to change the color of each individual item in the status strip, to include foreground color, back ground color as well as StartGradient and EndGradient colors.
A version is now available, just need to implement the text colour.
Quick turn @Wagnerp, Thanks!. Could you enable changing the text color aka ForeColor? Ideally it would work like this.
If (ForeColor != Color.Empty)
draw the text in the color specified with the configured cleartext/antialiased properties.
If (BackColor != Color.Empty)
draw the background using a solid brush.
else If (GradiantColorOne != Color.Empty || GradientColorTwo != Color.Empty)
draw the background using the LinearGradientBrush()
At that point, one can control just about all the attributes of the status strip.
I think it's done, is this what you mean?
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
Rectangle r = new Rectangle(0, 0, Width, Height);
if (ForeColor != Color.Empty)
{
g.TextRenderingHint = TextRenderingHint.AntiAlias;
Font typeface = new Font(Font.FontFamily, Font.Size, Font.Style, Font.Unit);
SolidBrush brush = new SolidBrush(ForeColor);
g.DrawString(Text, typeface, brush, 0, 0);
}
else if (BackColor != Color.Empty)
{
using (SolidBrush sb = new SolidBrush(BackColor))
{
g.FillRectangle(sb, r);
}
}
else if (GradientColourOne != Color.Empty || GradientColourTwo != Color.Empty)
{
using (LinearGradientBrush lgb = new LinearGradientBrush(r, GradientColourOne, GradientColourTwo, GradientMode))
{
g.FillRectangle(lgb, r);
}
}
base.OnPaint(e);
}
Been on vacation. Back now. I was thinking something more like this. The main difference is that I changed your first "else if" into just an "if". The only "else if" should occur regarding the background, not the foreground.
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
Rectangle r = new Rectangle(0, 0, Width, Height);
if (ForeColor != Color.Empty)
{
g.TextRenderingHint = TextRenderingHint.AntiAlias;
Font typeface = new Font(Font.FontFamily, Font.Size, Font.Style, Font.Unit);
SolidBrush brush = new SolidBrush(ForeColor);
g.DrawString(Text, typeface, brush, 0, 0);
}
if (BackColor != Color.Empty)
{
using (SolidBrush sb = new SolidBrush(BackColor))
{
g.FillRectangle(sb, r);
}
}
else if (GradientColourOne != Color.Empty || GradientColourTwo != Color.Empty)
{
using (LinearGradientBrush lgb = new LinearGradientBrush(r, GradientColourOne, GradientColourTwo, GradientMode))
{
g.FillRectangle(lgb, r);
}
}
base.OnPaint(e);
}
The only other thing that's a bit wiggy is that you're hard coding the TextRenderingHint. Should that be extracted from the system? I believe if one is using ClearText it's not just AntiAlias, but I could be wrong. I'm not real familiar with TextRenderingHint.
You may also want to use a using statement in the first if as well for the brush.
One last thing - This doesn't quite work. It appears to draw the text foreground and background color in the color you specify and the write over it with the palette color. I'm not sure what's going on exactly. Perhaps the base.OnPaint() is causing the second draw.
I'll update the code. There is one bug, which I don't know exactly where it's coming from. The bug is creating a text ghosting effect. Will try your solution though.
I saw that too and noticed that the text wasn't aligned exactly the same way as the default renderer draws it. Thanks!
I think the base.OnPaint()
call was the issue. Thanks for your suggestion.
Are you saying you got it to work? Because commenting that out only enabled the ForeColor - not the BackColor (assuming you're not trying to use the Gradient). Also, Did you figure out how to align the text because it was a little off on the left side when I tried it.
In the attached image the text on the Gradient background is supposed to be Red and the Third status items shows misaligned text when you have the OnPaint enabled (not commented out).
I think so. Don't know about the alignment though. Might look into a possible TextGlow
colour property to make the text stand out more.
Just downloaded the latest code base. FG works BG doesn't work.
var e = new ExtendedControls.ExtendedToolkit.ToolstripControls.ExtendedToolStripStatusLabel();
e.Text = "This is a test of the emergency blah blah";
e.ForeColor = Color.Red;
e.BackColor = Color.Black;
statusStrip1.Items.Add(e);
I put this code in the public Form1() method of your playground app to test it.
I then changed the code to this:
var e = new ExtendedControls.ExtendedToolkit.ToolstripControls.ExtendedToolStripStatusLabel();
e.Text = "This is a test of the emergency blah blah";
e.ForeColor = Color.Red;
//e.BackColor = Color.Black;
e.GradientColourOne = Color.Black;
e.GradientColourTwo = Color.DarkGray;
e.GradientMode = System.Drawing.Drawing2D.LinearGradientMode.Vertical;
statusStrip1.Items.Add(e);
And the text doesn't show up at all
I think I have it mostly working now. The problem is the background was drawing over the foreground. So I moved the DrawString last in the OnPaint() method.
protected override void OnPaint(PaintEventArgs e)
{
// Set a graphics variable
Graphics g = e.Graphics;
// Rectangle variable
Rectangle r = new Rectangle(0, 0, Width, Height);
if (BackColor != Color.Empty)
{
using (SolidBrush sb = new SolidBrush(BackColor))
{
g.FillRectangle(sb, r);
}
}
else if (GradientColourOne != Color.Empty || GradientColourTwo != Color.Empty)
{
using (LinearGradientBrush lgb = new LinearGradientBrush(r, GradientColourOne, GradientColourTwo, GradientMode))
{
g.FillRectangle(lgb, r);
}
}
if (ForeColor != Color.Empty)
{
g.TextRenderingHint = TextRenderingHint.AntiAlias | TextRenderingHint.ClearTypeGridFit;
Font typeface = new Font(Font.FontFamily, Font.Size, Font.Style, Font.Unit);
SolidBrush brush = new SolidBrush(ForeColor);
g.DrawString(Text, typeface, brush, 0, 0);
}
//base.OnPaint(e);
}
On my system, adding ClearyTypeGridFit also helps render the text closer to what the OS does.
And I'm not sure what TextGlow is supposed to do since there is nothing regarding it in the OnPaint and it's not calling the base. I figure Glow isn't doing anything at this point.
I also had to change how BackColor is being used
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
//public override Color BackColor { get => new Color(); set {; } }
public Color BackColor { get; set; }
Now that the background is able to be changed one could do something like this to alert a user. This also works with Gradients.
I've added the code that you have suggested, and it does look better. I've added new code for an alert option, but can't understand why it doesn't work. The TextGlow
feature doesn't currently work at the moment, as I'm still working on a C#
implementation, because most of the examples I've come across are written in VisualBasic
.
Regarding the Alert. I brought your code over and debugged it. The problem was you were setting the wrong variables and not forcing a redraw. Try this.
public async void BlinkLabel()
{
while (true)
{
await Task.Delay(_flashInterval);
BackColor = BackColor == AlertColourOne ? AlertColourTwo : AlertColourOne;
ForeColor = AlertTextColour;
// Force a redraw
Invalidate();
}
}
The same is true for SoftBlink()
public async void SoftBlink(Color alertColour1, Color alertColour2, Color alertTextColour, short cycleInterval, bool bkClr)
{
var sw = new Stopwatch();
sw.Start();
short halfCycle = (short)Math.Round(cycleInterval * 0.5);
while (true)
{
await Task.Delay(1);
var n = sw.ElapsedMilliseconds % cycleInterval;
var per = (double)Math.Abs(n - halfCycle) / halfCycle;
var red = (short)Math.Round((alertColour2.R - alertColour1.R) * per) + alertColour1.R;
var grn = (short)Math.Round((alertColour2.G - alertColour1.G) * per) + alertColour1.G;
var blw = (short)Math.Round((alertColour2.B - alertColour1.B) * per) + alertColour1.B;
var clr = Color.FromArgb(red, grn, blw);
if (bkClr)
{
BackColor = clr;
}
else
{
//ctrl.ForeColor = clr;
//ForeColor = alertTextColour;
ForeColor = clr;
}
// Force a redraw
Invalidate();
}
}
Also, I noticed that your property defaults aren't getting set when using Code First versus using the designer. So, I added the defaults to the constructor as well, otherwise the AlertBlinkInterval was set to 0 no matter what I set it to in my code.
public ExtendedToolStripStatusLabel()
{
Alert = false;
AlertColourOne = Color.White;
AlertColourTwo = Color.Black;
AlertTextColour = Color.Red;
GradientColourOne = Color.Empty;
GradientColourTwo = Color.Empty;
TextGlow = Color.White;
GradientMode = LinearGradientMode.ForwardDiagonal;
TextGlowSpread = 5;
AlertBlinkInterval = 256;
}
The only other thing I'd add to your Alert functionality is duration. So you can set the alert and tell it to "blink" for 5 seconds (or some duration) and then go back to normal.
Here's my changes with blinkDuration. I set the default duration to 10 seconds arbitrarily. Maybe there should be a property for that?
public async void BlinkLabel(long blinkDuration = 10_000)
{
var sw = Stopwatch.StartNew();
var fg = ForeColor;
var bg = BackColor;
while (sw.ElapsedMilliseconds < blinkDuration)
{
await Task.Delay(_flashInterval);
BackColor = BackColor == AlertColourOne ? AlertColourTwo : AlertColourOne;
ForeColor = AlertTextColour;
// Force a redraw
Invalidate();
}
BackColor = bg;
ForeColor = fg;
Invalidate();
sw.Stop();
}
public async void SoftBlink(Color alertColour1, Color alertColour2, Color alertTextColour, short cycleInterval, bool bkClr, long blinkDuration = 10_000)
{
var sw = Stopwatch.StartNew();
short halfCycle = (short)Math.Round(cycleInterval * 0.5);
var fg = ForeColor;
var bg = BackColor;
while (sw.ElapsedMilliseconds < blinkDuration)
{
await Task.Delay(1);
var n = sw.ElapsedMilliseconds % cycleInterval;
var per = (double)Math.Abs(n - halfCycle) / halfCycle;
var red = (short)Math.Round((alertColour2.R - alertColour1.R) * per) + alertColour1.R;
var grn = (short)Math.Round((alertColour2.G - alertColour1.G) * per) + alertColour1.G;
var blw = (short)Math.Round((alertColour2.B - alertColour1.B) * per) + alertColour1.B;
var clr = Color.FromArgb(red, grn, blw);
if (bkClr)
BackColor = clr;
else
ForeColor = clr;
// Force a redraw
Invalidate();
}
BackColor = bg;
ForeColor = fg;
Invalidate();
sw.Stop();
}
I've uploaded new code with a definable BlinkDuration
property. Thanks for your bug testing, it's much appreciated.
You are welcome.
Don't these lines need to default to the property BlinkDuration? https://github.com/Wagnerp/Krypton-Toolkit-Suite-Extended-NET-4.70/blob/75200438afff2779d4e08fab47923942612b5cae/Source/Krypton%20Toolkit%20Suite%20Extended/Extended%20Controls/ExtendedToolkit/ToolstripControls/ExtendedToolStripStatusLabel.cs#L423 like
public async void BlinkLabel(long blinkDuration = BlinkDuration)
https://github.com/Wagnerp/Krypton-Toolkit-Suite-Extended-NET-4.70/blob/75200438afff2779d4e08fab47923942612b5cae/Source/Krypton%20Toolkit%20Suite%20Extended/Extended%20Controls/ExtendedToolkit/ToolstripControls/ExtendedToolStripStatusLabel.cs#L451
public async void SoftBlink(Color alertColour1, Color alertColour2, Color alertTextColour, short cycleInterval, bool bkClr, long blinkDuration = BlinkDuration)
Otherwise the BlinkDuration property does nothing since it's not used anywhere. Or am I missing something?
I'll put it in. I don't know if these methods need to be put into the OnPaint
method, as it's not doing anything when called, if so I can create an enum
+ property for the user to choose from.
Note to myself: Revisit at some point.