i have to ZOOM-IN & ZOOM-OUT specific portion of 2D-graphics like (line, Rectangle, circle) which i have drawn on winform.
I am not used any picture box, panel.
I crated simple program to draw circle & on button click try to zoom
but it showing error "parameter is not valid"
in method Drawing() # Line- DeviceContexct.Transform = mainViewTransform;
public Graphics DeviceContexct;
public Matrix mainViewTransform = new Matrix();
private void ScalingCircle_Paint( object sender, PaintEventArgs e )
{
Pen myPen = new Pen(Color.Blue, 1);
e.Graphics.DrawRectangle(myPen, 50, 50, 100, 100);
mainViewTransform.Scale(3, 2);
DeviceContexct = e.Graphics;
}
private void Drawing(Graphics gr)
{
Pen myPen2 = new Pen(Color.Red, 1);
DeviceContexct.Transform = mainViewTransform;
DeviceContexct.DrawRectangle(myPen2, 50, 50, 100, 100);
}
private void button1_Click( object sender, EventArgs e )
{
Drawing(DeviceContexct);
}
I prefer to draw into Bitmaps with System.Drawing.Graphics object.
Then you can use your graphics object with "go.DrawImage(...)" to draw the bitmap directly into your winforms and actually give it a scale.
https://msdn.microsoft.com/en-us//library/ms142040(v=vs.110).aspx
I got the solution, thanks for your help.
I only need to call refresh/invalidate etc on button click.
public partial class ScalingCircle : Form
{
public Graphics DeviceContexct;
// current transformation matrix of main view (offset & scaling)
public Matrix mainViewTransform = new Matrix();
public int scale = 1;
public ScalingCircle()
{
InitializeComponent();
DeviceContexct = Graphics.FromHwnd(this.Handle);
DeviceContexct = this.CreateGraphics();
}
public void ScalingCircle_Paint(object sender, PaintEventArgs e)
{
DeviceContexct = e.Graphics;
DeviceContexct.PageUnit = GraphicsUnit.Pixel;
DeviceContexct.Transform = mainViewTransform;
ScalingCircle1(scale);
}
private void ScalingCircle1(int x )
{
Pen myPen2 = new Pen(Color.Black, 1);
DeviceContexct.Transform = mainViewTransform;
Rectangle myRectangle = new Rectangle(50, 50, 100 * x, 100 * x);
DeviceContexct.FillRectangle(new SolidBrush(Color.BurlyWood), myRectangle);
}
private void ScalingCircle_Load( object sender, EventArgs e )
{
this.ResizeRedraw = true;
}
private void button1_Click( object sender, EventArgs e )
{
scale += 5;
this.Refresh();
}
private void button2_Click( object sender, EventArgs e )
{
if (scale > 1)
{
scale -= 5;
this.Refresh();
}
}
}
You can use transformations for that. The graphics object you use for painting things has a Transformation property of type System.Drawing.Drawing2D.Matrix.
Graphics also has the ScaleTransform method.
I haven't used it myself, but that is how microsofts Chart does it.
https://msdn.microsoft.com/de-de/library/system.drawing.drawing2d.matrix(v=vs.110).aspx
https://msdn.microsoft.com/de-de/library/system.drawing.graphics.scaletransform(v=vs.110).aspx
Related
My problem is:
When I try to save the graphics object to a bitmap image it does not save correctly, instead the image is black in color and there is nothing else in the file.
I've seen other answers, but I think it's different when you draw multiple times in the graphics object.
So, here's my attempt, please let me know where my issue is.
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
namespace PenFlip
{
public partial class Match : Form
{
Graphics g;
private int x = -1;
private int y = -1;
private bool moving;
private Pen pen;
private Bitmap testBmp;
public Match()
{
InitializeComponent();
g = panel1.CreateGraphics();
g.SmoothingMode = SmoothingMode.AntiAlias;
pen = new Pen(Color.Black, 5);
pen.StartCap = pen.EndCap = LineCap.Round;
}
private void pictureBox1_Click(object sender, EventArgs e)
{
PictureBox pictureBox = (PictureBox) sender;
pen.Color = pictureBox.BackColor;
}
private void panel1_MouseDown(object sender, MouseEventArgs e)
{
moving = true;
x = e.X;
y = e.Y;
}
private void panel1_MouseMove(object sender, MouseEventArgs e)
{
if (moving && x != -1 && y != -1)
{
g.DrawLine(pen, new Point(x, y), e.Location);
x = e.X;
y = e.Y;
}
}
private void panel1_MouseUp(object sender, MouseEventArgs e)
{
moving = false;
x = -1;
y = -1;
g.Save();
}
private void button1_Click(object sender, EventArgs e)
{
// prints out the black image
testBmp = new Bitmap(400, 200, g);
testBmp.Save(#"test.bmp", ImageFormat.Bmp);
}
}
}
Uh, usage of Bitmap is tricky and has a lot of pitfalls (that's why it's use is deprecated, btw). Try to create the Graphics object from an in-memory bitmap, instead of grabbing it from a control. The bitmap should be the primary object, not the Graphics instance. You don't know what the control does to your bitmap.
So in the constructor, do something like:
public Match()
{
InitializeComponent();
bitmap = new Bitmap(panel1.Width, panel1.Height, PixelFormat.Format32bppArgb);
pen = new Pen(Color.Black, 5);
pen.StartCap = pen.EndCap = LineCap.Round;
}
and in each function, create a graphics object from the bitmap:
using (var graphics = Graphics.FromImage(image))
{
graphics.Draw(...);
}
You need to update the panel manually now, e.g. by attaching to it's OnPaint event.
I am creating a Graphics Form where objects with coordinates x,y are being drawn into the Graphics. It works properly for small x and y, but when I want to draw them in different place (f.e. x = 500, y = 300) they disappear.
public WindowHandler()
{
dc = this.CreateGraphics();
this.Size = new Size(sizeX, sizeY); // 800x600
startSimulation = new Button
{
// button properties
};
this.Controls.Add(startSimulation);
startSimulation.Click += new EventHandler(StartSimulationClick);
}
private void CreationsMethods()
{
creations.PaintAllAnimals(dc);
}
public void PaintAllAnimals(Graphics g)
{
foreach (var animal in ecoStructure.world.animals)
{
animal.PaintAnimal(g);
}
}
public void PaintAnimal(Graphics graphics)
{
Rectangle rectangle = new Rectangle(x, y, 3, 3);
Pen pen = new Pen(colour);
graphics.DrawRectangle(pen, rectangle);
graphics.FillRectangle(colour, rectangle);
}
I want to put all the objects onto the window. Is there any way to make the Graphics "bigger"? Do I need to make another one? Or should I use different tool to draw rectangles?
Thanks to #Chris Dunaway for posting an answer in comment.
So I deleted the CreateCraphics, and instead of that i am now using an OnPaint method. It works slowly, but works. So I will try to make it as fast as i can. For now, I just created this. NextStepClick is how I use the OnPaint to paint the rectangles.
private void CreationsMethods(object sender, PaintEventArgs e)
{
dc = e.Graphics;
base.OnPaint(e);
creations.PaintAllAnimals(dc);
}
private void NextStepClick(object sender, EventArgs e)
{
this.Refresh();
picBox.Paint += new System.Windows.Forms.PaintEventHandler(CreationsMethods);
}
Lately I found out how to rotate images and I already have problem.
Here is piece of code that I have problem with but you probably don't need to look at it anyway...
Graphics g = e.Graphics;
g.TranslateTransform((float)Width / 2, (float)Height / 2);
g.RotateTransform(myAngle);
Brush brush = new SolidBrush(Color.FromArgb(32, 1, 1, 1));
Pen pen = new Pen(Color.FromArgb(255, 128 , 128, 128), 3);
g.FillRectangle(brush, nX, nY, snX - nX, snY - nY);
g.DrawRectangle(pen, nX, nY, snX - nX, snY - nY);
variables X , sX , snX, Y , sY, snY are coordinates of mouse in specific moments and are calculated mostly in Form1_MouseMove and I can't show what's in there.
How can I make these variables also change no matter what myAngle is?
I've made an example, how to draw on a rotated bitmap: (using a picturebox/bitmap/trackbar)
public partial class Form1 : Form
{
private Bitmap _bitmap;
public Form1()
{
InitializeComponent();
_bitmap = new Bitmap(pictureBox1.Width, pictureBox1.Height);
}
private void TransformGraphics(Graphics g)
{
g.ResetTransform();
g.TranslateTransform(_bitmap.Width / 2, _bitmap.Height / 2);
g.RotateTransform(trackBar1.Value);
g.TranslateTransform(-_bitmap.Width / 2, -_bitmap.Height / 2);
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
TransformGraphics(e.Graphics);
e.Graphics.DrawImage(_bitmap, new Point());
}
private Point? _previousPoint;
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
_previousPoint = e.Location;
}
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
_previousPoint = null;
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (_previousPoint.HasValue)
{
using (Graphics g = Graphics.FromImage(_bitmap))
{
TransformGraphics(g);
var matrix = g.Transform;
matrix.Invert();
var points = new[] { _previousPoint.Value, e.Location };
matrix.TransformPoints(points);
g.ResetTransform();
g.DrawLine(Pens.Black, points[0], points[1]);
pictureBox1.Invalidate();
_previousPoint = e.Location;
}
}
}
private void trackBar1_ValueChanged(object sender, EventArgs e)
{
pictureBox1.Invalidate();
}
}
You can add another trackbar for scaling.
how can i call a method with system arguments in a mouse click event:
public void DrawStringRectangleF(PaintEventArgs e)
{
// Create string to draw.
String drawString = "Sample Text";
// Create font and brush.
Font drawFont = new Font("Arial", 16);
SolidBrush drawBrush = new SolidBrush(Color.Black);
// Create rectangle for drawing.
float x = 150.0F;
float y = 150.0F;
float width = 200.0F;
float height = 50.0F;
RectangleF drawRect = new RectangleF(x, y, width, height);
// Draw rectangle to screen.
Pen blackPen = new Pen(Color.Black);
e.Graphics.DrawRectangle(blackPen, x, y, width, height);
// Draw string to screen.
e.Graphics.DrawString(drawString, drawFont, drawBrush, drawRect);
}
i want to call this method in this event:
private void button1_Click(object sender, EventArgs e)
{
// calling DrawStringRectangleF(PaintEventArgs e) here
}
it's easy to call a method if the parameters are variables just by mentioning the values when calling these methods, but how to call it in such a case?!
In order to satisfy the existing method signature you could encapsulate the Graphics instance that you wish to use to draw the rectangle within an instance of the PaintEventArgs class like this
using (var controlGraphics = CreateGraphics())
{
var paintEventArgs = new PaintEventArgs(controlGraphics, ClientRectangle);
DrawStringRectangleF(paintEventArgs);
}
The Control.CreateGraphics method will produce a Graphics instance that you can use to paint on the underlying control (or form).
Understand that everything painted with that Graphics object will be erased each time the control is invalidated so you must re-draw the graphic continuously.
You may consider adapting the method signature so that it accepts an instance of Graphics instead which should make for a cleaner solution.
Here's an example using the Paint() event and a List of a custom class to store the information about what should be drawn:
public partial class Form1 : Form
{
public class TextData
{
public string Text;
public string FontName;
public int FontSize;
public RectangleF TextRectF;
public Color TextColor;
}
private List<TextData> Texts = new List<TextData>();
public Form1()
{
InitializeComponent();
this.Paint += new PaintEventHandler(Form1_Paint);
TextData td = new TextData();
td.Text = "Example";
td.FontName = "Courier";
td.FontSize = 24;
td.TextRectF = new RectangleF(50.0F, 50.0F, 175.0F, 75.0F);
td.TextColor = Color.Red;
Texts.Add(td);
}
void Form1_Paint(object sender, PaintEventArgs e)
{
foreach (TextData td in Texts)
{
using (Pen drawPen = new Pen(td.TextColor))
{
e.Graphics.DrawRectangle(drawPen, Rectangle.Round(td.TextRectF));
}
using (Font drawFont = new Font(td.FontName, td.FontSize))
{
using (SolidBrush drawBrush = new SolidBrush(td.TextColor))
{
e.Graphics.DrawString(td.Text, drawFont, drawBrush, td.TextRectF);
}
}
}
}
private void button1_Click(object sender, EventArgs e)
{
TextData td = new TextData();
td.Text = "Sample Text";
td.FontName = "Arial";
td.FontSize = 16;
td.TextRectF = new RectangleF(150.0F, 150.0F, 200.0F, 50.0F);
td.TextColor = Color.Black;
Texts.Add(td);
this.Refresh();
}
}
**i draw ellipse at runtime using following code.in that code i used graphics path for drawing(actually this is project requirement )and used widen method for graphics path.
but it gives runtime exception "out of memory".can i use this method in the case of ellipse?
while using widen method in the case of drawing rectangle at runtime, its working properly.
please solve this problem and give me some suggestion?**
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
}
Rectangle r;
bool isDown = false;
int initialX;
int initialY;
bool IsDrowing =true;
GraphicsPath gp1;
GraphicsPath gp2;
GraphicsPath gp3;
GraphicsPath gp;
Graphics g;
bool contained;
bool containedE;
bool containedC;
private void Form2_MouseDown(object sender, MouseEventArgs e)
{
isDown = true;
IsDrowing = true;
initialX = e.X;
initialY = e.Y;
}
private void Form2_MouseMove(object sender, MouseEventArgs e)
{
//IsDrowing = true;
if (isDown == true)
{
int width = e.X - initialX, height = e.Y - initialY;
r = new Rectangle(Math.Min(e.X, initialX),
Math.Min(e.Y, initialY),
Math.Abs(e.X - initialX),
Math.Abs(e.Y - initialY));
this.Invalidate();
}
}
private void Form2_Paint(object sender, PaintEventArgs e)
{
g = this.CreateGraphics();
gp = new GraphicsPath();
Pen pen = new Pen(Color.Red);
gp.AddEllipse(r);
gp.Widen(pen);
pen.DashStyle = DashStyle.Dash;
if (IsDrowing)
{
g.DrawPath(pen, gp);
}
private void Form2_MouseUp(object sender, MouseEventArgs e)
{
IsDrowing = false;
this.Refresh();
}
}
Basically: Avoid the GraphicsPath.Widen Method. It's buggy, search for "spirograph bug"
In your case this manifests because you try to widen a 0 by 0 rectangle. Modify your code like this:
private void Form2_Paint(object sender, PaintEventArgs e)
{
if (IsDrowing)
{
g = e.Graphics;
gp = new GraphicsPath();
gp.AddEllipse(r);
gp.Widen(new Pen(Color.Red, 10));
Pen pen = new Pen(Color.Red, 1);
pen.DashStyle = DashStyle.Dash;
g.DrawPath(pen, gp);
}
}
It may need additional work, but avoid the widening of an empty rectangle/ellipse.