I've an oval picturebox (Code below). I want to add a border around the picturebox. I allready tried to add a second rect but this only made my region smaller. Is there any way to make a border?
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
class OvalPictureBox : PictureBox
{
public OvalPictureBox()
{
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
using (var gp = new GraphicsPath())
{
gp.AddEllipse(new Rectangle(0, 0, this.Width - 1, this.Height - 1));
this.Region = new Region(gp);
}
}
private void InitializeComponent()
{
((System.ComponentModel.ISupportInitialize)(this)).BeginInit();
this.SuspendLayout();
//
// OvalPictureBox
//
((System.ComponentModel.ISupportInitialize)(this)).EndInit();
this.ResumeLayout(false);
}
}
EDIT
I figured it out. I just draw an ellipse in the picturebox.
float penWidth = 5F;
Pen myPen = new Pen(Color.FromArgb(242, 141, 1), penWidth);
e.Graphics.DrawEllipse(myPen, new RectangleF(new PointF(0, 0), new
SizeF((float)(portraitPicture.Width - 1), portraitPicture.Height - 1)));
myPen.Dispose();
Is there a cleaner or better way? Or is this the best way?
Just draw ellipse that is bigger than the one you already have. But this ellipse should be drawn first, otherwise it would cover the other ellipse.
How much should it be bigger? Border width :)
Related
Is it possible to create another Graphics form an existing Graphics with a different coordinate system? For example, let's say that the black rectangle is the ClipRectangle of the Graphics object I got in OnPaint(). If I only want to draw inside the blue rectangle, I would have to constantly calculate the offset. That is, to draw at the logical point (0,0) in the blue rectangle, I would have to add the x and y offsets. That makes drawing code complicated.
Is there a way to create some sort of virtual Graphics using the Graphics above but with a different coordinate system? Something like below
//not actual code
var virtualG = Graphics.FromExistingGraphics(e.Graphics, 200/*x1*/, 200/*y1*/, 1000/*x2*/, 600/*y2*/);
//Doing the same thing as e.Graphics.DrawLine(pen, 200, 200, 400, 400)
virtualG.DrawLine(pen, 0, 0, 200, 200);
//Draws nothing, NOT the same as e.Graphics.DrawLine(pen, 1100, 200, 1200, 400)
//, because its outside of the virtualG's bounds.
virtualG.DrawLine(pen, 900, 0, 1000, 200);
I thought about creating a new Graphics using bitmap, draw there and then copy the bitmap to the specified location of the Graphics from OnPaint, but that could be slow.
If you don't want any scaling, and simply want to move the origin to (200, 200), then use TranslateTransform as suggested previously. If you want to clip any drawings outside that rectangle, then use SetClip.
Here's an example, showing the same set of lines drawn before and after translating and clipping. The black lines are before, while the blue line is after (the second blue line is clipped and therefore not visible). The red rectangle shows the clipping region:
Code:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public class Line
{
public Point ptA;
public Point ptB;
public void Draw(Graphics G, Color color)
{
using (Pen P = new Pen(color))
{
G.DrawLine(P, ptA, ptB);
}
}
}
private List<Line> Lines = new List<Line>();
private void Form1_Load(object sender, EventArgs e)
{
Lines.Add(new Line() { ptA = new Point(0,0), ptB = new Point(200, 200)});
Lines.Add(new Line() { ptA = new Point(900, 0), ptB = new Point(1000, 200) });
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
Graphics G = e.Graphics;
G.Clear(Color.DarkGray);
foreach(Line line in Lines)
{
line.Draw(G, Color.Black);
}
Rectangle rc = new Rectangle(new Point(200, 200), new Size(800, 400));
G.DrawRectangle(Pens.Red, rc);
G.SetClip(rc);
G.TranslateTransform(rc.Left, rc.Top); // origin is now at (200, 200)
foreach (Line line in Lines)
{
line.Draw(G, Color.Blue);
}
}
}
I'm creating a program that uses the flycapture camera. I've created a class that extends the pictureBox class in order to draw a crosshair, consisting of two lines, onto the screen. I want to be able to move the crosshair from the center to any other location on the screen.
The problem is when the form is resized, the crosshair moves to a different location as shown here. I want the crosshair to be pointing at the same part of the image as before it was resized (in the example it is no longer pointing at the grey mesh). I'm drawing the crosshair in relation to the height and width of the pictureBox. I want to be able to draw the lines on the image but the image height and width are always the same regardless of the image's size.
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace FlyCapture2SimpleGUI_CSharp
{
class IMSPictureBox : PictureBox
{
private Color colorSetting = Color.Black;
private float width = 1.0f;
public IMSPictureBox()
{
this.Paint += IMSPictureBox_Paint;
}
private void IMSPictureBox_Paint(object sender, PaintEventArgs e)
{
//Draw if image has loaded
if (this.Image != null)
{
//Draw horizontal line
e.Graphics.DrawLine(
new Pen(this.colorSetting, this.width),
new Point(0, this.Size.Height / 2 + 100),
new Point(this.Size.Width, this.Size.Height / 2 + 100));
//Draw vertical line
e.Graphics.DrawLine(
new Pen(this.colorSetting, this.width),
new Point(this.Size.Width / 2 + 100, 0),
new Point(this.Size.Width / 2 + 100, this.Size.Height));
}
}
}
}
Edit:
As DiskJunky suggested, I'm now drawing on the image itself and not using the Paint function above.
Here is the image getting set:
private void UpdateUI(object sender, ProgressChangedEventArgs e)
{
UpdateStatusBar();
pictureBox1.SetImage = m_processedImage.bitmap;
pictureBox1.Invalidate();
}
Here are the lines drawing on the image:
public System.Drawing.Image SetImage
{
set
{
using (Graphics g = Graphics.FromImage(value))
{
g.DrawLine(new Pen(Color.Red, 3.0f), new Point(0, 0), new Point(value.Width, value.Height));
g.Dispose();
}
this.Image = value;
}
get
{
return this.Image;
}
}
I now have a line that scales with the image, but now it's constantly flickering.
This isn't exact but modifying to something like the following will keep the position static as the picture box resizes;
class IMSPictureBox : PictureBox
{
private Color colorSetting = Color.Black;
private float width = 1.0f;
private Tuple<Point, Point> _verticalLine;
private Tuple<Point, Point> _horizontalLine;
public IMSPictureBox()
{
this.Paint += IMSPictureBox_Paint;
}
private void IMSPictureBox_Paint(object sender, PaintEventArgs e)
{
//Draw if image has loaded
if (this.Image != null)
{
//Draw vertical line
if (_verticalLine == null)
{
_verticalLine = new Tuple<Point, Point>(new Point(100, 0), new Point(100, this.Size.Height);
}
e.Graphics.DrawLine(
new Pen(this.colorSetting, this.width),
_verticalLine.Item1,
_verticalLine.Item2);
//Draw horizontal line
if (_horizontalLine == null)
{
_horizontalLine = new Tuple<Point, Point>(new Point(0, 100), new Point(this.Size.Width, 100);
}
e.Graphics.DrawLine(
new Pen(this.colorSetting, this.width),
_horizontalLine.Item1,
_horizontalLine.Item2);
}
}
}
Edit
The above solution outlines the concept of preserving the line position. As per discussion in comments below, this developed into a more involved solution for the OP as additional requirements came out of investigating and testing the solution above for the original intent - to cleanly draw a a coordinate marker on an image.
To that end a manual double buffering mechanism is recommended in that scenario as the PictureBox control supports limited drawing capability itself. An example of manually implementing a double buffering solution can be found here; https://learn.microsoft.com/en-us/dotnet/framework/winforms/advanced/how-to-manually-render-buffered-graphics
Rather than calling DrawEllipse() there'll be calls to DrawLine() to display the coordinate marker. One caveat is that if the image is still being displayed in the PictureBox then the PictureBoxSizeMode.Zoom value may still have to be accounted for.
I'm just starting to learn the GDI+ system for drawing lines, circles etc. I've created a component (scanner) that inherits a Panel to draw on (not sure if panel or picture box is best).
On the "Scanner" Im currently drawing a circle on it. the component can be added to a winform and using docking will resize when the winform resizes. At the moment I'm getting the size of the component to calculate the size of the circle but what I want to do is basically say no matter what size the component is the "canvas" is always 300 x 300 wide, so I can say the circle should be positioned at 25,25 with a size of 250x250.
As you might guess from the name "Scanner" I want to plot points on it, but these will be calculated from the center (150,150) location.
Below is the code I have that basically draws the circle.
Many thanks for any help on this.
public partial class Scanner : Panel
{
public Scanner() {
InitializeComponent();
this.DoubleBuffered = true;
}
protected override void OnPaint(PaintEventArgs e) {
Graphics g = e.Graphics;
Draw(g);
base.OnPaint(e);
}
protected override void OnResize(EventArgs e) {
Graphics g = this.CreateGraphics();
Draw(g);
base.OnResize(e);
}
private void Draw(Graphics g) {
g.Clear(Color.Black);
g.PageUnit = GraphicsUnit.Pixel;
Pen green = new Pen(Color.Green);
Font fnt = new Font("Arial", 10);
SolidBrush sb = new SolidBrush(Color.Red);
int pos = (this.Width < this.Height ? this.Width : this.Height) / 2;
int size = (int)(pos * 1.9);
pos -= ((int)size / 2);
g.DrawEllipse(green, pos, pos, size, size);
g.DrawString(this.Width.ToString(), fnt, sb, new Point(0, 0));
}
}
Based on your recent comment, I understand you want to do your drawing on a fixed-size canvas, and plot this canvas inside the control, as large as will fit in the control.
Try the code below:
public class Scanner : Panel
{
private Image _scanner;
public Scanner()
{
this.SetStyle(ControlStyles.ResizeRedraw, true);
CreateScanner();
}
private void CreateScanner()
{
Bitmap scanner = new Bitmap(300, 300);
Graphics g = Graphics.FromImage(scanner);
g.DrawEllipse(Pens.Green, 25, 25, 250, 250);
g.Dispose();
_scanner = scanner;
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
int shortestSide = Math.Min(this.Width, this.Height);
if (null != _scanner)
e.Graphics.DrawImage(_scanner, 0, 0, shortestSide, shortestSide);
}
}
I'm trying to draw a line that goes from middle top to bottom.
I know I have to use the Pen class to accomplish this.
private void RepaintPreview()
{
Pen blackPen = new Pen(Brushes.Black);
blackPen.Width = 1.0f;
blackPen.LineJoin = System.Drawing.Drawing2D.LineJoin.Bevel;
ptbTablePreview.Image.draw?
}
Basically, how can I draw this line on the image? Thank you.
I tried the following, and it works with me:
1- I set the image of the picturebox in design time
2- I handled the Paint event of the picturebox, and added the following code:
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
using (Pen p = new Pen(Color.Black, 2))
{
e.Graphics.DrawLine(p, new Point(pictureBox1.Width / 2, 0), new Point(pictureBox1.Width / 2, pictureBox1.Height));
}
}
I have drawn a rectangle. It is undreneath a horizontal scroll bar on screen. Now i want to zoom the rectangle. On zooming the height of rectangle increases, the location shifts up and horizontal scroll bar moves up. How to do this? I am writing this piece of code:
rect = new Rectangle(rect.Location.X, this.Height - rect.Height,rect.Width, Convert.ToInt32(rect.Size.Height * zoom));
g.FillRectangle(brush, rect);
This works for the location of the rectangle that is the rectangle shifts up but the height doesn't increase. Help!
If you simply want to scale the rectangle around the center of the rectangle then you need to increase the width and height of the rectangle and subtract half the increase from the location.
This is not tested, but should give you the general idea
double newHeight = oldHeight * scale;
double deltaY = (newHeight - oldHeight) * 0.5;
rect = new Rectangle(
rect.Location.X, (int)(rect.Location.Y - deltaY),
rect.Width, (int)newHeight);
Possibly a better alternative would be to look at using Graphics.ScaleTransform.
Just add a txtZoom to your form:
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.txtZoom.Text = "1";
this.txtZoom.KeyDown += new KeyEventHandler(txtZoom_KeyDown);
this.txtZoom_KeyDown(txtZoom, new KeyEventArgs(Keys.Enter));
}
void txtZoom_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyData == Keys.Enter)
{
this.Zoom = int.Parse(txtZoom.Text);
this.Invalidate();
}
}
public int Zoom { get; set; }
protected override void OnPaint(PaintEventArgs e)
{
GraphicsPath path = new GraphicsPath();
path.AddRectangle(new Rectangle(10, 10, 100, 100));
Matrix m = new Matrix();
m.Scale(Zoom, Zoom);
path.Transform(m);
this.AutoScrollMinSize = Size.Round(path.GetBounds().Size);
e.Graphics.FillPath(Brushes.Black, path);
}
}
}