I am having some issues rotating the text on a tab. The tabs originally worked just fine, but then I wanted to bold the text when selected, so I used the Draw Item Event. I added a RotateTransform and a TranslateTransform, but its not working. The text just doesn't show up. I have troubleshot it and if I take the rotation away, the text is visible, but when I use the rotate to make the text vertical, it disappears. Here's my code:
private void tabControl1_DrawItem(object sender, DrawItemEventArgs e)
{
Graphics g = e.Graphics;
string tabText = tabControl1.TabPages[e.Index].Text;
SizeF textSize = g.MeasureString(tabText, tabControl1.Font);
Brush _textBrush = Brushes.Black;
TabPage _tabPage = tabControl1.TabPages[e.Index];
System.Drawing.Rectangle _tabBounds = tabControl1.GetTabRect(e.Index);
StringFormat _stringFlags = new StringFormat();
_stringFlags.Alignment = StringAlignment.Center;
_stringFlags.LineAlignment = StringAlignment.Center;
PointF tabPt = new PointF(_tabBounds.Left+(_tabBounds.Width), _tabBounds.Top+(_tabBounds.Height));
if (e.Index == tabControl1.SelectedIndex)
{
g.RotateTransform(-90);
g.TranslateTransform(tabPt.X, tabPt.Y);
g.DrawString(tabControl1.TabPages[e.Index].Text,
new Font(tabControl1.Font, FontStyle.Bold),
_textBrush,
new PointF(tabPt.X, tabPt.Y));
g.ResetTransform();
}
else
{
g.TranslateTransform(tabPt.X, tabPt.Y);
g.RotateTransform(-90);
g.DrawString(tabControl1.TabPages[e.Index].Text,
tabControl1.Font,
_textBrush,
new PointF(tabPt.X,tabPt.Y));
g.ResetTransform();
}
}
}
Any help would be greatly appreciated.
EDIT
Here's the image:
Here's the new code:
Graphics g = e.Graphics;
string tabText = tabControl1.TabPages[e.Index].Text;
SizeF textSize = g.MeasureString(tabText, tabControl1.Font);
Brush _textBrush = Brushes.Black;
TabPage _tabPage = tabControl1.TabPages[e.Index];
System.Drawing.Rectangle _tabBounds = tabControl1.GetTabRect(e.Index);
PointF rotPt = new PointF(_tabBounds.Left + (_tabBounds.Width / 2) - (2.75F), _tabBounds.Top + (_tabBounds.Height / 2) + (textSize.Width/2));
PointF tabPt = new PointF(_tabBounds.Left + (_tabBounds.Width / 2) - (2.75F), _tabBounds.Top + (_tabBounds.Height / 2) + (textSize.Width)/2);
if (e.Index == tabControl1.SelectedIndex)
{
g.TranslateTransform(rotPt.X, rotPt.Y);
g.RotateTransform(-90);
g.TranslateTransform(-(rotPt.X), -(rotPt.Y));
g.DrawString(tabText,
new Font(tabControl1.Font, FontStyle.Bold),
_textBrush,
new PointF(rotPt.X, rotPt.Y));
}
else
{
g.TranslateTransform(rotPt.X, rotPt.Y);
g.RotateTransform(-90);
g.TranslateTransform(-rotPt.X, -rotPt.Y);
g.DrawString(tabText,
tabControl1.Font,
_textBrush,
new PointF(rotPt.X,rotPt.Y));
}
Thanks TaW for your help. Here's the final code and its working.
public form1()
{
InitializeComponent();
tabControl1.DrawMode = TabDrawMode.OwnerDrawFixed;
tabControl1.DrawItem += new DrawItemEventHandler(tabControl1_DrawItem);
}
private void tabControl1_DrawItem(object sender, DrawItemEventArgs e)
{
Graphics g = e.Graphics;
string tabText = tabControl1.TabPages[e.Index].Text;
SizeF textSize = g.MeasureString(tabText, tabControl1.Font);
Brush _textBrush = Brushes.Black;
TabPage _tabPage = tabControl1.TabPages[e.Index];
System.Drawing.Rectangle _tabBounds = tabControl1.GetTabRect(e.Index);
PointF rotPt = new PointF(_tabBounds.Left + (_tabBounds.Width / 2) - (textSize.Height / 2), _tabBounds.Top + (_tabBounds.Height / 2) + (textSize.Width/2));
if (e.State.HasFlag(DrawItemState.Selected))
{
g.TranslateTransform(rotPt.X, rotPt.Y);
g.RotateTransform(-90);
g.TranslateTransform(-(rotPt.X), -(rotPt.Y));
g.DrawString(tabText,
new Font(tabControl1.Font, FontStyle.Bold),
_textBrush,
new PointF(rotPt.X, rotPt.Y));
g.ResetTransform();
}
else
{
g.TranslateTransform(rotPt.X, rotPt.Y);
g.RotateTransform(-90);
g.TranslateTransform(-rotPt.X, -rotPt.Y);
g.DrawString(tabText,
tabControl1.Font,
_textBrush,
new PointF(rotPt.X,rotPt.Y));
g.ResetTransform();
}
}
P.S. I never did get the e.Bounds rather than GetTabRect (i'm not sure how to set it to the selected tab).
Related
I want to save all drawstring which is done with the codes at the bottom half. I am saving it by saving the "Points" in a List type. The purpose of saving is because I want to have the ability to delete a particular drawing. All other drawings will be retained and only the one which wants to be deleted will be removed. My main query is why can't I use the same code with some minor editing(Top half of the code is the code I use to add new drawstring) that I use to draw to redraw when I am deleting a particular drawing.
Side_pictureBox.ImageLocation = AppDomain.CurrentDomain.BaseDirectory + #"pictures for app\Bus_Nearside.png";
Side_pictureBox.Image = Image.FromFile(AppDomain.CurrentDomain.BaseDirectory + #"\pictures for app\Bus_Nearside.png");
Bitmap bm = new Bitmap(Side_pictureBox.Image);
if (Tagged_Remarks_listBox.SelectedIndex == 0)
{
for (int x = 0; x <= NumberingPosition.Count - 1; x++)
{
if (x != 0)
{
using (Graphics gr = Graphics.FromImage(bm))
{
gr.SmoothingMode = SmoothingMode.AntiAlias;
Font drawFont = new Font("Calibri (Body)", 15);
SolidBrush drawBrush = new SolidBrush(Color.Blue);
//MessageBox.Show(Numbering[u] + NumberingPosition[u]);
gr.DrawString(Numbering[x], drawFont, drawBrush, NumberingPosition[x]);
}
}
Side_pictureBox.Image = bm;
Side_pictureBox.Invalidate();
}
//Above code is when I first drawstring ,Below Code is to redraw when deleting particular drawing//
Bitmap bm = new Bitmap(Side_pictureBox.Image);
using (Graphics gr = Graphics.FromImage(bm))
{
gr.SmoothingMode = SmoothingMode.AntiAlias;
String drawString = numbering_for_digram.ToString();
Font drawFont = new Font("Calibri (Body)", 15);
SolidBrush drawBrush = new SolidBrush(Color.Blue);
gr.DrawString(drawString, drawFont, drawBrush, lastPoint);
Numbering.Add(drawString);
drawFont.Dispose();
drawBrush.Dispose();
}
Side_pictureBox.Image = bm;
If we're in the Paint() event, then I'd expect to see something more like this:
if (Tagged_Remarks_listBox.SelectedIndex == 0)
{
Graphics gr = e.Graphics;
gr.SmoothingMode = SmoothingMode.AntiAlias;
Font drawFont = new Font("Calibri (Body)", 15);
SolidBrush drawBrush = new SolidBrush(Color.Blue);
for (int x = 1; x <= NumberingPosition.Count - 1; x++)
{
//MessageBox.Show(Numbering[u] + NumberingPosition[u]);
gr.DrawString(Numbering[x], drawFont, drawBrush, NumberingPosition[x]);
}
drawFont.Dispose();
drawBrush.Dispose();
}
Note that calling Invalidate() in the Paint() would cause it to repaint itself REPEATEDLY and FOREVER...which might be part of the problem.
I found a way to make it work(Maybe not the best, most effective, efficient way), by using Bitmap to take in all the drawings. This bitmap will then be saved as a Png file. Once all drawings are done, I declare the picturebox.image as the bitmap's Png file.
private void Side_pictureBox_Paint(object sender, PaintEventArgs e)
{
if (Tagged_Remarks_listBox.SelectedIndex == 0 && selectedindexreset == true)
{
//Side_pictureBox.ImageLocation = AppDomain.CurrentDomain.BaseDirectory + #"pictures for app\Bus_Nearside.png";
Side_pictureBox.Image = Image.FromFile(AppDomain.CurrentDomain.BaseDirectory + #"\pictures for app\Bus_Nearside.png");
Bitmap bmforedit = new Bitmap(Side_pictureBox.Image);
//MessageBox.Show("inside");
using (Graphics gr = Graphics.FromImage(bmforedit))
{
for (int x = 0; x <= NumberingPosition.Count - 1; x++)
{
//MessageBox.Show(x.ToString());
if (Numbering[x] != "1") // change accordingly
{
//MessageBox.Show(Numbering[x]);
gr.SmoothingMode = SmoothingMode.AntiAlias;
Font drawFont = new Font("Calibri (Body)", 15);
SolidBrush drawBrush = new SolidBrush(Color.Blue);
//MessageBox.Show(Numbering[x] + NumberingPosition[x]);
gr.DrawString(Numbering[x], drawFont, drawBrush, NumberingPosition[x]);
drawFont.Dispose();
drawBrush.Dispose();
}
}
// bmforedit.Save(#"C:\Users\user\Desktop\PDI_APP_EDIT_FOR_TO\PDIPROTOTYPE2\bin\Debug\pictures for app\TestImage.png");
Side_pictureBox.Image.Dispose();
//bmforedit.Save(#"C:\Users\user\Desktop\PDI_APP_EDIT_FOR_TO\PDIPROTOTYPE2\bin\Debug\pictures for app\TestImage1.png");
Side_pictureBox.Image = bmforedit;
}
for (int u = 0; u <= PrevStore.Count - 1; u++)
{
using (Graphics g = Graphics.FromImage(bmforedit))
{
if (u < StartDrawCount[0] || u > StopDrawCount[0])
{
g.DrawLine(new Pen(Color.DarkRed, 2), PrevStore[u], NowStore[u]);
g.SmoothingMode = SmoothingMode.AntiAlias;
}
}
}
bmforedit.Save(#"C:\Users\user\Desktop\PDI_APP_EDIT_FOR_TO\PDIPROTOTYPE2\bin\Debug\pictures for app\TestImage2.png");
selectedindexreset = false;
Side_pictureBox.ImageLocation = AppDomain.CurrentDomain.BaseDirectory + #"pictures for app\TestImage2.png";
Side_pictureBox.Refresh();
}
}
Everytime I type in my textbox, the text perfectly draws in the picurebox, but whenever I erase all the letters or type a [space] first, the picturebox becomes like this:
TextBox Text Change Event
private void tbox_Text_TextChanged(object sender, EventArgs e)
{
_LayerType = (LayerClass.Type)System.Enum.Parse(typeof(LayerClass.Type), "Text");
pictureBox_Canvass.Invalidate();
text = tbox_Text.Text;
UpdateFont();
textRect = txt.MeasureCharacters(fontFamily, text);
}
Measure Characters Method
public RectangleF MeasureCharacters(Font f, string text)
{
RectangleF r = new RectangleF();
GraphicsPath path = new GraphicsPath();
path.AddString(text, f.FontFamily, (int)f.Style, f.Size, new PointF(250 - (r.Width / 2), 250 - (r.Height / 2)), StringFormat.GenericDefault);
var bounds = path.GetBounds();
r = new RectangleF(bounds.Left, bounds.Top, bounds.Width, bounds.Height);
return r;
}
Text Drawing
public LayerClass DrawString(LayerClass.Type _text, string text, RectangleF rect, Font _fontStyle, Brush brush, float angle, PaintEventArgs e)
{
using (StringFormat string_format = new StringFormat())
{
SizeF stringSize = e.Graphics.MeasureString(text, _fontStyle);
rect.Location = new PointF(Shape.center.X - (rect.Width / 2), Shape.center.Y - (rect.Height / 2));
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.CompositingQuality = CompositingQuality.HighQuality;
//e.Graphics.DrawRectangle(Pens.Red, Rectangle.Round(rect));
RectangleF r = new RectangleF(rect.Location, rect.Size);
GraphicsPath path = new GraphicsPath();
//Exception thrown here
path.AddString(text, _fontStyle.FontFamily, Convert.ToInt32(_fontStyle.Style), r.Height, r.Location, string_format);
RectangleF text_rectf = path.GetBounds();
PointF[] target_pts = {
new PointF(r.Left, r.Top),
new PointF(r.Right, r.Top),
new PointF(r.Left, r.Bottom)};
Matrix m = new Matrix(text_rectf, target_pts);
Matrix flip = new Matrix();
flip.Translate(-stringSize.Width, 1);
m.RotateAt(angle, new PointF(Shape.center.X - (r.Width/4.5f), Shape.center.Y));
path.Transform(m);
if (flipped)
path.Transform(flip);
if (!isOutlined)
e.Graphics.FillPath(Brushes.Red, path);
else
e.Graphics.DrawPath(Pens.Red, path);
}
this._Text = text;
this._TextRect = rect;
this.brush = brush;
this._Angle = angle;
return new LayerClass(_text, this, text, rect);
}
Error:
A first chance exception of type 'System.OutOfMemoryException' occurred in System.Drawing.dll
The exception comes from the line in the
path.AddString(text, _fontStyle.FontFamily, Convert.ToInt32(_fontStyle.Style), r.Height, r.Location, string_format);
EDIT:
This is called on my onPaint Event:
public void Source(PaintEventArgs e)
{
switch (_LayerType)
{
case LayerClass.Type.Rectangle:
shape.DrawRectangle(LayerClass.Type.Rectangle, rectangleColor, strokeRect, rectangleWidth, rectangleHeight, e.Graphics, rectRadius);
break;
case LayerClass.Type.Square:
shape.DrawSquare(LayerClass.Type.Square, squareColor, strokeSquare, squareWidth, squareHeight, e.Graphics, squareRadius);
break;
case LayerClass.Type.Circle:
shape.DrawCircle(LayerClass.Type.Circle, circleColor, strokeCircle, circleWidth, circleHeight, e.Graphics);
break;
case LayerClass.Type.Ellipse:
shape.DrawEllipse(LayerClass.Type.Ellipse, ellipseColor, strokeEllipse, ellipseWidth, ellipseHeight, e.Graphics);
break;
case LayerClass.Type.Triangle:
shape.DrawTriangle(LayerClass.Type.Triangle, triangleColor, strokeTriangle, triangleWidth, e.Graphics, triangleRadius);
break;
case LayerClass.Type.Image:
img.ImageDrawing(LayerClass.Type.Image, ImageBitmap, imgRect, path, rotationAngle, e, newLoc, flipped);
break;
case LayerClass.Type.Text:
txt.DrawString(LayerClass.Type.Text, text, textRect, fontFamily, brush, textAngle, e); //CALLS THE TEXT DRAWING
break;
}
}
Fixed it using this condition:
if (text == "" || text == " ")
path.Dispose();
Okay, to start off. I'm trying to make dynamic editable, addable, and removeable text onto a picturebox. I got that working.
When saving an image from a picturebox, it doesn't save the labels. I now got it to draw the labels as a string using Graphics. Yet, it only draws the last modified/added/edited label to the pictureBox. I'm lost.
Here's my code for drawing the labels & saving them:
if (sfd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
string ext = Path.GetExtension(sfd.FileName);
switch (ext)
{
case ".jpg":
format = ImageFormat.Jpeg;
break;
case ".bmp":
format = ImageFormat.Bmp;
break;
}
Bitmap bmp = new Bitmap(pictureBox1.Image);
RectangleF rectf = new RectangleF(70, 90, 90, 50);
Graphics g = Graphics.FromImage(bmp);
g.SmoothingMode = SmoothingMode.AntiAlias;
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.Flush();
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
SolidBrush brush = new SolidBrush(label.ForeColor);
for (int i = 0; i < n; i++)
{
g.DrawString(label.Text, label.Font, brush, label.Location);
label.SelectNextControl(label, false, false, true, false);
}
pictureBox1.Image = bmp;
pictureBox1.Image.Save(sfd.FileName, format);
}
Here's where the labels are defined and created:
label = new CustomLabel();
label.Name = "" + n;
label.Location = new Point(newTextbox.Location.X, newTextbox.Location.Y);
label.Text = newTextbox.Text;
label.Font = new Font("Verdana", fontSize);
label.BackColor = Color.Transparent;
label.ForeColor = textColor;
label.AutoSize = true;
label.Visible = true;
newTextbox.Visible = false;
newTextbox.Dispose();
pictureBox1.Controls.Add(label);
TextSelected = false;
label.DoubleClick += new System.EventHandler(this.label_DoubleClick);
label.MouseDown += new MouseEventHandler(this.label_MouseDown);
label.MouseUp += new MouseEventHandler(this.MouseUp);
label.MouseMove += new MouseEventHandler(this.MouseMove);
n++;
And n is defined:
public int n = 1;
Where the stroke is added to the text:
public class CustomLabel : Label
{
public CustomLabel()
{
OutlineForeColor = Color.Black;
OutlineWidth = 3;
}
public Color OutlineForeColor { get; set; }
public float OutlineWidth { get; set; }
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.FillRectangle(new SolidBrush(BackColor), ClientRectangle);
using (GraphicsPath gp = new GraphicsPath())
using (Pen outline = new Pen(OutlineForeColor, OutlineWidth)
{ LineJoin = LineJoin.Round })
using (StringFormat sf = new StringFormat())
using (Brush foreBrush = new SolidBrush(ForeColor))
{
gp.AddString(Text, Font.FontFamily, (int)Font.Style,
Font.Size, ClientRectangle, sf);
e.Graphics.ScaleTransform(1.3f, 1.35f);
e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
e.Graphics.DrawPath(outline, gp);
e.Graphics.FillPath(foreBrush, gp);
}
}
}
The problem is in your for loop:
for (int i = 0; i < n; i++)
{
g.DrawString(label.Text, label.Font, brush, label.Location);
label.SelectNextControl(label, false, false, true, false);
}
Here label never changes, so you are just drawing the same label n times. And I don't know what SelectNextControl does.
I suggest looping over the controls in the picture box:
foreach (var customLabel in pictureBox1.Controls.OfType<CustomLabel>()) {
g.DrawString(customLabel.Text, customLabel.Font, brush, customLabel.Location);
}
Why the 4`th column is not selected? I use listView3_DrawSubItem and e.DrawDefault = true; for that column and now is not selectable.
Edit:
The listView1_DrawSubItem code:
// Only interested in 2nd column.
if (e.Header != this.action)
{
e.DrawDefault = true;
return;
}
drawItem(e);
And the drawitem code:
string drawString = e.SubItem.Text;
float size = 8.25F;
e.DrawBackground();
Bitmap image = new Bitmap(DesktopCleaner.Properties.Resources.folder_icon_512x512);
if (drawString == "Leave on Desktop")
{
image = new Bitmap(DesktopCleaner.Properties.Resources.desk);
}
else if (drawString == "Recycle")
{
image = new Bitmap(DesktopCleaner.Properties.Resources.recyclebin_preview_1);
}
else if (drawString == "Delete")
{
image = new Bitmap(DesktopCleaner.Properties.Resources.free_vector_delete_icon_101805_Delete_icon);
}
var imageRect = new Rectangle(e.Bounds.X + 3, e.Bounds.Y, image.Width - 2, image.Height - 2);
e.Graphics.DrawImage(image, imageRect);
System.Drawing.Font drawFont = new System.Drawing.Font(listView1.Font.FontFamily, size, FontStyle.Bold);
System.Drawing.SolidBrush drawBrush = new System.Drawing.SolidBrush(System.Drawing.Color.Black);
System.Drawing.StringFormat drawFormat = new System.Drawing.StringFormat();
var strrect = new Rectangle(e.Bounds.X + 18, e.Bounds.Y + 3, 150, e.Bounds.Height);
e.Graphics.DrawString(drawString, drawFont, drawBrush, strrect, drawFormat);
You are responsible for drawing the selection.
So even if you use the nice methods:
e.DrawBackground();
e.DrawText();
no selection is being drawn.
So you need to use FillRectangle and DrawString with the appropriate Colors, maybe like this:
bool selected = e.Item.Selected;
using ( SolidBrush backBrush = new SolidBrush(
selected? SystemColors.MenuHighlight :SystemColors.Window ) )
e.Graphics.FillRectangle(backBrush, e.Bounds);
using (SolidBrush textBrush = new SolidBrush(
selected ? Color.White : Color.Black ))
e.Graphics.DrawString(e.Item.Text, yourFont, textBrush, e.Bounds.X, e.Bounds.Y);
The code is simplified; you'll use your coodinates to make room for the icons etc..
I have a picture box (picture box 1) and in that box have drawn a rectangle and displayed the drawn portion using another picture box (picture box 2). Problem is when i draw rectangle (in picture box 1)the picture box 2 will not shows up but when change the position of the form (move the form) the picture box 2 shows up.
How to display the drawn potion...
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
using (Pen pen = new Pen(Color.Green, 2))
{
pen.Color = Color.Red;
pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
e.Graphics.DrawRectangle(pen, rect);
foreach (Rectangle r in rectangles)
{
label1.Top = r.Top; label1.Left = r.Left; label1.Width = r.Width;
label1.Height = r.Height;
e.Graphics.DrawRectangle(pen, r);
e.Graphics.DrawString(label1.Text, label1.Font, new SolidBrush(label1.ForeColor), r);
}
}
if (!(rect.Width <= 0 | rect.Height <= 0))
{
sz1.Width = rect.Width * Convert.ToInt16(1.5);
sz1.Height = rect.Height * Convert.ToInt16(1.5);
pictureBox2.Size = sz1;
w.X = 500; w.Y = 20;
pictureBox2.Location = w;
Bitmap niv = new Bitmap(pictureBox2.Width, pictureBox2.Height);
using (Graphics g1 = Graphics.FromImage(niv))
{
g1.InterpolationMode = InterpolationMode.HighQualityBicubic;
g1.DrawImage(pictureBox1.Image, pictureBox2.ClientRectangle, rect, GraphicsUnit.Pixel);
}
pictureBox2.Image = niv;
pictureBox2.Visible = true;
pictureBox2.Invalidate();
}
}
You can paint or repainting your picturebox using the OnPaint event.
I got it. just right click on the picture box2 and selected "Bring to Front" option.