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();
Related
I have an image drawn in the picturebox and I need to scale it using a trackbar. But this happens whenever I scale it up, and if I continue scaling - it becomes more blurry.
My code:
Image Drawing
public LayerClass ImageDrawing(LayerClass.Type img, Bitmap bm, Rectangle imgRect, String filepath, int angle, PaintEventArgs e)
{
bm = ImageClass.GrayscaleImage(bm);
bm = MakeTransparentImage(bm);
e.Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
bm = RotateImage(bm, angle);
imgRect = new Rectangle((int)(Shape.center.X - (bm.Width / 2)), (int)(Shape.center.Y - (bm.Height / 2)), (int)bm.Width, (int)bm.Height);
e.Graphics.DrawRectangle(new Pen(Color.Red, 1), imgRect);
e.Graphics.DrawImage(bm, imgRect);
this.imageBitmap = bm;
this.filePath = filePath;
this.rotationAngle = angle;
this.location = location;
this.imageRect = imgRect;
return new LayerClass(LayerClass.Type.Image, this, filePath, imgRect);
}
Scaling
private void trackBar_ScaleImg_Scroll(object sender, EventArgs e)
{
//Width Scaling
if(rb_WidthImage.Checked)
{
imgRect.Width = trackBar_ScaleImg.Value;
imgRect.X = (int)(Shape.center.X - (imgRect.Width / 2));
lbl_imgWidth.Text = Math.Round((trackBar_ScaleImg.Value * 25.4) / 96).ToString();
toolTip_Scroll.SetToolTip(trackBar_ScaleImg, lbl_imgWidth.Text);
ImageBitmap = new Bitmap(ImageBitmap, new Size(imgRect.Width, imgRect.Height));
}
}
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);
}
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).
I have been trying to write a program, to be able to load an image on a form and select a rectangle on it, then load that rectangle onto another picture box (pictureBox2), and I also want to be able to highlight what I have selected on the original picture, in pictureBox1, as I move my mouse.
So far I have this code, but it doesn't properly respond to the mouseMove event, the rectangle that I highlight isn't selected properly. What is the problem?
public partial class Form1 : Form
{
Bitmap original;
bool isSelecting;
int x0, y0, x1, y1;
public Form1()
{
InitializeComponent();
pictureBox1.MouseDown += new MouseEventHandler(picOriginal_MouseDown);
pictureBox1.MouseMove += new MouseEventHandler(picOriginal_MouseMove);
pictureBox1.MouseUp += new MouseEventHandler(picOriginal_MouseUp);
}
#region helpder methods
// Start selecting the rectangle.
private void picOriginal_MouseDown(object sender, MouseEventArgs e)
{
if(original != null)
{
isSelecting = true;
// Save the start point.
x0 = e.X;
y0 = e.Y;
}
}
// Continue selecting.
private void picOriginal_MouseMove(object sender, MouseEventArgs e)
{
if(original != null)
{
// Do nothing it we're not selecting an area.
if(!isSelecting) return;
// Save the new point.
x1 = e.X;
y1 = e.Y;
// Make a Bitmap to display the selection rectangle.
Bitmap bm = new Bitmap(original);
// Draw the rectangle.
using(Graphics gr = Graphics.FromImage(bm))
{
gr.DrawRectangle(Pens.Red,
Math.Min(x0, x1), Math.Min(y0, y1),
Math.Abs(x0 - x1), Math.Abs(y0 - y1)
);
}
// Display the temporary bitmap.
pictureBox1.Image = new Bitmap(bm, new Size(pictureBox1.Width, pictureBox1.Height));
}
}
// Finish selecting the area.
private void picOriginal_MouseUp(object sender, MouseEventArgs e)
{
if(original != null)
{
// Do nothing it we're not selecting an area.
if(!isSelecting) return;
isSelecting = false;
// Display the original image.
pictureBox1.Image = original;
// Copy the selected part of the image.
int wid = Math.Abs(x0 - x1);
int hgt = Math.Abs(y0 - y1);
if((wid < 1) || (hgt < 1)) return;
Bitmap area = new Bitmap(wid, hgt);
using(Graphics gr = Graphics.FromImage(area))
{
Rectangle source_rectangle =
new Rectangle(Math.Min(x0, x1), Math.Min(y0, y1),
wid, hgt);
Rectangle dest_rectangle =
new Rectangle(0, 0, wid, hgt);
gr.DrawImage(original, dest_rectangle,
source_rectangle, GraphicsUnit.Pixel);
}
// Display the result.
pictureBox2.Image = area;
}
}
#endregion
private void button1_Click(object sender, EventArgs e)
{
if(original != null)
{
}
}
private void button2_Click(object sender, EventArgs e)
{
OpenFileDialog dialog = new OpenFileDialog();
dialog.Filter = "jpg files (*.jpg)|*.jpg|All files(*.*)|*.*";
if(dialog.ShowDialog() == DialogResult.OK)
{
original = new Bitmap(dialog.FileName);
pictureBox1.Image = new Bitmap(original, new Size(pictureBox1.Width, pictureBox1.Height));
}
dialog.Dispose();
}
}
I think your problem is the 'original' image size which is not the same as the picturebox size. Try this code to compensate for the zoom ratio between your 'original' image and the picturebox size:
// Draw the rectangle.
float zoomX = (float)original.Size.Width / pictureBox1.Width;
float zoomY = (float)original.Size.Height / pictureBox1.Height;
using (Graphics gr = Graphics.FromImage(bm))
{
gr.DrawRectangle(Pens.Red,
Math.Min(x0, x1) * zoomX, Math.Min(y0, y1) * zoomY,
Math.Abs(x0 - x1) * zoomX, Math.Abs(y0 - y1) * zoomY
);
}
This fixes the red rectangle in my case. But the copied part in picturebox2 is still not correct...
And this fixes the copy to the second picturebox:
// Copy the selected part of the image.
float zoomX = (float)original.Size.Width / pictureBox1.Width;
float zoomY = (float)original.Size.Height / pictureBox1.Height;
int wid = (int)(zoomX * Math.Abs(x0 - x1));
int hgt = (int)(zoomY * Math.Abs(y0 - y1));
if ((wid < 1) || (hgt < 1)) return;
Bitmap area = new Bitmap(wid, hgt);
using (Graphics gr = Graphics.FromImage(area))
{
Rectangle source_rectangle =
new Rectangle((int)(zoomX * Math.Min(x0, x1)), (int)(zoomY * Math.Min(y0, y1)),
wid, hgt);
Rectangle dest_rectangle =
new Rectangle(0, 0, wid, hgt);
gr.DrawImage(original, dest_rectangle,
source_rectangle, GraphicsUnit.Pixel);
}
I'm trying to draw Right-Aligned text in a custom control, however, it seems for some reason it doesn't align to my target horizontal position and there's a difference between strings.
I can live with the fact that it doesn't exactly matches my target horizontal position, but the difference between strings is visually awful!
Any pointers?
The isolated code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace RightAlignTest {
class RightControlTest : UserControl {
public RightControlTest() {
this.SetStyle(ControlStyles.UserPaint, true);
this.SetStyle(ControlStyles.ResizeRedraw, true);
}
public static void DrawString(Graphics g, string s, Font f, RectangleF r, Color c) {
float locx = r.Left;
float locy = r.Top;
SizeF txts = g.MeasureString(s, f);
locx = (locx + r.Width - txts.Width);
g.DrawString(s, f, new SolidBrush(c), locx, locy);
}
protected override void OnPaint(PaintEventArgs e) {
base.OnPaint(e);
int rightTarget = Width - 20;
Font f = new Font("Arial Unicode MS", 13f, FontStyle.Regular);
int i = 0;
string[] strings = { "Current Limit 1:", "Current Limit 2:", "Temperature Center 1:", "Temperature Center 2:" };
foreach (var s in strings) {
Rectangle r1 = new Rectangle(0, 30 * i++, rightTarget, Height);
DrawString(e.Graphics, s, f, r1, Color.Black);
}
e.Graphics.DrawLine(new Pen(new SolidBrush(Color.Blue)), rightTarget, 0, rightTarget, Height);
}
}
public partial class Form1 : Form {
public Form1() {
RightControlTest t = new RightControlTest();
t.Dock = DockStyle.Fill;
Controls.Add(t);
}
}
}
Try if this works:
public static void DrawString(
Graphics g, string s, Font f,
RectangleF r, Color c)
{
StringFormat stringFormat = new StringFormat();
stringFormat.Alignment = StringAlignment.Far;
stringFormat.LineAlignment = StringAlignment.Center; // Not necessary here
g.DrawString(s, f, new SolidBrush(c), r, stringFormat);
}
using StringFormat example taken from http://msdn.microsoft.com/en-us/library/332kzs7c.aspx
Use StringFormat.SetTabStop(int,float[]), like this:
private void PrintPage_Print(object sender, PrintPageEventArgs e)
{
using (Font f = new Font("Segoe UI", 10f, FontStyle.Regular, GraphicsUnit.Point))
{
using (Brush b = new SolidBrush(Color.Black))
{
using (Graphics g = e.Graphics)
{
float y = 5;
string headLine = "Article\tUnit\tNet\tGross";
Pen pen = new Pen(b);
SizeF measure = g.MeasureString(headLine, f);
StringFormat format = new StringFormat();
float[] tabs = new float[] { 200, 100, 55, 55};
Rectangle rect = new Rectangle(5, (int)y, (int)(tabs.Sum()+5), (int)measure.Height);
format.SetTabStops(0, tabs);
g.DrawString(headLine, f, b, rect, format);
g.DrawRectangle(pen, rect);
y += rect.Height + 3f;
format.LineAlignment = StringAlignment.Far;
foreach (var product in Bill.ListPositions())
{
measure = g.MeasureString(product.PositionString, f);
rect = new Rectangle(5, (int)y, (int)(tabs.Sum()+5), (int)measure.Height);
g.DrawString(product.PositionString, f, b, rect, format);
y += measure.Height + 2f;
}
g.DrawLine(pen, new Point(0, (int)y), new Point((int)(tabs.Sum()), (int)y));
tabs = new float[] { 300, 110 };
format.LineAlignment = StringAlignment.Near;
format.SetTabStops(0, tabs);
foreach (var line in DrawTotalSummaryLines())
{
measure = g.MeasureString(line, f);
rect = new Rectangle(5, (int)y, (int)(tabs.Sum()+5), (int)measure.Height);
g.DrawString(line, f, b, rect, format);
y += measure.Height + 2f;
if (line.Contains("Gross:") ||line.Contains("CHANGE:"))
{
g.DrawLine(pen, new Point(0, (int)y), new Point((int)(tabs.Sum()), (int)y));
y += measure.Height + 2f;
}
}
g.Dispose();
pen.Dispose();
}
b.Dispose();
}
f.Dispose();
}
The result should look like this: