WinForms: Button with two lines, one above the other - c#

I need to put two lines as text in a WinForms button so I have found this solution. I need first line, that above, to be printed in black color, and second line, that under the first one, to be printed in red color. My problem is that background rectangle is not transparent. I need background rectangle to be transparent so I have done some improvements but without success. Also first line is printed at the top not at center (vertical) and the distance between two lines (line spacing) there is too much separation. I would like to reduce it as well and center in vertical the two lines within the button. Below the code.
private void TextButton(Button btn, string line1, string line2)
{
btn.Text = String.Empty;
Bitmap bmp = new Bitmap(btn.ClientRectangle.Width, btn.ClientRectangle.Height);
using (Graphics G = Graphics.FromImage(bmp))
{
G.Clear(btn.BackColor);
StringFormat SF = new StringFormat();
SF.Alignment = StringAlignment.Center;
SF.LineAlignment = StringAlignment.Near;
using (Font tahoma = new Font("Tahoma", 15.75F, System.Drawing.FontStyle.Bold))
{
Rectangle RC = btn.ClientRectangle;
RC.Inflate(-5, -5);
G.FillRectangle(Brushes.Transparent,RC.X,RC.Y,RC.Width,RC.Height);
G.DrawString(line1, tahoma, Brushes.Black, RC, SF);
}
using (Font tahoma2 = new Font("Tahoma", 12))
{
SF.LineAlignment = StringAlignment.Center;
G.FillRectangle(Brushes.Transparent,btn.ClientRectangle.X,btn.ClientRectangle.Y,btn.ClientRectangle.Width,btn.ClientRectangle.Height);
G.DrawString(line2, tahoma2, Brushes.Red, btn.ClientRectangle, SF);
}
}
btn.Image = bmp;
btn.ImageAlign = ContentAlignment.MiddleCenter;
}

Finally I have solved. Below my solution.
private void TextButton(Button btn, string line1, string line2)
{
btn.Text = String.Empty;
Bitmap bmp = new Bitmap(btn.ClientRectangle.Width, btn.ClientRectangle.Height);
using (Graphics G = Graphics.FromImage(bmp))
{
G.Clear(Color.Transparent); <----- I have set this
G.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit; <--- This to avoid bad text within bitmap
G.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; <--- Also this to avoid bad text within bitmap
StringFormat SF = new StringFormat();
SF.Alignment = StringAlignment.Center;
SF.LineAlignment = StringAlignment.Near;
using (Font tahoma = new Font("Tahoma", 15.75F, System.Drawing.FontStyle.Bold))
{
Rectangle RC = new Rectangle(btn.ClientRectangle.X, btn.ClientRectangle.Y + 30, btn.ClientRectangle.Width, btn.ClientRectangle.Height-30);
RC.Inflate(-5, -5);
G.DrawString(line1, tahoma, Brushes.Black, RC, SF);
}
using (Font tahoma2 = new Font("Tahoma", 12))
{
Rectangle RC = new Rectangle(btn.ClientRectangle.X, btn.ClientRectangle.Y + 30, btn.ClientRectangle.Width, btn.ClientRectangle.Height-30);
RC.Inflate(-5, -5);
SF.LineAlignment = StringAlignment.Center;
G.DrawString(line2, tahoma2, Brushes.Red, RC, SF);
}
}
btn.Image = bmp;
btn.ImageAlign = ContentAlignment.MiddleCenter;
}

Related

Circle Cut Off in PictureBox C#

i created a function which is drawing a circle with a letter inside.
i write it into a stream and set it to picture box in Zoom Mode.
It looks nice but some pieces of the circle are cut off.
Here is the Code for the Circle:
public MemoryStream GenerateCircle(string name)
{
var avatarString = string.Format("{0}", name[0]).ToUpper();
var bgColour = ColorTranslator.FromHtml("#007FBC");
var bmp = new Bitmap(70, 70);
var sf = new StringFormat();
sf.Alignment = StringAlignment.Center;
sf.LineAlignment = StringAlignment.Center;
var font = new Font("Arial", 34, FontStyle.Bold, GraphicsUnit.Pixel);
var graphics = Graphics.FromImage(bmp);
graphics.Clear(Color.White);
graphics.SmoothingMode = SmoothingMode.AntiAlias;
graphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
Rectangle rect = new Rectangle(0, 0, 70, 70);
Brush b = new SolidBrush(bgColour);
Pen pen = new Pen(bgColour);
graphics.DrawEllipse(pen, rect);
graphics.FillEllipse(b, rect);
graphics.DrawString(avatarString, font, new SolidBrush(Color.WhiteSmoke), 35, 35, sf);
graphics.Flush();
var ms = new MemoryStream();
bmp.Save(ms, ImageFormat.Png);
return ms;
}
i set it like that:
avatarpicturebox.Image = Image.FromStream(GenerateCircle("Test"));
That is what it looks like:
Circle Cuts Off
Can someone help here please?
Just reduce the circle's width and height 1 pixel each to avoid clipping the right and bottom sides due to the pixels offset. Use Rectangle.Inflate method and pass negative width and height values to shrink the destination rectangle to fit.
Modifying your function to return a Bitmap instead of MemoryStream.
public Bitmap GenerateCircle(string name)
{
var bgColour = ColorTranslator.FromHtml("#007FBC");
var bmp = new Bitmap(70, 70, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
using (var graphics = Graphics.FromImage(bmp))
using (var sf = new StringFormat())
using (var font = new Font("Arial", 34, FontStyle.Bold, GraphicsUnit.Pixel))
using (var b = new SolidBrush(bgColour))
using (var pen = new Pen(bgColour))
{
sf.Alignment = StringAlignment.Center;
sf.LineAlignment = StringAlignment.Center;
graphics.Clear(Color.White);
graphics.SmoothingMode = SmoothingMode.AntiAlias;
graphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
graphics.PixelOffsetMode = PixelOffsetMode.Half;
Rectangle rect = new Rectangle(Point.Empty, bmp.Size);
rect.Inflate(-2, -2);
graphics.DrawEllipse(pen, rect);
graphics.FillEllipse(b, rect);
graphics.DrawString(name.ToUpper(), font, Brushes.WhiteSmoke, rect, sf);
}
return bmp;
}
And the caller:
private void SomeButton_Click(object sender, EventArgs e)
{
avatarpicturebox.Image?.Dispose();
avatarpicturebox.Image = GenerateCircle("T");
}
Side Notes
Always dispose of the graphics objects you create either by calling explicitly the .Dispose method or by creating them with using keyword.
Use the Graphics.DrawString overloads that take a Rectangle and StringFormat params to draw the strings. Use the later to dictate how and where the string should be drawn in the given Rectangle. See also StringFormatFlags enum.

Bitmap.MeasureString not working as expected

I need to generate a single image, with a single character (letter) in it, which completely fills the image. I found a Bitmap.MeasureString option.
So what I tried to do was measure the size the string would be using this function, and then resizing the image to match, and then draw the letter.
But I am getting loads of white space all around the letter. I need the border of the image to be on the edges of the letter.
So a W would be a wide image, but an I would be a very narrow image.
I drew a line from the top left to bottom right to show what's happening.
Can you spot an issue with my code? Am I maybe misunderstanding this function? Or, it doesn't work as I'd hoped?
In the example image I posted, I want the top border of the image to be on the top border of the A. And the left border touching the bottom right hand of the A. And the bottom being right on the bottom edge of the A.
Here's what I am trying:
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
namespace SpikeFontToImage
{
class Program
{
static void Main(string[] args)
{
Bitmap source = new Bitmap(1, 1);
Bitmap destination;
Font stringFont = new Font("Tohoma", 1024);
string text = "A";
using (var measure = Graphics.FromImage(source))
{
var size = measure.MeasureString(text, stringFont);
Console.WriteLine($"{text} is {size} in size");
destination = new Bitmap( (int)size.Width, (int)size.Height);
RectangleF rectf = new RectangleF(0, 0, destination.Width, destination.Height);
StringFormat format = new StringFormat()
{
Alignment = StringAlignment.Center,
LineAlignment = StringAlignment.Center
};
//RectangleF rectf = new RectangleF(70, 90, 90, 50);
Graphics g = Graphics.FromImage(destination);
g.SmoothingMode = SmoothingMode.AntiAlias;
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
g.DrawString(text, stringFont, Brushes.Black, rectf, format);
Pen pen = new Pen(Color.GreenYellow, 3);
g.DrawLine(pen, new Point(0, 0), new Point((int)size.Width, (int)size.Height));
g.Flush();
destination.Save(#"c:\temp\ccl.jpg");
}
}
}
}

C# tab control left horizontal alignment with picture

I have followed the instructions in the msdn website:
https://msdn.microsoft.com/en-us/library/vstudio/ms404305%28v=vs.100%29.aspx
to display a tab control as the following picture:
And this is the code:
private void tcMain_DrawItem(Object sender, DrawItemEventArgs e)
{
Graphics g = e.Graphics;
Brush _textBrush;
TabPage _tabPage = tcMain.TabPages[e.Index];
Rectangle _tabBounds = tcMain.GetTabRect(e.Index);
if (e.State == DrawItemState.Selected)
{
_textBrush = new SolidBrush(Color.Red);
g.FillRectangle(Brushes.Gray, e.Bounds);
}
else
{
_textBrush = new System.Drawing.SolidBrush(e.ForeColor);
e.DrawBackground();
}
Font _tabFont = new Font("Times New Roman", (float)22, FontStyle.Regular, GraphicsUnit.Pixel);
StringFormat _stringFlags = new StringFormat();
_stringFlags.Alignment = StringAlignment.Near;
_stringFlags.LineAlignment = StringAlignment.Center;
g.DrawString(_tabPage.Text, _tabFont, _textBrush, _tabBounds, new StringFormat(_stringFlags));
}
Now I want to add a picture to the left of the tab control buttons? I mean, how to show a picture to the left of the word Book for example?
After trying some answers from below I ended up with the code:
private void tcMain_DrawItem(Object sender, DrawItemEventArgs e)
{
Graphics g = e.Graphics;
Brush _textBrush;
TabPage _tabPage = tcMain.TabPages[e.Index];
Rectangle _tabBounds = tcMain.GetTabRect(e.Index);
if (e.State == DrawItemState.Selected)
{
_textBrush = new SolidBrush(Color.Red);
g.FillRectangle(Brushes.Gray, e.Bounds);
}
else
{
_textBrush = new System.Drawing.SolidBrush(e.ForeColor);
e.DrawBackground();
}
tcMain.ImageList = imgList;
tcMain.TabPages[0].ImageIndex = 1;
tcMain.TabPages[1].ImageIndex = 0;
tcMain.TabPages[2].ImageIndex = 3;
tcMain.TabPages[3].ImageIndex = 2;
Rectangle tabImage = tcMain.GetTabRect(e.Index);
tabImage.Size = new Size(40, 40);
g.DrawImage(tcMain.ImageList.Images[_tabPage.ImageIndex], tabImage);
Font _tabFont = new Font("Times New Roman", (float)22, FontStyle.Regular, GraphicsUnit.Pixel);
StringFormat _stringFlags = new StringFormat();
_stringFlags.Alignment = StringAlignment.Center;
_stringFlags.LineAlignment = StringAlignment.Center;
g.DrawString(_tabPage.Text, _tabFont, _textBrush, _tabBounds, new StringFormat(_stringFlags));
}
Then when I take a snapshot to the results it comes like this:
Refreshing all the time
You could add an ImageList control to your project and add some images to it and set the ImageIndex property of your TabPages. Then just use DrawImage() method in your DrawItem event.
Rectangle tabImage = tcMain.GetTabRect(e.Index);
tabImage.Size = new Size(16, 16);
g.DrawImage(tcMain.ImageList.Images[_tabPage.ImageIndex], tabImage);
You could also use ImageKey instead of ImageIndex.
g.DrawImage(tcMain.ImageList.Images[_tabPage.ImageKey], tabImage);
If you add the ImageList and the ImageIndex programmatically take a look:
ImageList imageList = new ImageList();
imageList.Images.Add("key1", Image.FromFile("pathtofile"));
imageList.Images.Add("key2", Image.FromFile("pathtofile"));
tcMain.ImageList = imageList;
tcMain.TabPages[0].ImageIndex = 1;
tcMain.TabPages[1].ImageIndex = 0;

Why isn't this code able to centre the text on the graphics object?

I've been hit and miss with using the graphics object method MeasureCharacterRanges(). Below is example code where it doesn't work. When the rectangle is drawn, it's not around the 'X', but slightly to the left.
'X', clearly, does not mark the spot.
Why?
public partial class Form1 : Form
{
private string test = "X";
public Form1()
{
InitializeComponent();
this.ResizeRedraw = true;
this.Paint += new System.Windows.Forms.PaintEventHandler(this.Form1_Paint);
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
using (Graphics g = this.CreateGraphics())
{
g.Clear(this.BackColor);
using (Font font = new Font(Font.Name, this.Size.Height / 8))
{
Rectangle layout = this.ClientRectangle;
layout.Width *= 2;
using (StringFormat stringFormat = new StringFormat())
{
CharacterRange[] charRange = { new CharacterRange(0, test.Length) };
stringFormat.SetMeasurableCharacterRanges(charRange);
Region[] sr = g.MeasureCharacterRanges(test, font, layout, stringFormat);
RectangleF rectangle = sr[0].GetBounds(g);
PointF location = new PointF((this.ClientRectangle.Width - rectangle.Width) / 2.0f, ((this.ClientRectangle.Height - rectangle.Height) / 2.0F));
rectangle.Location = location;
using (SolidBrush brush = new SolidBrush(Color.Black))
{
g.DrawString(test, font, brush, rectangle.Location);
}
g.DrawRectangle(Pens.Red, Rectangle.Round(rectangle));
}
}
}
}
}
You need to create your StringFormat with the GenericTypographic property, and then pass the stringFormat into the DrawString method with one of the other overloads so that it knows about the StringFormat you've specified.
If you don't do this, DrawString just uses the default StringFormat which doesn't have the correct Property values for Trimming, FormatFlags and Alignment etc.
// StringFormat created using GenericTypographic
using (StringFormat stringFormat = new StringFormat(StringFormat.GenericTypographic))
{
CharacterRange[] charRange = { new CharacterRange(0, test.Length) };
stringFormat.SetMeasurableCharacterRanges(charRange);
Region[] sr = g.MeasureCharacterRanges(test, font, layout, stringFormat);
RectangleF rectangle = sr[0].GetBounds(g);
PointF location = new PointF((this.ClientRectangle.Width - rectangle.Width) / 2.0f, ((this.ClientRectangle.Height - rectangle.Height) / 2.0F));
rectangle.Location = location;
using (SolidBrush brush = new SolidBrush(Color.Black))
{
// Now passing in stringFormat
g.DrawString(test, font, brush, rectangle.Location, stringFormat);
}
g.DrawRectangle(Pens.Red, Rectangle.Round(rectangle));
}

How to Drawing Text with Outline onto Images?

I have been searching and looking for Drawing text with outline onto images?
Here my code
private static void tulisnamafile(string imagepath, string textnya)
{
Image image = Image.FromStream(new MemoryStream(File.ReadAllBytes(imagepath)));
Bitmap newImage = new Bitmap(640, 380);
using (Graphics g = Graphics.FromImage(newImage))
{
// Draw base image
g.DrawImageUnscaled(image, 0, 0);
//Static is HERE
SolidBrush brushing = new SolidBrush(Color.White);
Font font = new Font(("Comic Sans MS"), 20.0f);
int napoint = newImage.Height - 90;
int napointa = image.Width - 200;
FontFamily ff = new FontFamily("Times New Roman");
int fontSize = 24;
Font f = new Font(ff, fontSize, FontStyle.Regular);
StringFormat sf = new StringFormat();
Rectangle displayRectangle = new Rectangle(new Point(5, napoint), new Size(newImage.Width - 1, newImage.Height - 1));
g.DrawEllipse(Pens.Magenta, new Rectangle(0, 0, 1, 1));
GraphicsPath gp = new GraphicsPath();
gp.AddString(textnya, ff, (int)FontStyle.Bold, fontSize + 4, new Point(0, 0), sf);
g.FillPath(Brushes.White, gp);
g.DrawPath(Pens.Black, gp);
g.Flush(FlushIntention.Sync);
g.Dispose();
}
image.Dispose();
string fileName = "ab.jpg";
string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName);
MessageBox.Show(path);
newImage.Save(path, System.Drawing.Imaging.ImageFormat.Jpeg);
newImage.Dispose();
}
and Its the trigger
private void button3_Click(object sender, EventArgs e)
{
string imagename = "C:\\Documents and Settings\\admin\\My Documents\\Visual Studio 2008\\Projects\\template\\template\\bin\\Debug\\bg.jpg";
tulisnamafile(imagename, "SlimPort® SP1002; Connect mobile devices to any big screen. High Speed micro USB");
}
Check the Code result:
Such messing result, UNWRAPPED and with white color
This what I want, and with wrapping?
I found in CodeProject but not luck, its using C++. Based someone in neowin and tried this one too..
But still not luck.
UPDATE:
Here my working code, for who maybe need it... based from Abdias Software's code ( check the answers ), I make small changes ( there some error in those code).
private static void tulisnamafile(string imagepath, string textnya)
{
float fontSize = 22;
Image image = Image.FromStream(new MemoryStream(File.ReadAllBytes(imagepath)));
//some test image for this demo
Bitmap bmp = (Bitmap)Image.FromFile(imagepath);
Graphics g = Graphics.FromImage(bmp);
//this will center align our text at the bottom of the image
StringFormat sf = new StringFormat();
sf.Alignment = StringAlignment.Center;
sf.LineAlignment = StringAlignment.Far;
//define a font to use.
Font f = new Font("Impact", fontSize, FontStyle.Bold, GraphicsUnit.Pixel);
//pen for outline - set width parameter
Pen p = new Pen(ColorTranslator.FromHtml("#77090C"), 8);
p.LineJoin = LineJoin.Round; //prevent "spikes" at the path
//this makes the gradient repeat for each text line
Rectangle fr = new Rectangle(0, bmp.Height - f.Height, bmp.Width, f.Height);
LinearGradientBrush b = new LinearGradientBrush(fr,
ColorTranslator.FromHtml("#FF6493"),
ColorTranslator.FromHtml("#D00F14"),
90);
//this will be the rectangle used to draw and auto-wrap the text.
//basically = image size
Rectangle r = new Rectangle(0, 0, bmp.Width, bmp.Height);
GraphicsPath gp = new GraphicsPath();
//look mom! no pre-wrapping!
gp.AddString(textnya, f.FontFamily, (int)FontStyle.Bold, fontSize, r, sf);
//these affect lines such as those in paths. Textrenderhint doesn't affect
//text in a path as it is converted to ..well, a path.
g.SmoothingMode = SmoothingMode.AntiAlias;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
//TODO: shadow -> g.translate, fillpath once, remove translate
g.DrawPath(p, gp);
g.FillPath(b, gp);
//cleanup
gp.Dispose();
b.Dispose();
b.Dispose();
f.Dispose();
sf.Dispose();
g.Dispose();
string fileName = "ab.jpg";
string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName);
bmp.Save(path, System.Drawing.Imaging.ImageFormat.Jpeg);
bmp.Dispose();
}
Just to sum up:
Define a GraphicPath and then use DrawPath to draw outlined version of text, FillPath to draw a filled version.
For the second image with the woman, a second (filled) version is drawn first at a small offset.
For the gradient use LinearGradientBrush for brush. Thickness of the outline is defined by the Pen's thickness.
For wrapping define a StringFormat and use a Rectangle to define the region you want the text to be in.
To center the text you can define the rectangle to have the same width as the image, then set strformat.Alignment to Center.
UPDATE: To replicate the text in the second image you can use this code:
float fontSize = 52;
//some test image for this demo
Bitmap bmp = (Bitmap)Image.FromFile(s"test.jpg");
Graphics g = Graphics.FromImage(bmp);
//this will center align our text at the bottom of the image
StringFormat sf = new StringFormat();
sf.Alignment = StringAlignment.Center;
sf.LineAlignment = StringAlignment.Far;
//define a font to use.
Font f = new Font("Impact", fontSize, FontStyle.Bold, GraphicsUnit.Pixel);
//pen for outline - set width parameter
Pen p = new Pen(ColorTranslator.FromHtml("#77090C"), 8);
p.LineJoin = LineJoin.Round; //prevent "spikes" at the path
//this makes the gradient repeat for each text line
Rectangle fr = new Rectangle(0, bmp.Height - f.Height, bmp.Width, f.Height);
LinearGradientBrush b = new LinearGradientBrush(fr,
ColorTranslator.FromHtml("#FF6493"),
ColorTranslator.FromHtml("#D00F14"),
90);
//this will be the rectangle used to draw and auto-wrap the text.
//basically = image size
Rectangle r = new Rectangle(0, 0, bmp.Width, bmp.Height);
GraphicsPath gp = new GraphicsPath();
//look mom! no pre-wrapping!
gp.AddString("Demo for Stack Overflow",
f.FontFamily, (int)f.Style, fontSize, r, sf);
//these affect lines such as those in paths. Textrenderhint doesn't affect
//text in a path as it is converted to ..well, a path.
g.SmoothingMode = SmoothingMode.AntiAlias;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
//TODO: shadow -> g.translate, fillpath once, remove translate
g.DrawPath(p, gp);
g.FillPath(b, gp);
//cleanup
gp.Dispose();
b.Dispose();
b.Dispose();
f.Dispose();
sf.Dispose();
g.Dispose();
bmp.Save(s"test_result.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
bmp.Dispose();
This will produce this result:
To produce the extra "shadow" just translate the gfirst, draw filled, then remove the translation.
The FromHtml is used here as I picked the colors from your image and was to lazy to convert. Just use Color.FromARGB() or a fixed color - as you want.
VB version:
Dim fontSize As Single = 52
Dim bmp As Bitmap = Bitmap.FromFile("c:\test.jpg")
Dim g As Graphics = Graphics.FromImage(bmp)
Dim sf As New StringFormat(StringFormatFlags.NoClip)
sf.Alignment = StringAlignment.Center
sf.LineAlignment = StringAlignment.Far
Dim f As New Font("Impact", fontSize, FontStyle.Bold, GraphicsUnit.Pixel)
Dim p As New Pen(ColorTranslator.FromHtml("#77090C"), 4)
p.LineJoin = LineJoin.Round
'rectangle for font to repeat gradient for each line
Dim fr As New Rectangle(0, bmp.Height - f.Height, bmp.Width, f.Height)
Dim b As New LinearGradientBrush(fr,
ColorTranslator.FromHtml("#FF6493"),
ColorTranslator.FromHtml("#D00F14"),
90)
Dim r As New Rectangle(0, 0, bmp.Width, bmp.Height)
Dim gp As New GraphicsPath
gp.AddString("Demo for Stack Overflow",
f.FontFamily,
f.Style,
fontSize,
r,
sf)
g.SmoothingMode = SmoothingMode.AntiAlias
g.PixelOffsetMode = PixelOffsetMode.HighQuality
g.DrawPath(p, gp)
g.FillPath(b, gp)
gp.Dispose() 'path
b.Dispose() 'b
b.Dispose() 'p
f.Dispose() 'font
sf.Dispose() 'stringformat
g.Dispose() 'g
bmp.Save("c:\test_result.jpg", Imaging.ImageFormat.Jpeg)
bmp.Dispose()
A simple way to get "better" results could be to draw the text twice. Draw the shadow first, for example some pixels to the right and to the bottom, if you want the classic drop-shadow look, in gray color. You may also want to consider using a different font, any font without serifs will look better i would guess.
For the gradient effect see the msdn page or google how on how to use it.
Also, play around with the SmoothingMode and TextRenderingHint of the graphics object, HighQuality and Antialias should produce better looking results.

Categories

Resources