In my code, let's say I have the PaintObject(Graphics g). In some other function, I want to call the PaintObject function to draw something at an offset, instead of drawing it at (0,0).
I know that in Java, I could use the Graphics.create(x, y, width, height) function to create a copy of my graphics object which I could use, which would draw within those bounds of the original graphics. Is there a way to do something similarly in C#?
Just to give you an example of what my code could look like:
class MyClass : UserControl {
void PaintObject(Graphics g) {
// Example: draw 10x10 rectangle
g.DrawRectangle(new Pen(Color.Black), 0, 0, 10, 10);
}
protected override void OnPaint(PaintEventArgs e) {
Graphics g = e.Graphics;
// TODO: Paint object from PaintObject() at offset (50, 50)
}
}
Set a transformation on the Graphics object:
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
Matrix transformation = new Matrix();
transformation.Translate(50, 50);
g.Transform = transformation;
}
or
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
g.TranslateTransform(50, 50);
}
Use the Graphics method
public void TranslateTransform(float dx, float dy)
g.TranslateTransform(dx, dy);
You can use the Graphics.TranslateTransform method:
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.TranslateTransform(50, 50);
PaintObject(e.Graphics);
}
Related
I am using Emgucv and Zxing to create a QR Code Scanner And I have created a customer PictureBox to scan QR Code.The webcam is responsible for scanning the QR Code to PictureBox.And I also want to make an animation of a scan in PictureBox.The code works fine but the animation of the red line doesn't show up in the PictureBox when I start to scan QR code.Here's my Code:
Customer picturebox:
public partial class qrCodeViewer : PictureBox
{
int y = 0;
public Timer _timer;
public qrCodeViewer()
{
InitializeComponent();
}
public void _timer_Tick(object sender, EventArgs e)
{
y += 10;
if (y >= 300)
{
y = 0;
}
Invalidate();
}
protected override void OnPaint(PaintEventArgs pe)
{
Graphics g = pe.Graphics;
g.DrawLine(new Pen(Color.Red, 2f), new Point(0, y), new Point(360, y));
base.OnPaint(pe);
}
}
WinForms:
private void readQR_Click(object sender, EventArgs e)
{
qrRealTime._timer = new Timer();
qrRealTime._timer.Start();
qrRealTime._timer.Interval = 50;
qrRealTime._timer.Tick += new EventHandler(qrRealTime._timer_Tick);
openWebCam();
_timer.Start();
_timer.Tick += new EventHandler(TimerEventProcessor);
}
private void TimerEventProcessor(object sender, EventArgs e)
{
Image<Bgr, Byte> frame = new Image<Bgr, Byte>(360, 280);
frame = cap.QueryFrame();
qrRealTime.Image = frame.ToBitmap();
ZXing.IBarcodeReader reader = new ZXing.BarcodeReader();
ZXing.Result result = reader.Decode(frame.ToBitmap());
if (result != null)
{
}
}
My problem is how to show up the animation in PictureBox?
WinForms's image
The call order in the OnPaint method matters.
First call
base.OnPaint(pe);
to draw the beneath image.
Then call the other two lines to draw the red line. Now the code looks like
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
Graphics g = pe.Graphics;
g.DrawLine(new Pen(Color.Red, 2f), new Point(0, y), new Point(360, y));
}
I have MainForm class. Here I can make somthing like this.
private void MainForm_Paint(object sender, PaintEventArgs e)
{
Graphics graphics = this.CreateGraphics();
Rectangle rectangle = new Rectangle(50, 100, 150, 150);
graphics.DrawEllipse(Pens.Black, rectangle);
graphics.DrawRectangle(Pens.Red, rectangle);
}
And I can see result in my Form.
But I have another class Image. And I want to draw from here. How can I do it?
Send the PaintEventArgs (the below came from one i have been using)
class Draw
{
public void Paint(PaintEventArgs e)
{
e.Graphics.DrawRectangles(Pens.Blue, GetRectangle());
}
}
where GetRectangle would be another method to define the rectangle
you should also be able to just send your object (in your case the instance of MainForm)
class Draw
{
public void Paint(MainForm main)
{
Graphics graphics = main.CreateGraphics();
}
}
or the graphics object
class Draw
{
public void Paint(Graphics graphics)
{
Rectangle rectangle = new Rectangle(50, 100, 150, 150);
graphics.DrawEllipse(Pens.Black, rectangle);
graphics.DrawRectangle(Pens.Red, rectangle);
}
}
you still need the event handler for the PictureBox, so you would do something like
private void MainForm_Paint(object sender, PaintEventArgs e)
{
Graphics graphics = this.CreateGraphics();
Draw image = new Draw();
image.Paint(graphics);
}
I have found this question (as a few others), but this is the one I have implemented so far:
Crosshair cursor with additional lines in C#
As it states, I can use a stock cursor "cross" directly in the IDE. This is a really good way to do things. The answer specified in the answer above draws a cross on the screen at the given width / height. Eg:
private Cursor crossCursor(Pen pen, Brush brush, int x, int y)
{
var pic = new Bitmap(x, y);
Graphics gr = Graphics.FromImage(pic);
var pathX = new GraphicsPath();
var pathY = new GraphicsPath();
pathX.AddLine(0, y / 2, x, y / 2);
pathY.AddLine(x / 2, 0, x / 2, y);
gr.DrawPath(pen, pathX);
gr.DrawPath(pen, pathY);
IntPtr ptr = pic.GetHicon();
var c = new Cursor(ptr);
return c;
}
My issue is that I want my cross hairs to extend to the Bounds of the viewing area. To provide context here, I have:
//Form
//TableLayoutPanel
//UserControl (fills the TableLayoutPanel visible area)
So how can I adjust my cursor so that the lines extend (much like in CAD pacakages)?
Thanks.
Update: I have tried calling the method from here:
protected override void OnLoad(System.EventArgs e)
{
Cursor = crossCursor(Pens.WhiteSmoke, Brushes.WhiteSmoke, Bounds.Width, Bounds.Height);
}
But it is not Ok because at this point in time Bounds is returning a dimension of 150 by 150 which is not the size of the TableLayoutPanel.
Update: I have adjuted it to use the Resize handler instead and it does improve things:
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
Cursor = crossCursor(Pens.WhiteSmoke, Brushes.WhiteSmoke, Bounds.Width, Bounds.Height);
}
The only problem now (and it kind of makes sense I suppose) is that the cursor will only take the full width and height of the view when it is central to the view. As soon as I move about in the view that cursor does not adjust. I always want a horizontal/vertical line through the mouse position (not just the initial cross).
See:
The crosshairs need extending (the thicker red lines). Either I need to constantly create the cursor as the mouse moves or construct the two lines another way. What to do?
I came across this:
https://social.msdn.microsoft.com/Forums/vstudio/en-US/7bdbad6d-1f65-461b-8f0c-6ef4f243fa6b/crosshair-cursor-using-c?forum=csharpgeneral
So, instead of changing the cursor object I now draw lines in the controls MouseMove handler:
Region r = new Region();
r.Union(new Rectangle(0, lastY, this.Width, 1));
r.Union(new Rectangle(lastX, 0, 1, this.Height));
this.Invalidate(r);
this.Update();
Graphics g = Graphics.FromHwnd(this.Handle);
g.DrawLine(Pens.White, 0, e.Y, this.Width, e.Y);
g.DrawLine(Pens.White, e.X, 0, e.X, this.Height);
int intDiameter = 20;//the diameter of this circle
g.DrawEllipse(Pens.White, e.X - intDiameter / 2, e.Y - intDiameter / 2, 20, 20);
//to draw the circle
lastX = e.X;
lastY = e.Y;
It works, but I get noticiable screen flicker doing it this way.
You don't need to create a cursor. You can create a double buffered control and draw cross over control.
using System;
using System.Drawing;
using System.Windows.Forms;
public class DrawingSurface : Control
{
Pen crossPen;
Pen rectanglePen;
Brush rectangleBrush;
public DrawingSurface()
{
this.DoubleBuffered = true;
this.ResizeRedraw = true;
crossPen = new Pen(Color.Red, 2);
rectangleBrush = new SolidBrush(Color.FromArgb(50, Color.Blue));
rectanglePen = new Pen(Color.Blue, 1);
}
bool mouseDown = false;
Point startPoint = Point.Empty;
Point endPoint = Point.Empty;
protected override void OnMouseDown(MouseEventArgs e)
{
startPoint = e.Location;
mouseDown = true;
base.OnMouseDown(e);
}
protected override void OnMouseUp(MouseEventArgs e)
{
mouseDown = false;
base.OnMouseUp(e);
}
protected override void OnMouseMove(MouseEventArgs e)
{
endPoint = e.Location;
this.Invalidate();
base.OnMouseMove(e);
}
protected override void OnPaint(PaintEventArgs e)
{
var g = e.Graphics;
if (this.ClientRectangle.Contains(endPoint))
DrawCross(e.Graphics, endPoint);
if (mouseDown)
DrawRectangle(e.Graphics, startPoint, endPoint);
}
void DrawCross(Graphics g, Point point)
{
g.DrawLine(crossPen, new Point(0, point.Y), new Point(Width, point.Y));
g.DrawLine(crossPen, new Point(point.X, 0), new Point(point.X, Height));
}
void DrawRectangle(Graphics g, Point point1, Point point2)
{
var rectangle = new Rectangle(
Math.Min(point1.X, point2.X), Math.Min(point1.Y, point2.Y),
Math.Abs(point1.X - point2.X), Math.Abs(point1.Y - point2.Y));
g.FillRectangle(rectangleBrush, rectangle);
g.DrawRectangle(rectanglePen, rectangle);
}
protected override void Dispose(bool disposing)
{
crossPen.Dispose();
rectanglePen.Dispose();
rectangleBrush.Dispose();
base.Dispose(disposing);
}
}
The problem isn't that I don't know how to make a border-less form re-sizable, or to how to draw a border. The problem is what happens when you re-size the form with that custom border.
Here is a screenshot, because I don't know how to explain it:
Here is how I created the border (currently):
private void Form1_Paint(object sender, PaintEventArgs e)
{
int width = 1;
Rectangle rec = this.ClientRectangle;
ButtonBorderStyle bbs = ButtonBorderStyle.Solid;
Color clr = Color.Gray;
ControlPaint.DrawBorder(e.Graphics, rec, clr, width, bbs, clr, width, bbs, clr, width, bbs, clr, width, bbs);
}
As for re-sizing a border-less form; I created a repository for the project.
Resize Custom Border - Bitbucket
I don't have any idea as to why this happens, so I wouldn't know where to begin. I just need to draw a border without it doing this. I have tried other ways of drawing one, but the results were the same.
Hopefully this and the repository becomes useful for anyone trying to do the same.
Thank you for taking your time to read if you did.
Try to use Graphics.DrawRectangle instead of DrawBorder
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Single fWidth = 5.0f;
Rectangle r = new Rectangle(0,0,this.ClientRectangle.Width-1,this.ClientRectangle.Height-1);
e.Graphics.DrawRectangle(new Pen(Color.Gray, fWidth), r);
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
this.Invalidate();
}
Use Graphic library :
Step 1: Override the OnPaint handler for your main form
Step 2: Define a rectangle that covers your current form
Step 3: Draw the defined rectangle
protected override void OnPaint(PaintEventArgs e)
{
Rectangle r = new Rectangle(0,0,this.ClientRectangle.Width-1,this.ClientRectangle.Height-1);
e.Graphics.DrawRectangle(new Pen(Color.Gray, 1.0f), r);
}
You may also implement this using a condition statement like:
this.form.Resize += // some handler1
//in hadler1
{
this.form.Paint += // Your new paint handler2
}
//in handler2
{
Rectangle r = new Rectangle(0,0,this.ClientRectangle.Width-1,this.ClientRectangle.Height-1);
e.Graphics.DrawRectangle(new Pen(Color.Gray, 1.0f), r);
}
Can you explain me what's wrong with this code? because it is not drawing anything.
doesn't it suppose to draw a rectangle in my form? thanks!
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Graphics g = this.CreateGraphics();
Rectangle r = new Rectangle(0, 0, 150, 150);
g.DrawRectangle(System.Drawing.Pens.Black, r);
}
}
Make your painting in the OnPaint method:
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Graphics g = e.Graphics;
Rectangle r = new Rectangle(0, 0, 150, 150);
g.DrawRectangle(System.Drawing.Pens.Black, r);
}