Hi all I am trying to create a program that takes commands from the user to draw shapes on the screen. The program should have basic commands to draw shapes and a clear command to clear the screen afterwards, the user should be able to type moveTo in the text box and the pen should move the the coordinates they set it to then the user should be able to type in the shape they want to draw(circle(radius), rectangle(width, height), triangle(side, side, side). I have got it to work for the rectangle as i want however struggling to implement the other parts can anyone help.
public partial class Form1 : Form
{
String input;
Bitmap drawOutput;
String command, command2, command3;
int x, y;
public Form1()
{
InitializeComponent();
drawOutput = new Bitmap(OutputBox.Size.Width, OutputBox.Size.Height);
OutputBox.Image = drawOutput;
}
private void Form1_Load(object sender, EventArgs e)
{
Graphics g;
g = Graphics.FromImage(drawOutput);
Pen mypen = new Pen(Color.Black);
g.Clear(Color.White);
g.Dispose();
}
private void CMDBox_TextChanged(object sender, EventArgs e)
{
input = CMDBox.Text;
}
private void ExecuteBtn_Click(object sender, EventArgs e)
{
string[] spilt = input.Split(' ');
foreach (String words in spilt)
{
command = spilt[0];
command2 = spilt[1];
command3 = spilt[2];
x = Int32.Parse(command2);
y = Int32.Parse(command3);
Graphics g;
g = Graphics.FromImage(drawOutput);
Pen pen = new Pen(Color.Black, 5);
if (command == "circle")
{
g.DrawEllipse(pen, 0, 0, x, y);
setImage(g);
}
else if (command == "rectangle")
{
g.DrawRectangle(pen, 0, 0, x, y);
setImage(g);
}
}
}
public void setImage(Graphics g)
{
g = Graphics.FromImage(drawOutput);
OutputBox.Image = drawOutput;
g.Dispose();
}
}
The basic context of this program is to create a really basic programming language which takes the users input and display it on screen
You don't need to declare a class level Bitmap (drawOutput) nor create Graphics object from it. You need to do the drawing in the Paint event handler. Try the following:
Declare enumeration for your shapes, class level variables to hold the type of the shape, x and y coordinates, and the size of the shape w and h.
enum Shapes
{
Ellipse,
Rectangle,
Line
}
int x, y, w, h;
Shapes shape;
Validate the user's input in the ExecuteBtn click event, assign the values, and call the OutputBox.Invalidate() if the input is correct.
var cmd = textBox1.Text.Split(' ');
var validCmds = Enum.GetNames(typeof(Shapes));
if (cmd.Length < 5 || !validCmds.Contains(cmd[0], StringComparer.OrdinalIgnoreCase))
{
MessageBox.Show("Enter a valid command.");
return;
}
if(!int.TryParse(cmd[1], out x) ||
!int.TryParse(cmd[2], out y) ||
!int.TryParse(cmd[3], out w) ||
!int.TryParse(cmd[4], out h))
{
MessageBox.Show("Enter a valid shape");
return;
}
shape = (Shapes)validCmds.ToList().FindIndex(a => a.Equals(cmd[0], StringComparison.OrdinalIgnoreCase));
OutputBox.Invalidate();
Draw the shape in the Paint event.
private void OutputBox_Paint(object sender, PaintEventArgs e)
{
if (w > 0 && h > 0)
{
e.Graphics.Clear(Color.White);
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
using (Pen pn = new Pen(Color.Black, 5))
{
switch (shape)
{
case Shapes.Ellipse:
e.Graphics.DrawEllipse(pn, x, y, w, h);
break;
case Shapes.Rectangle:
e.Graphics.DrawRectangle(pn, x, y, w, h);
break;
case Shapes.Line:
e.Graphics.DrawLine(pn, x, y, w, h);
break;
}
}
}
}
Better yet, the Shape class approach as commented below.
Create the Shape class to enumerate your shapes, declare properties for them, and a method to parse a given command string and outs the required shape on success:
public class Shape
{
#region enums.
public enum Shapes
{
Ellipse,
Rectangle,
Line
}
#endregion
#region Constructors
public Shape()
{ }
public Shape(Shapes shape, int x, int y, int width, int height)
{
ThisShape = shape;
X = x;
Y = y;
Width = width;
Height = height;
}
#endregion
#region Properties
public Shapes ThisShape { get; set; } = Shapes.Ellipse;
public int X { get; set; }
public int Y { get; set; }
public int Width { get; set; }
public int Height { get; set; }
#endregion
#region Methods
public static bool TryParse(string input, out Shape result)
{
result = null;
if (string.IsNullOrEmpty(input))
return false;
var cmd = input.Split(' ');
var validCmds = Enum.GetNames(typeof(Shapes));
if (cmd.Length < 5 || !validCmds.Contains(cmd[0], StringComparer.OrdinalIgnoreCase))
return false;
int x, y, w, h;
if (!int.TryParse(cmd[1], out x) ||
!int.TryParse(cmd[2], out y) ||
!int.TryParse(cmd[3], out w) ||
!int.TryParse(cmd[4], out h))
return false;
if (w <= 0 || h <= 0)
return false;
var shape = (Shapes)validCmds.ToList().FindIndex(a => a.Equals(cmd[0], StringComparison.OrdinalIgnoreCase));
result = new Shape(shape, x, y, w, h);
return true;
}
#endregion
}
In your Form, declare a shape variable:
Shape shape;
The ExecuteBtn click event:
shape = null;
if (!Shape.TryParse(textBox1.Text, out shape))
{
MessageBox.Show("Enter a valid command.");
return;
}
OutputBox.Invalidate();
Finally, your paint event:
private void OutputBox_Paint(object sender, PaintEventArgs e)
{
if (shape != null)
{
e.Graphics.Clear(Color.White);
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
using (Pen pn = new Pen(Color.Black, 5))
{
switch (shape.ThisShape)
{
case Shape.Shapes.Ellipse:
e.Graphics.DrawEllipse(pn, shape.X, shape.Y, shape.Width, shape.Height);
break;
case Shape.Shapes.Rectangle:
e.Graphics.DrawRectangle(pn, shape.X, shape.Y, shape.Width, shape.Height);
break;
case Shape.Shapes.Line:
//Note: the Width and Height properties play here the X2 and Y2 roles.
e.Graphics.DrawLine(pn, shape.X, shape.Y, shape.Width, shape.Height);
break;
}
}
}
}
Related
I would like to design a chess board and drag over pieces (shown in pictureBox Controls) all child of the main board (pictureBox1).
Problem I encounter is the transparency is only set to the Parent pictureBox1.
Which shows this effect: The square is showing.
private void CommonPiece_Mouse_Move(object sender, MouseEventArgs e)
{
if (Piece_Selected)
{
int MousePositionX = pictureBox1.PointToClient(Cursor.Position).X;
int MousePositionY = pictureBox1.PointToClient(Cursor.Position).Y;
(sender as PictureBox).Left = MousePositionX - 35;
(sender as PictureBox).Top = MousePositionY - 25;
}
}
What would be a good way to go about it?
There are several solutions for this problem. The one in the following screen recording, is based on drawing png images as movable shapes; it uses the idea that I've explained in How to drag and move shapes in C#. Basically there's only one control -the drawing surface, and all the other stuff are movable drawings.
You can use any image for the pieces by passing an Image to the ImageShape
You can modify the size of the Pieces by setting Height of the shape
It supports snapping to the grid
You can customize the texture, easily by setting the BackgroundImage of the drawing surface
You can change the grid size, by setting GridSize property of the drawing surface
You can change the Color of White and Grid grids, by assigning the color to WhiteColor and BlackColor
It's of course a quick example showing how to draw movable objects, including png images keeping their transparency. You know how to improve it :)
Drawing and moving shapes - Chess pieces
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using System.Linq;
using System.Windows.Forms;
public interface IShape
{
bool HitTest(Point p);
void Draw(Graphics g);
void Move(int dx, int dy);
Point Location { get; set; }
}
public class ImageShape : IShape
{
public int Height { get; set; } = 100;
public Point Location { get; set; }
private Image _image;
public ImageShape(Image image)
{
_image = image;
}
public void Draw(Graphics g)
{
var r = new Rectangle(Location, new Size(Height, Height));
r.Inflate(-5, -5);
g.DrawImage(_image, r);
}
public bool HitTest(Point p)
{
return new Rectangle(Location, new Size(Height, Height)).Contains(p);
}
public void Move(int dx, int dy)
{
Location = new Point(Location.X + dx, Location.Y + dy);
}
}
public class DrawingSurface : Control
{
public List<IShape> Shapes { get; private set; }
public int GridSize { get; set; } = 100;
public Color WhiteColor = Color.FromArgb(200, Color.White);
public Color BlackColor = Color.FromArgb(120, Color.Black);
IShape selectedShape;
bool moving;
Point previousPoint = Point.Empty;
public DrawingSurface()
{
DoubleBuffered = true;
ResizeRedraw = true;
Shapes = new List<IShape>();
}
protected override void OnMouseDown(MouseEventArgs e)
{
for (var i = Shapes.Count - 1; i >= 0; i--)
if (Shapes[i].HitTest(e.Location))
{
selectedShape = Shapes[i];
break;
}
if (selectedShape != null)
{
moving = true;
previousPoint = e.Location;
Invalidate();
}
base.OnMouseDown(e);
}
protected override void OnMouseMove(MouseEventArgs e)
{
if (moving)
{
var dx = e.X - previousPoint.X;
var dy = e.Y - previousPoint.Y;
selectedShape.Move(dx, dy);
previousPoint = e.Location;
this.Invalidate();
}
base.OnMouseMove(e);
}
protected override void OnMouseUp(MouseEventArgs e)
{
if (moving)
{
int i = (e.X / GridSize) * GridSize;
int j = (e.Y / GridSize) * GridSize;
selectedShape.Location = new Point(i, j);
selectedShape = null;
moving = false;
this.Invalidate();
}
base.OnMouseUp(e);
}
protected override void OnPaint(PaintEventArgs e)
{
var g = e.Graphics;
g.InterpolationMode = InterpolationMode.High;
g.SmoothingMode = SmoothingMode.HighQuality;
g.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
g.CompositingQuality = CompositingQuality.HighQuality;
foreach (var shape in Shapes.Except(new[] { selectedShape }))
shape.Draw(g);
if (selectedShape != null)
selectedShape.Draw(g);
}
protected override void OnPaintBackground(PaintEventArgs e)
{
base.OnPaintBackground(e);
var g = e.Graphics;
using (var w = new SolidBrush(WhiteColor))
using (var b = new SolidBrush(BlackColor))
for (int i = 0; i < 8; i++)
for (int j = 0; j < 8; j++)
g.FillRectangle((i + j) % 2 == 0 ? b : w,
i * GridSize, j * GridSize, GridSize, GridSize);
}
}
To add pieces:
private void Form1_Load(object sender, EventArgs e)
{
this.drawingSurface1.Shapes.Add(new ImageShape(
Properties.Resources.KingWhite) { Location = new Point(0, 0) });
this.drawingSurface1.Shapes.Add(
new ImageShape(Properties.Resources.kingBlack) { Location = new Point(0, 100) });
}
Here's an example that makes the PictureBox Non-Rectangular. The PB will literally not exist where the transparent areas were:
using System.Runtime.InteropServices;
namespace CS_Scratch_WindowsFormsApp2
{
public partial class Form1 : Form
{
public const int HT_CAPTION = 0x2;
public const int WM_NCLBUTTONDOWN = 0xA1;
[System.Runtime.InteropServices.DllImportAttribute("user32.dll")]
public static extern bool ReleaseCapture();
[System.Runtime.InteropServices.DllImportAttribute("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
public Form1()
{
InitializeComponent();
pbChessPiece.MouseMove += PbChessPiece_MouseMove;
}
private void PbChessPiece_MouseMove(object sender, MouseEventArgs e)
{
PictureBox pb = (PictureBox)sender;
if (!DesignMode && e.Button == MouseButtons.Left)
{
ReleaseCapture();
SendMessage(pb.Handle, WM_NCLBUTTONDOWN, HT_CAPTION, 0);
}
}
private void button1_Click(object sender, EventArgs e)
{
Bitmap bmp = (Bitmap)pbChessPiece.Image;
Region rgn = new Region();
rgn.Union(new Rectangle(0, 0, bmp.Width, bmp.Height));
for(int x=0; x<bmp.Width; x++)
{
for(int y=0; y<bmp.Height; y++)
{
if (bmp.GetPixel(x, y).A == 0)
{
rgn.Exclude(new Rectangle(x, y, 1, 1));
}
}
}
pbChessPiece.Region = rgn;
}
}
}
Example run:
I did something similar, but I did not use Drag & Drop events
On MouseDown I assumed a drag operation started, I've opened a translucent unfocused window on top of the drop area, and started drawing dragged image on that form. Since the form is translucent it can show images with transparency on top of everything. I did not use images, but instead draw rectangles where users could drop. If my memory serves me correct, MouseMove events are forwarded to the dragged component, the original chess piece in your case, or the opened but unfocused form, so I can easily track the location. On MouseUp event I wrapped everything.
Since you can draw the image on the new form you can tilt image chess piece image to the left or right for a nice touch.
I developed a very strict forms designer application using this technique.
You can use the panel instead of using the pictureBox and display the image in the background of the controls. Remember that your images must be "png" and the surrounding of the image must be completely transparent. Also, the background color of the controls of the panels Set by events so that it becomes transparent during drag and returns to the cell color after drag and drop.
Good luck.
Pic of Form
I am trying to get input from the user to set the x, y coordinates to the DrawEllipse function, when i hard code the values into it it draws a circle on the screen just fine however when i try passing in a int that has come from user input it doesn't work.
I am trying to make a program that responds to commands from users e.g. the user can type in "draw shape x y".
The string is being split when a space is detected the string is split into an array.
public partial class Form1 : Form
{
String input;
Bitmap drawOutput;
String command, command2, command3;
int x, y;
public Form1()
{
InitializeComponent();
drawOutput = new Bitmap(OutputBox.Size.Width, OutputBox.Size.Height);
OutputBox.Image = drawOutput;
}
private void Form1_Load(object sender, EventArgs e)
{
Graphics g;
g = Graphics.FromImage(drawOutput);
Pen mypen = new Pen(Color.Black);
g.Clear(Color.White);
g.Dispose();
}
private void CMDBox_TextChanged(object sender, EventArgs e)
{
input = CMDBox.Text;
}
private void ExecuteBtn_Click(object sender, EventArgs e)
{
String[] spilt = input.Split(' ');
foreach (String words in spilt)
{
command = spilt[0];
command2 = spilt[1];
command3 = spilt[2];
x = Int32.Parse(command2);
y = Int32.Parse(command3);
Graphics g;
g = Graphics.FromImage(drawOutput);
Pen pen = new Pen(Color.Black, 5);
if (input == command)
{
g.DrawEllipse(pen, 0, 0, x, y);
OutputBox.Image = drawOutput;
g.Dispose();
}
}
}
}
Can anyone help?
I'm drawing rectangles in a panel starting from the left side.
When I reach the right side of the panel I'd like to shift left the rectangles previously drawn to have the space to draw another one, and so on.
Which is the simplest way to do it?
I'm drawing using System.Drawings.Graphics.
I'm using Winforms. The code is:
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;
using System.Runtime.InteropServices;
namespace FFViewer
{
public partial class Form1 : Form
{
[DllImport("shlwapi.dll")]
public static extern int ColorHLSToRGB(int H, int L, int S);
int x=0;
int y=300;
int h = 0;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
panel_Paint();
x = x + 40;
h = h + 40;
if (h >= 240)
h = 0;
}
private void panel_Paint()
{
int val;
Color color;
val = ColorHLSToRGB(h, 128, 240);
Graphics g = panel1.CreateGraphics();
color = ColorTranslator.FromWin32(val);
SolidBrush sb = new SolidBrush( color );
g.FillRectangle(sb, x, y, 40, 100);
}
}
}
So, when I draw the latest rectangle on the right side, I'd like to shift left all the rectangles to leave the space to draw another one on the right side.
P.S. I don't have enough reputation to post images :(
Here's the "old school" way of doing it. This is basically what was done when a continuous graph of a real-time value needed to be displayed AND you didn't want to store the any of the values anywhere. This has severe limitations as it copies from the screen and the drawing will be erased the when window repaints itself. This first example is simply here to demonstrate the process, and is an extension of the way you created the initial blocks:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
[DllImport("shlwapi.dll")]
public static extern int ColorHLSToRGB(int H, int L, int S);
int x = 0;
int width = 40;
int y = 300;
int height = 100;
int h = 0;
private void button1_Click(object sender, EventArgs e)
{
if (x + width > panel1.ClientSize.Width) // if drawing the next block would exceed the panel width...
{
// capture what's currently on the screen
Bitmap bmp = new Bitmap(x, height);
using (Graphics g = Graphics.FromImage(bmp))
{
g.CopyFromScreen(panel1.PointToScreen(new Point(0, y)), new Point(0, 0), bmp.Size);
}
// draw it shifted to the left
using (Graphics g = panel1.CreateGraphics())
{
g.DrawImage(bmp, new Point(-width, y));
}
// move x back so the new rectangle will draw where the last one was previously
x = x - width;
}
// draw the new block and increment values
panel_Paint();
x = x + width;
h = h + width;
if (h >= 240)
{
h = 0;
}
}
private void panel_Paint()
{
int val;
Color color;
val = ColorHLSToRGB(h, 128, 240);
color = ColorTranslator.FromWin32(val);
using (Graphics g = panel1.CreateGraphics())
{
using (SolidBrush sb = new SolidBrush(color))
{
g.FillRectangle(sb, x, y, width, height);
}
}
}
}
This could be fixed by creating a Bitmap of the correct size and drawing to that instead. Then you shift everything and draw the new block on the right side. Finally, you'd draw that Bitmap in the Paint() event. So this is doing the same thing as above except we aren't copying from the screen, and the panel will properly redraw itself when requested:
public partial class Form1 : Form
{
[DllImport("shlwapi.dll")]
public static extern int ColorHLSToRGB(int H, int L, int S);
int x = 0;
int width = 40;
int y = 300;
int height = 100;
int h = 0;
Bitmap bmp;
public Form1()
{
InitializeComponent();
this.Load += Form1_Load;
panel1.Paint += Panel1_Paint;
}
private void Form1_Load(object sender, EventArgs e)
{
int numBlocks = (int)(panel1.Width / width);
bmp = new Bitmap(numBlocks * width, height);
using (Graphics g = Graphics.FromImage(bmp))
{
g.Clear(panel1.BackColor);
}
}
private void Panel1_Paint(object sender, PaintEventArgs e)
{
if (bmp != null)
{
e.Graphics.DrawImage(bmp, new Point(0, y));
}
}
private void button1_Click(object sender, EventArgs e)
{
using (Graphics g = Graphics.FromImage(bmp))
{
if (x + width > bmp.Width) // if drawing the next block would exceed the bmp width...
{
g.DrawImage(bmp, new Point(-width, 0)); // draw ourself shifted to the left
x = x - width;
}
// draw the new block
int val;
Color color;
val = ColorHLSToRGB(h, 128, 240);
color = ColorTranslator.FromWin32(val);
using (SolidBrush sb = new SolidBrush(color))
{
g.FillRectangle(sb, x, 0, width, height);
}
}
x = x + width;
h = h + width;
if (h >= 240)
{
h = 0;
}
panel1.Invalidate(); // force panel1 to redraw itself
}
}
You should not use panel1.CreateGraphics(), but instead handle the Paint event of the panel, otherwise the rectangles might disappear, for instance after a popup appears in front of your form:
panel1.Paint += new PaintEventHandler(panel1_paint);
You'll need to paint all (visible) rectangles in the paint handler; you could keep a List<Rectangle> in your form to store the rectangles you have added:
private List<Rectangle> rectangles = new List<Rectangle>();
...
private void button1_Click(object sender, EventArgs e)
{
rectangles.Add(new Rectangle(x, y, width, height));
panel1.Invalidate(); // cause the paint event to be called
// todo increment x/y
}
Then, in the panel1_Paint handler you can simply draw the rectangles, after having called Graphics.TranslateTransform() to shift the whole drawing area:
private void panel1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.TranslateTransform(-x, 0);
foreach (Rectangle rectangle in rectangles)
{
// paint em
}
e.Graphics.ResetTransform();
}
I'm trying to add a control to my Panel. At mouseDown on the panel the point is saved and at mouseUp the point is saved. But at panel mouseUp nothing is drawn. How to solve it?
Ellipse class:
class Ellipse : Control
{
private int x;
private int y;
private int width;
private int height;
public Ellipse(int x, int y, int width, int height)
{
setY(y);
setX(x);
setWidth(width);
setHeight(height);
}
public int getX() { return x;}
public int getY() { return y; }
public int getWidth() { return width; }
public int getHeight() { return height; }
public void setX(int newx) { x = newx; }
public void setY(int newy) { y = newy; }
public void setWidth(int newwidth) { width = newwidth; }
public void setHeight(int newheight) { height = newheight; }
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
// Call methods of the System.Drawing.Graphics object.
// Declare and instantiate a new pen.
System.Drawing.Pen myPen = new System.Drawing.Pen(Color.Aqua);
// Draw an aqua rectangle in the rectangle represented by the control.
e.Graphics.FillEllipse(Brushes.Black,x,y,width,height);
}
}
Form1 class
private void panel_MouseDown(object sender, MouseEventArgs e)
{
draw = true;
x = e.X;
y = e.Y;
}
private void panel_MouseUp(object sender, MouseEventArgs e)
{
draw = false;
xe = e.X;
ye = e.Y;
Item item;
Enum.TryParse<Item>(menuComboBoxShape.ComboBox.SelectedValue.ToString(), out item);
switch (item)
{
case Item.Pencil:
using (Graphics g = panel.CreateGraphics())
using (var pen = new Pen(System.Drawing.Color.Black)) //Create the pen used to draw the line (using statement makes sure the pen is disposed)
{
g.DrawLine(pen,new Point(x, y), new Point(xe, ye));
}
break;
case Item.Rectangle:
break;
case Item.Ellipse:
Ellipse el = new Ellipse(x,y,xe-x,ye-y);
panel.Controls.Add(el);
break;
default:
break;
}
}
You are inheriting your Ellipse class from Control, but in fact you're not using it as a control - you're not adding it in Controls collection of form, so in fact it is invisible, inactive and not receiving any events from form.
Also painting the control from outer code looks like a bad design. Control should paint itself, and you should set it bounds from outer code.
Here is snippet to drive you to the right way:
class Ellipse : Control
{
Point mDown { get; set; }
public Ellipse()
{
MouseDown += shape_MouseDown;
MouseMove += shape_MouseMove;
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.FillEllipse(Brushes.Black, this.Bounds);
}
private void shape_MouseDown(object sender, MouseEventArgs e)
{
mDown = e.Location;
}
private void shape_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
Location = new Point(e.X + Left - mDown.X, e.Y + Top - mDown.Y);
}
}
}
And in the form you should create it like:
el = new Ellipse();
el.Bounds = new Rectangle(0, 0, 100, 100);
Controls.Add(el);
Update
Based on your updated code, I can see a couple of issues:
You actually don't need x, y, width, height properties of your Ellipse class and according getter/setter methods, since it's Control, and it has its own Location and Width, Height public properties.
You are drawing your ellipse incorrectly. Assuming it should fill all the area, painting should be e.Graphics.FillEllipse(Brushes.Black,0,0,Width,Height) (here I assuming using Control.Width instead of your width and so on). Otherwise you're additionally shifting your painted ellipse.
Code in panel_MouseUp concerning ellipse creation should be something like
var el = new Ellipse();
panel.Controls.Add(el);
el.Location = new Point(x, y);
el.Width = (xe - x);
el.Height = (ye - y);
Or, if it should be one single ellipse (right now you're creating new one each time) - create this one outside of mouseUp handler and inside of handler just change it's size and location.
Is it possible to display a label vertically in a Windows Forms?
Labels are easy, all you have to do is override the Paint event and draw the text vertically. Do note that GDI is optimised for Drawing text horizontally. If you rotate text (even if you rotate through multiples of 90 degrees) it will looks notably worse.
Perhaps the best thing to do is draw your text (or get a label to draw itself) onto a bitmap, then display the bitmap rotated.
Some C# code for drawing a Custom Control with vertical text. Note that ClearType text NEVER works if the text is not horizontal:
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
public partial class VerticalLabel : UserControl
{
public VerticalLabel()
{
InitializeComponent();
}
private void VerticalLabel_SizeChanged(object sender, EventArgs e)
{
GenerateTexture();
}
private void GenerateTexture()
{
StringFormat format = new StringFormat();
format.Alignment = StringAlignment.Center;
format.LineAlignment = StringAlignment.Center;
format.Trimming = StringTrimming.EllipsisCharacter;
Bitmap img = new Bitmap(this.Height, this.Width);
Graphics G = Graphics.FromImage(img);
G.Clear(this.BackColor);
SolidBrush brush_text = new SolidBrush(this.ForeColor);
G.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixelGridFit;
G.DrawString(this.Name, this.Font, brush_text, new Rectangle(0, 0, img.Width, img.Height), format);
brush_text.Dispose();
img.RotateFlip(RotateFlipType.Rotate270FlipNone);
this.BackgroundImage = img;
}
}
Create a class myLabel which can rotate it's Text on any angle specified by you.
You can use it by code or simply dragging from ToolBox
using System.Drawing;
class myLabel:System.Windows.Forms.Label
{
public int RotateAngle { get; set; } // to rotate your text
public string NewText { get; set; } // to draw text
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
Brush b =new SolidBrush(this.ForeColor);
e.Graphics.TranslateTransform(this.Width / 2, this.Height / 2);
e.Graphics.RotateTransform(this.RotateAngle);
e.Graphics.DrawString(this.NewText, this.Font,b , 0f, 0f);
base.OnPaint(e);
}
}
Now this custom control is used into your form.
You have to set below properties
1. mylbl.Text = ""; //which can be changed by NewText property
2. mylbl.AutoSize = false; // adjust according to your text
3. mylbl.NewText = "Hello"; // whatever you want to display
4. mylbl.ForeColor = Color.Red; // color to display
5. mylbl.RotateAngle = -90; //angle to rotate
I expanded on Javed Akram's answer to resize the widget automatically (I needed this feature). It works for both positive and negative angles, the way that Javed states:
1. mylbl.Text = ""; // which can be changed by NewText property
2. mylbl.AutoSize = false; // adjust according to your text
3. mylbl.NewText = "Hello"; // whatever you want to display
4. mylbl.ForeColor = Color.Red; // color to display
5. mylbl.RotateAngle = -90; // angle to rotate
Here is the code:
public class RotatingLabel : System.Windows.Forms.Label
{
private int m_RotateAngle = 0;
private string m_NewText = string.Empty;
public int RotateAngle { get { return m_RotateAngle; } set { m_RotateAngle = value; Invalidate(); } }
public string NewText { get { return m_NewText; } set { m_NewText = value; Invalidate(); } }
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
Func<double, double> DegToRad = (angle) => Math.PI * angle / 180.0;
Brush b = new SolidBrush(this.ForeColor);
SizeF size = e.Graphics.MeasureString(this.NewText, this.Font, this.Parent.Width);
int normalAngle = ((RotateAngle % 360) + 360) % 360;
double normaleRads = DegToRad(normalAngle);
int hSinTheta = (int)Math.Ceiling((size.Height * Math.Sin(normaleRads)));
int wCosTheta = (int)Math.Ceiling((size.Width * Math.Cos(normaleRads)));
int wSinTheta = (int)Math.Ceiling((size.Width * Math.Sin(normaleRads)));
int hCosTheta = (int)Math.Ceiling((size.Height * Math.Cos(normaleRads)));
int rotatedWidth = Math.Abs(hSinTheta) + Math.Abs(wCosTheta);
int rotatedHeight = Math.Abs(wSinTheta) + Math.Abs(hCosTheta);
this.Width = rotatedWidth;
this.Height = rotatedHeight;
int numQuadrants =
(normalAngle >= 0 && normalAngle < 90) ? 1 :
(normalAngle >= 90 && normalAngle < 180) ? 2 :
(normalAngle >= 180 && normalAngle < 270) ? 3 :
(normalAngle >= 270 && normalAngle < 360) ? 4 :
0;
int horizShift = 0;
int vertShift = 0;
if (numQuadrants == 1)
{
horizShift = Math.Abs(hSinTheta);
}
else if (numQuadrants == 2)
{
horizShift = rotatedWidth;
vertShift = Math.Abs(hCosTheta);
}
else if (numQuadrants == 3)
{
horizShift = Math.Abs(wCosTheta);
vertShift = rotatedHeight;
}
else if (numQuadrants == 4)
{
vertShift = Math.Abs(wSinTheta);
}
e.Graphics.TranslateTransform(horizShift, vertShift);
e.Graphics.RotateTransform(this.RotateAngle);
e.Graphics.DrawString(this.NewText, this.Font, b, 0f, 0f);
base.OnPaint(e);
}
}
I found a way to simply do it without adding code or classes to your project!
When you create your label, simply add:
this.label1.text = "V\nE\nR\nT\nI\nC\nA\nL\n";
This worked for me!
You can rotate text instead of the label control in the OnPaint event or Paint method:
private void uc1_Paint(object sender, PaintEventArgs e)
{
string Name;
var g = e.Graphics;
g.DrawString(Name, new Font("Tahoma", 8), Brushes.Black, 0, 0,
new StringFormat(StringFormatFlags.DirectionVertical));
}
2015 update on an old post. Since most of the other answers seem to heavily affect VS2013's designer in terms of usability, I'd suggest this solution:
http://www.codeproject.com/Articles/19774/Extended-Vertical-Label-Control-in-C-NET
It absolutely works. I found it on net and little changed
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
using System.ComponentModel;
public class VerticalLabel : System.Windows.Forms.Label
{
private bool bFlip = true;
public VerticalLabel()
{
}
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
StringFormat stringFormat = new StringFormat();
stringFormat.Alignment = StringAlignment.Center;
stringFormat.Trimming = StringTrimming.None;
stringFormat.FormatFlags = StringFormatFlags.DirectionVertical;
Brush textBrush = new SolidBrush(this.ForeColor);
Matrix storedState = g.Transform;
if (bFlip)
{
g.RotateTransform(180f);
g.TranslateTransform(-ClientRectangle.Width,-ClientRectangle.Height);
}
g.DrawString(
this.Text,
this.Font,
textBrush,
ClientRectangle,
stringFormat);
g.Transform = storedState;
}
[Description("When this parameter is true the VLabel flips at 180 degrees."),Category("Appearance")]
public bool Flip180
{
get
{
return bFlip;
}
set
{
bFlip = value;
this.Invalidate();
}
}
}
Used pieces from others
Jeremy
public partial class VerticalLabel_UserControl : UserControl
{
private IComponentChangeService _changeService;
private string strPropertyText = "Vertical Text";
public VerticalLabel_UserControl()
{
InitializeComponent();
}
[EditorBrowsable(EditorBrowsableState.Always)]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[Bindable(true)]
public override string Text { get { return base.Text; } set { base.Text = value; this.Invalidate(); } }
private void VerticalLabel_UserControl_SizeChanged(object sender, EventArgs e)
{
GenerateTexture();
}
protected override void OnTextChanged(EventArgs e)
{
base.OnTextChanged(e);
}
private void GenerateTexture()
{
StringFormat format = new StringFormat();
format.Alignment = StringAlignment.Center;
format.LineAlignment = StringAlignment.Center;
// format.Trimming = StringTrimming.EllipsisCharacter;
Bitmap img = new Bitmap(this.Height, this.Width);
Graphics G = Graphics.FromImage(img);
G.Clear(this.BackColor);
SolidBrush brush_text = new SolidBrush(this.ForeColor);
G.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixelGridFit;
G.DrawString(this.strPropertyText, this.Font, brush_text, new Rectangle(0, 0, img.Width, img.Height), format);
img.RotateFlip(RotateFlipType.Rotate270FlipNone);
this.BackgroundImage = img;
brush_text.Dispose();
}
public override System.ComponentModel.ISite Site
{
get
{
return base.Site;
}
set
{
_changeService = (IComponentChangeService)GetService(typeof(IComponentChangeService));
if (_changeService != null)
_changeService.ComponentChanged -= new ComponentChangedEventHandler(OnComponentChanged);
base.Site = value;
if (!DesignMode)
return;
_changeService = (IComponentChangeService)GetService(typeof(IComponentChangeService));
if (_changeService != null)
_changeService.ComponentChanged += new ComponentChangedEventHandler(OnComponentChanged);
}
}
private void OnComponentChanged(object sender, ComponentChangedEventArgs ce)
{
VerticalLabel_UserControl label = ce.Component as VerticalLabel_UserControl;
if (label == null || !label.DesignMode)
return;
if (((IComponent)ce.Component).Site == null || ce.Member == null || ce.Member.Name != "Text")
return;
//Causes the default text to be updated
string strName = this.Name.ToLower();
string strText = this.Text.ToLower();
if (strText.Contains(strName))
{
this.Text = "Vertical Text";
}
else
{
strPropertyText = this.Text;
}
//Prints the text vertically
GenerateTexture();
}
}
I just turned off the AutoSize property and resized the label vertically. I made the label wide enough for only one character. Then I changed TextAlign to center to make the alignment look better. This worked great for me.