Multiple colors in a C# .NET label - c#

I'm looking for a way to display multiple colors in a single C#/.NET label. E.g the label is displaying a series of csv separated values that each take on a color depending on a bucket they fall into. I would prefer not to use multiple labels, as the values are variable length and I don't want to play with dynamic layouts. Is there a native support for this?

There is no native control in .NET that does this. Your best bet is to write your own UserControl (call it RainbowLabel or something). Normally you would have a custom label control inherit directly from Label, but since you can't get multi-colored text in one label, you would just inherit from UserControl.
For rendering the text, your UserControl could split the text on commas and then dynamically load a differently-colored Label for each chunk. A better way, however, would be to render the text directly onto your UserControl using the DrawString and MeasureString methods in the Graphics namespace.
Writing UserControls in .NET is really not difficult, and this kind of unusual problem is exactly what custom UserControls are for.
Update: here's a simple method you can use for rendering the multi-colored text on a PictureBox:
public void RenderRainbowText(string Text, PictureBox pb)
{
// PictureBox needs an image to draw on
pb.Image = new Bitmap(pb.Width, pb.Height);
using (Graphics g = Graphics.FromImage(pb.Image))
{
// create all-white background for drawing
SolidBrush brush = new SolidBrush(Color.White);
g.FillRectangle(brush, 0, 0,
pb.Image.Width, pb.Image.Height);
// draw comma-delimited elements in multiple colors
string[] chunks = Text.Split(',');
brush = new SolidBrush(Color.Black);
SolidBrush[] brushes = new SolidBrush[] {
new SolidBrush(Color.Red),
new SolidBrush(Color.Green),
new SolidBrush(Color.Blue),
new SolidBrush(Color.Purple) };
float x = 0;
for (int i = 0; i < chunks.Length; i++)
{
// draw text in whatever color
g.DrawString(chunks[i], pb.Font, brushes[i], x, 0);
// measure text and advance x
x += (g.MeasureString(chunks[i], pb.Font)).Width;
// draw the comma back in, in black
if (i < (chunks.Length - 1))
{
g.DrawString(",", pb.Font, brush, x, 0);
x += (g.MeasureString(",", pb.Font)).Width;
}
}
}
}
Obviously this will break if you have more than 4 comma-delimited elements in your text, but you get the idea. Also, there appears to be a small glitch in MeasureString that makes it return a width that is a couple pixels wider than necessary, so the multi-colored string appears stretched out - you might want to tweak that part.
It should be straightforward to modify this code for a UserControl.
Note: TextRenderer is a better class to use for drawing and measuring strings, since it uses ints. Graphics.DrawString and .MeasureString use floats, so you'll get off-by-a-pixel errors here and there.
Update: Forget about using TextRenderer. It is dog slow.

You could try using a RichTextBox so that you can get multiple colors for the string and then make it read only and remove the border. Change the background color to the same as the Form it is on and you might get away with it.

As an alternative, you might do this as rtf or html in a suitable control (such as WebBrowser). It would probably take a bit more resources that you'd ideally like, but it'll work fairly quickly.

If you are building your Windows app for people with XP and up, you can use WPF. Even if it's a Windows Forms app, you can add a WPF UserControl.
I would then use a Label, and set the "Foreground" property to be a gradient of colors.
Or, in Windows Forms (no WPF), you could just use a "Flow Panel", and then in a for loop add multiple Labels as segments of your sentense... they will all "flow" together as if it was one label.

I'm using colored labels quite often to mark keywords in red color etc.
Like in Phil Wright's answer I use a RichTextBox control, remove the border and set the background color to SystemColors.Control.
To write colored text the control is first cleared and then I use this function to append colored text:
private void rtb_AppendText(Font selfont, Color color, Color bcolor,
string text, RichTextBox box)
{
// append the text to the RichTextBox control
int start = box.TextLength;
box.AppendText(text);
int end = box.TextLength;
// select the new text
box.Select(start, end - start);
// set the attributes of the new text
box.SelectionColor = color;
box.SelectionFont = selfont;
box.SelectionBackColor = bcolor;
// unselect
box.Select(end, 0);
// only required for multi line text to scroll to the end
box.ScrollToCaret();
}
If you want to run this function with "mono" then add a space before every new colored text, or mono will not set new the color correctly. This is not required with .NET
Usage:
myRtb.Text = "";
rtb_AppendText(new Font("Courier New", (float)10),
Color.Red, SystemColors.Control, " my red text", myRtb);
rtb_AppendText(new Font("Courier New", (float)10),
Color.Blue, SystemColors.Control, " followed by blue", myRtb);

Slightly off topic ... You could check also:
generate html color table
model colors in sql
the result

You can simply use multiple labels. Set the font properties you want and then use the left. top and width properties to display the words you want displayed differently. This is assuming you are using windows forms.

Try this,
labelId.Text = "Successfully sent to" + "<a style='color:Blue'> " + name + "</a>";

There is no native support for this; you will either have to use multiple labels or find a 3rd-party control that will provide this functionality.

I don't think so. You should create one yourself.

As per your Question your requirement is simple like
lable.Text = "This color is Red", So it have to display text like this
"The color is" will be in Blue and "Red" will be red color ..
This can be done like this
lable.Text = "<span style='Color:Blue'>" + " The color is " +"</span>" + "<span style='Color:Red'>"Red"</span>"

Related

In C#, in tooltip how to change different color for differnt part of text?

In the MS chart, I am displaying tooltip using below code.
ToolTip ToolTip = new ToolTip();
ToolTip .Show(" X value:"+s+"\nLine 1 Y value: =" + ss + "\nLine 2 Y value:=" + ss1, chart, (int)e.Location.X, (int)e.Location.Y);
I am able to set only one foreground color using ToolTip .ForeColor = System.Drawing.Color.Red;.
I am new to C#.
How to assign a different colour and draw text in custom tooltip class or how to use HTML renderer to achieve my requirement?
I could not assign a different colour for a different part of the tooltip text.
How to achieve it?
You can owner-draw the tooltip
Example:
ToolTip ToolTip = new ToolTip();
ToolTip.OwnerDraw = true;
ToolTip.Popup += (ss, ee) => { ee.ToolTipSize = new Size(200, 50); };
ToolTip.Draw += (ss, ee) =>
{
ee.DrawBackground();
ee.DrawBorder();
ee.Graphics.DrawString("Warning", Font, Brushes.Red, 10, 1);
ee.Graphics.DrawString(ee.ToolTipText, Font, Brushes.Black, 1, 22);
};
ToolTip.Show("Demo only", somecontrol..);
This is just a simple example; there are many more parameters to style the tooltip, including drawing stuff, images, brushes of all types, etc..
It is also recommended to use TextRenderer instead of the classic GDI+ DrawString.
Note how I set the Size in the PopUp event!
All sorts of formatting is possible with the text; for multiline text it is recommended to use an overload with bounding rectangle instead of x/y coordinates and maybe also alignment with a StringFormat. Do note though, that is is always tricky to embed formatted parts inside of a text.
Possible, but tedious to get really right, as always with GDI drawing. -
The basic trick is to determine a bounding rectangle first; this can be done with MeasureString.
The short answer would be "natively you simply can't" (Except you count drawing the label yourself as natively). But as always in programming there are creative ways to get the result you want.
The answer in the Question Orel suggested basically makes use of a renderer for HTML Markup to render the styled text inside your application.
If you have a look at the newer version of this library here they actually provide a WinForms ToolTip Control which accepts HTML Markup to render inside it's content area.
To use this renderer they provide a nuget package which makes installation trivial. Simply manage your projects nuget packages and search for HtmlRenderer.WinForms and install the latest version. (Check if it also installs the latest Version of HtmlRenderer.Core because it didn't on my end and I had to update the Core package)
After this rebuild your project to get the new controls in your designer toolbox.
To test the package I dragged a textbox and a HtmlToolTip onto my Form. To set the new toolTip you use it just like a normal WinForms tooltip.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.htmlToolTip1.SetToolTip(this.textBox1, "<h1 style=\"color:red\">Hello</h1><i style=\"color:blue\">World</i>");
}
}
Now you can style your toolTip content with HTML markup and change the foreground colors as you like:

How to draw rich text with Skia or SkiaSharp

I want to draw rich text like iOS's Attributed Text or Xamarin.Forms's FormattedString with SkiaSharp, but I can't find how to.
I found the DrawText method, but it's for simple text rendering with one color and one font. No mixed colors and/or fonts and no styles like bold, italic, strike-through, or underline.
Do I have to do it with my own rich text rendering logic?
This is doable using the SKPaint type. Basically, as you draw, you set the properties on the paint:
var paint = new SKPaint();
paint.StrikeThruText = true;
paint.TextSize = 24;
paint.Color = SKColors.Yellow;
paint.UnderlineText = true;
paint.Typeface = SKTypeface.FromFamilyName(
"Arial",
SKFontStyleWeight.Bold,
SKFontStyleWidth.Normal,
SKFontStyleSlant.Italic);
and then you can draw:
canvas.DrawText("Fancy Text", 30, 30, paint);
I hope this helps!
For other effects, you can use the SKShader and SKMaskFilter types. This does a blur:
path.MaskFilter = SKMaskFilter.CreateBlur(SKBlurStyle.Normal, 5);
EDIT
After some time, it seems we actually have a much better way to draw text - and not just the basic underlines. I would whole-heartedly recommend this library by Brad Robinson: https://github.com/toptensoftware/RichTextKit
I mean, just look at that beautiful thing!

MeasureString() doesn't return enough width

I have the following code to create a Label on a PictureBox:
Label l = new Label();
l.Text = _name;
l.Size = CreateGraphics().MeasureString(_name, l.Font).ToSize();
l.BackColor = Color.White;
but the label is always dropping the last character. If I add a character to the call:
l.Size = CreateGraphics().MeasureString(_name+".", l.Font).ToSize();
it works fine, but that doesn't feel right.
There seems to be some white space just before the text in the label, but Padding is set to 0. How can I fix this the correct way?
Can't you use the AutoSize property?
MeasureString is notoriously inaccurate, though normally it returns a size bigger than you'd expect:
The MeasureString method is designed for use with individual strings and includes a small amount of extra space before and after the string to allow for overhanging glyphs. Also, the DrawString method adjusts glyph points to optimize display quality and might display a string narrower than reported by MeasureString. To obtain metrics suitable for adjacent strings in layout (for example, when implementing formatted text), use the MeasureCharacterRanges method or one of the MeasureString methods that takes a StringFormat, and pass GenericTypographic. Also, ensure the TextRenderingHint for the Graphics is AntiAlias.
http://msdn.microsoft.com/en-us/library/6xe5hazb.aspx
ToSize() truncates values of the SizeF to the next lower integer values.
So, to avoid losses you can do something like that:
l.Size = (CreateGraphics().MeasureString(_name, l.Font) + new SizeF(1, 0)).ToSize();
Is it something as simple as getting the size wrong when you declare your font compared to the UI font size?

How to get the exact text margins used by TextRenderer

System.Windows.Forms.TextRenderer.DrawText method renders formatted text with or without left and right padding depending on the value of the flags parameter:
TextFormatFlags.NoPadding - fits the text tightly into the bounding box,
TextFormatFlags.GlyphOverhangPadding - adds some left and right margins,
TextFormatFlags.LeftAndRightPadding - adds even bigger margins.
Now, my question is how can I get the exact amount of padding (left and right) added by DrawText to the text for a given device context, string, font etc?
I've dug into .NET 4 with .NET Reflector and found that TextRenderer calculates "overhang padding" which is 1/6 of the font's height and then multiplies this value to calculate left and right margins using these coefficients:
left 1.0, right 1.5 for TextFormatFlags.GlyphOverhangPadding,
left 2.0, right 2.5 for TextFormatFlags.LeftAndRightPadding.
The resulting values are rounded up and passed to the DrawTextExA or DrawTextExW native API functions. It's difficult to recreate this process because font's height is taken not from System.Drawing.Font but from System.Windows.Forms.Internal.WindowsFont and these classes return different values for the same font. And a lot of other internal BCL classes from the System.Windows.Forms.Internal namespace are involved. Decompiling all of them and reusing their code in my app is not an option, because that would be a serious .NET implementation dependency. That's why I need to know if there is some public API in WinForms or at least which Windows functions I can use to get the values of left and right margins.
Note: I've tried to TextRenderer.MeasureText with and without padding and compare the results but that gave me only the sum of left and right margins and I need them separately.
Note 2: In case you wonder why I need this: I want to draw one string with multiple fonts/colors. That involves calling DrawText once for every uniformly formatted substring with NoPadding option (so that the text doesn't spread) but I also want to add manually normal GlyphOverhangPadding at the very beginning and very end of the whole multi-format text.
The value you need for computing left and right margins is TEXTMETRIC.tmHeight, which is possible to obtain using Win32 API.
However, I found that tmHeight is just a line height of a font in pixels, so these three approaches will give you the same value (you can use whichever you like in your code):
int apiHeight = GetTextMetrics(graphics, font).tmHeight;
int gdiHeight = TextRenderer.MeasureString(...).Height;
int gdipHeight = (int)Math.Ceiling(font.GetHeight(graphics));
To obtain left and right margins, we use the same code as TextRenderer does under the hood:
private const float ItalicPaddingFactor = 0.5f;
...
float overhangPadding = (gdiHeight / 6.0f);
//NOTE: proper margins for TextFormatFlags.LeftAndRightPadding flag
//int leftMargin = (int)Math.Ceiling(overhangPadding);
//int rightMargin = (int)Math.Ceiling(overhangPadding * (2 + ItalicPaddingFactor));
//NOTE: proper margins for TextFormatFlags.GlyphOverhangPadding flag
int leftMargin = (int)Math.Ceiling(overhangPadding);
int rightMargin = (int)Math.Ceiling(overhangPadding * (1 + ItalicPaddingFactor));
Size sizeOverhangPadding = TextRenderer.MeasureText(e.Graphics, "ABC", font, Size.Empty, TextFormatFlags.GlyphOverhangPadding);
Size sizeNoPadding = TextRenderer.MeasureText(e.Graphics, "ABC", font, Size.Empty, TextFormatFlags.NoPadding);
int overallPadding = (sizeOverhangPadding.Width - sizeNoPadding.Width);
Now you can easily check that
(leftMargin + rightMargin) == overallPadding
Just to note:
I needed to solve this problem in order to implement "Search Highlight" feature in a ListView-based control that uses GDI text rendering:
Works like a charm :)
This answer is an excerpt from here - http://www.techyv.com/questions/how-get-exact-text-margins-used-textrenderer#comment-35164
If you have ever wanted a Label or TextBox in Windows Forms that performs a little more like on the web, then you've probably figured out that there's no intuitive way to make a Label or TextBox automatically adjust its height to fit the text it contains. While it may not be intuitive, it's definitely not impossible.
In this example, I'll use a TextBox (you could just as easily use a Label) that is docked to the top of a form.To use this, add aTextBox called MyTextBox to the form, and set Dock to DockStyle.Top. Wire up the Resize event of the TextBox to this event handler.
private void MyTextBox_Resize( object sender, EventArgs e )
{
// Grab a reference to the TextBox
TextBox tb = sender as TextBox;
// Figure out how much space is used for borders, etc.
int chromeHeight = tb.Height - tb.ClientSize.Height;
// Create a proposed size that is very tall, but exact in width.
Size proposedSize = new Size( tb.ClientSize.Width, int.MaxValue );
// Measure the text using the TextRenderer
Size textSize = TextRenderer.MeasureText( tb.Text, tb.Font,
proposedSize, TextFormatFlags.TextBoxControl
| TextFormatFlags.WordBreak );
// Adjust the height to include the text height, chrome height,
// and vertical margin
tb.Height = chromeHeight + textSize.Height
+ tb.Margin.Vertical;
}
If you want to resize the a Label or TextBox that is not docked (for example, one that is in a FlowLayoutPanel or other Panel, or just placed on the form), then you can handle the Form's Resize even instead, and just modify the Control's properties directly.
This might seem (very) crude, but this is the only native implementation I can think of:
DrawText draws to an IDeviceContext, which is implemented by Graphics. Now, we can take advantage of that with the following code:
Bitmap bmp = new Bitmap(....);
Graphics graphic = Graphics.FromImage(bmp);
textRenderer.DrawText(graphic,....);
graphic.Dispose();
With the new Bitmap you can go pixel by pixel and count them by some condition.
Again, this method is very crude and wasteful, but at least it's native....
This is not tested but based on the following sources:
http://msdn.microsoft.com/en-us/library/4ftkekek.aspx
http://msdn.microsoft.com/en-us/library/system.drawing.idevicecontext.aspx
http://msdn.microsoft.com/en-us/library/system.drawing.graphics.aspx
http://www.pcreview.co.uk/forums/graphics-bitmap-t1399954.html
I've done something similar a few years ago, to highlight search results (search pattern appears in bold etc.). My implementation was in DevExpress, so the code might not be relevant. If you think it's of use I can copy it, just need to find that implementation.
In System.Windows.Forms, the class to use would be Graphics. It has a MeasureCharacterRanges() method which accepts a StringFormat (start with GenericTypographic and go from there). It is much more appropriate than TextRenderer for displaying a complete string by chaining parts with different styles, fonts or brushes.
You've gone way further than me with the actual padding measuring. DevExpress's controls gave you the text bounding rectangle to start with so that was done for me.
Here's an article by Pierre Arnaud that came up for me in Google, which touches on this area. Unfortunately the GDI+ "Gory details" link there is broken.
Cheers,
Jonno
The fix is to calculate what MeasureText is going to add:
var formatFlags FormatFlags =
TextFormatFlags.NoPadding |
TextFormatFlags.SingleLine;
int largeWidth = TextRenderer.MeasureText(
" ",
font,
new Size(int.MaxValue, int.MaxValue),
formatFlags
).Width;
int smallWidth = TextRenderer.MeasureText(
" ",
font,
new Size(int.MaxValue, int.MaxValue),
formatFlags
).Width;
int extra = smallWidth - (largeWidth - smallWidth);
We calculate the width of one space and the width of two spaces. Both have the extra width added, so we can extrapolate the extra width that is being added. The added width apparently is always the same, so subtracting extra from every width returned by MeasureText gives the expected results.

Drawing a contrasted string on an image

So, I have a snapshot of a video source, which I get into an Image, grab a Graphics object for it, and then draw a timestamp in the bottom right of the image. No problem thus far. However, I cannot guarantee what colour is going to be behind the text, so no matter what brush I use, it will almost certainly clash with some of the images that it is drawn on, making the text unreadable.
I am wondering if anyone knows of a way (either a method in .net, or a nice algorithm), for determining the best colour for a string based on the image behind it.
Cheers
just draw the string 5 times.
One time 1(or2) pixels to the left in black
One time 1(or2) pixels to the right in black
One time 1(or2) pixels above it in black
One time 1(or2) pixels below it in black
and the final time in white on the place where you want it
The only reliable way is to use a contrasting outline.
Back in the days of Commodore 64 sprite graphics, if you wanted something to stand out against any background, you used XOR blitting. Some people referred to this as 'reverse video'.
You can draw lines this way using ControlPaint.DrawReversibleLine, but that won't work for text.
This CodeProject article shows how you can create an XOR brush using interop to gdi32.dll.
Or, if it is allowed, you could use a background color (your choice) for the text (for example white text on black background).
Otherwise, you would need to capture the rectangle where the text is written (for every frame), create the negative image of it, and then get the median color in the rectangle and use it to write the text.
A more complex solution would get you to use two layers (initial picture - L1 and Text (transparent background, black text) - L2),
and before combining them, take all the pixels from L2 that contain text and change the color for the each pixel of the text to the "negative" underlying pixel color value of the L1, but you won't get something that's too usable from a "viewer's" point of view.
This could be a number of variations on the answer by reinier.
Draw underlying text (the four offset ones mentioned by reinier) not in black, but actually in a contrasting color to the foreground color of the actualy text.
Draw the text twice: once in a contrasting color but in bold and/or a slightly larger size, then in the text foreground color over that. Might have to fiddle a bit with coordinates and even need to do the drawing per word or even character to get both passes to nicely align and not give an ugly end result.
Do what reinier suggested, but perhaps not four times (all four directions), but maybe three or even two times to get a kind of "shaded" look.
Let go of the whole "draw text pixel by pixel using API calls" approach and use advanced multilayer compositing techniques like the ones available in WPF design.
For some examples of the last option, check out slides 18 and 21 in Advanced OSM Cartography on SlideShare.
The following snippet shows how to invert a color (background) and then applies Dinah's suggestion to create the background using Graphics.DrawString().
private static Color InvertColor(Color c)
{
return Color.FromArgb(255 - c.R, 255 - c.G, 255 - c.B);
}
// In the following, constants and inplace vars can be parameters in your code
const byte ALPHA = 192;
var textColor = Color.Orange;
var textBrush = new SolidBrush(Color.FromArgb(ALPHA, textColor));
var textBrushBkg = new SolidBrush(Color.FromArgb(ALPHA, InvertColor(textColor)));
var font = new Font("Tahoma", 7);
var info = "whatever you wanna write";
var r = new Rectangle(10, 10, 10, 10);
// write the text
using (var g = Graphics.FromImage(yourBitmap))
{
g.Clear(Color.Transparent);
// to avoid bleeding of transparent color, must use SingleBitPerPixelGridFit
g.TextRenderingHint = TextRenderingHint.SingleBitPerPixelGridFit;
// Draw background for text
g.DrawString(info, font, textBrushBkg, r.Left - 1, r.Top - 1);
g.DrawString(info, font, textBrushBkg, r.Left + 1, r.Top + 1);
g.DrawString(info, font, textBrushBkg, r.Left + 1, r.Top - 1);
g.DrawString(info, font, textBrushBkg, r.Left - 1, r.Top + 1);
// Draw text
g.DrawString(info, font, textBrush, r.Left, r.Top);
}

Categories

Resources