I have drawn a region in C# Winform onPaint event. I dont want to have these changes in full screen mode. I have tried to save and restore the graphics but not able to do, please let me know if I'm missing something.
//class member
bool mFullSize = false;
GraphicsState transState;
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
// Save translated graphics state.
Graphics gr = e.Graphics;
transState = gr.Save();
int x = 50;
int y = 50;
int width = 100;
int height = 100;
Rectangle rect = new Rectangle(x, y, width / 2, height / 2);
Region region = new Region(rect);
GraphicsPath path = new GraphicsPath();
path.AddPie(x, y, width, height, 180, 90);
region.Exclude(path);
if (!mFullSize)
{
gr.FillRegion(Brushes.Black, region);
}
else
{
gr.Restore(transState);
}
}
protected override void OnResize(EventArgs e)
{
if (this.WindowState == FormWindowState.Maximized)
{
mFullSize = true;
}
else
{
mFullSize = false;
}
}
Why don't you simply return when the Form is full sized? It's painted on maximization anyway, so I don't see no need to (re-)store any graphics state.
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
if (WindowState == FormWindowState.Maximized) return; // just don't paint the things you don't want to
Graphics gr = e.Graphics;
int x = 50;
int y = 50;
int width = 100;
int height = 100;
Rectangle rect = new Rectangle(x, y, width / 2, height / 2);
Region region = new Region(rect);
GraphicsPath path = new GraphicsPath();
path.AddPie(x, y, width, height, 180, 90);
region.Exclude(path);
gr.FillRegion(Brushes.Black, region);
}
UPDATE: The problem is OnResize is not called when maximizing. You need to use OnSizeChanged or simply check the WindowState directly in your OnPaint method.
UPDATE2: First update was not completely correct (bug in debug-messages). OnResize is actually called. But you forgot to call base.OnResize and so OnPaint is not called.
So be sure to call base.OnResize in your overload. In your OnPaint you can check the WindowState directly and avoid painting if your Form is maximized.
protected override void OnResize(EventArgs e)
{
// DON'T FORGET TO CALL THE BASE METHOD
base.OnResize(e);
mFullSize = this.WindowState == FormWindowState.Maximized;
}
Related
I am developping a custom C# UserControl WinForm to display an image on background and display scrollbars when I zoom with the mouse. For this, I overrided the OnPaint method. In it, if I have an image loaded, according some parameters I know the source and destination rectangle sizes. In the same way, I know what scale and translation apply to always keeping the top left corner on screen when zooming. And for the zoom, I use the scrollmouse event to update the zoom factory.
Here is my code related to this override method.
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
// Draw image
if(image != null)
{
//
Rectangle srcRect, destRect;
Point pt = new Point((int)(hScrollBar1.Value/zoom), (int)(vScrollBar1.Value/zoom));
if (canvasSize.Width * zoom < viewRectWidth && canvasSize.Height * zoom < viewRectHeight)
srcRect = new Rectangle(0, 0, canvasSize.Width, canvasSize.Height); // view all image
else if (canvasSize.Width * zoom < viewRectWidth)
srcRect = new Rectangle(0, pt.Y, canvasSize.Width, (int)(viewRectHeight / zoom)); // view a portion of image but center on width
else if (canvasSize.Height * zoom < viewRectHeight)
srcRect = new Rectangle(pt.X, 0, (int)(viewRectWidth / zoom), canvasSize.Height); // view a portion of image but center on height
else
srcRect = new Rectangle(pt, new Size((int)(viewRectWidth / zoom), (int)(viewRectHeight / zoom))); // view a portion of image
destRect = new Rectangle((int)(-srcRect.Width/2),
(int)-srcRect.Height/2,
srcRect.Width,
srcRect.Height); // the center of apparent image is on origin
Matrix mx = new Matrix(); // create an identity matrix
mx.Scale(zoom, zoom); // zoom image
// Move image to view window center
mx.Translate(viewRectWidth / 2.0f, viewRectHeight / 2.0f, MatrixOrder.Append);
// Display image on widget
Graphics g = e.Graphics;
g.InterpolationMode = interMode;
g.Transform = mx;
g.DrawImage(image, destRect, srcRect, GraphicsUnit.Pixel);
}
}
My question is how to get the pixel value when I am on the MouseMove override method of this WinForm ?
I think understand that it is possible only in method with PaintEventArgs but I am not sure how to deal with it. I tried a lot of things but for now the better I got is use the mouse position on the screen and find the pixel value in the original bitmap with these "wrong" coordinates. I can't link this relative position on the screen with the real coordinates of the pixel of the image display at this place. Maybe there is method to "just" get the pixel value not passing through the image bitmap I use for the paint method ? Or maybe not.
Thank you in advance for your help. Best regards.
I couldn't completely understand your drawing code but you can do an inverse transformation on mouse coordinate. So, you can translate the mouse coordinate back to the origin and scale it by 1/zoom. This simple process gives you the image space coordinate.
I provide an example code with its own drawing code (not your code/algorithm) but that can still give you the idea of inverse transformation. It is pretty simple so look at the example code.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace GetPixelFromZoomedImage
{
public partial class MainForm : Form
{
public Form1()
{
SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint | ControlStyles.ResizeRedraw, true);
InitializeComponent();
}
private float m_zoom = 1.0f;
private Bitmap m_image;
private Point m_origin = Point.Empty;
private Point m_delta = Point.Empty;
private SolidBrush m_brush = new SolidBrush(Color.Transparent);
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
g.TranslateTransform(m_origin.X, m_origin.Y);
g.ScaleTransform(m_zoom, m_zoom);
g.DrawImageUnscaled(m_image, Point.Empty);
g.ResetTransform();
g.FillRectangle(m_brush, ClientSize.Width - 50, 0, 50, 50);
base.OnPaint(e);
}
protected override void OnHandleCreated(EventArgs e)
{
m_image = (Bitmap)Image.FromFile("test.png");
base.OnHandleCreated(e);
}
protected override void OnMouseDown(MouseEventArgs e)
{
if(e.Button == MouseButtons.Left)
{
m_delta = new Point(m_origin.X - e.X, m_origin.Y - e.Y);
}
base.OnMouseDown(e);
}
protected override void OnMouseMove(MouseEventArgs e)
{
if(e.Button == MouseButtons.Left)
{
m_origin = new Point(e.X + m_delta.X, e.Y + m_delta.Y);
Invalidate();
}
int x = (int)((e.X - m_origin.X) / m_zoom);
int y = (int)((e.Y - m_origin.Y) / m_zoom);
if (x < 0 || x >= m_image.Width || y < 0 || y >= m_image.Height)
return;
m_brush.Color = m_image.GetPixel(x, y);
Invalidate();
base.OnMouseMove(e);
}
protected override void OnMouseUp(MouseEventArgs e)
{
base.OnMouseUp(e);
}
protected override void OnMouseWheel(MouseEventArgs e)
{
float scaleFactor = 1.6f * (float)Math.Abs(e.Delta) / 120;
if(e.Delta > 0)
m_zoom *= scaleFactor;
else
m_zoom /= scaleFactor;
m_zoom = m_zoom > 64.0f ? 64.0f : m_zoom;
m_zoom = m_zoom < 0.1f ? 0.1f : m_zoom;
Invalidate();
base.OnMouseWheel(e);
}
}
}
I have a Label and I am trying to draw an inner circle (not filled) inside this Label, as big as possible.
I have tried two methods, one method applied to label1 and another one applied to label2. In both cases it does not work.
Note: the Label should keep its background color and content.
How can I get rid of this?
Code:
void DrawCircle1(Graphics g, Point centerPos, int radius, int cutOutLen)
{
RectangleF rectangle = new RectangleF(centerPos.X, centerPos.Y,
radius * 2,
radius * 2
);
// calculate the start angle
float startAngle = (float)(Math.Asin(
1f * (radius - cutOutLen) / radius) / Math.PI * 180);
using (System.Drawing.Drawing2D.GraphicsPath path = new System.Drawing.Drawing2D.GraphicsPath())
{
path.AddArc(rectangle, 180 - startAngle, 180 + 2 * startAngle);
path.CloseFigure();
//g.FillPath(Brushes.Yellow, path);
using (Pen p = new Pen(Brushes.Yellow))
{
g.DrawPath(new Pen(Brushes.Blue, 2), path);
}
}
}
private void DrawCircle2(PaintEventArgs e)
{
Label tempLabel = label2;
using (System.Drawing.SolidBrush myBrush = new System.Drawing.SolidBrush(System.Drawing.Color.Red))
{
using (System.Drawing.Pen myPen = new Pen(myBrush, 2))
{
e.Graphics.DrawEllipse(myPen, new System.Drawing.Rectangle(tempLabel.Location.X, tempLabel.Location.Y,
tempLabel.Width, tempLabel.Height));
}
}
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
DrawCircle1(e.Graphics, new Point(label1.Width/2, label1.Height/2), 10, 50);
DrawCircle2(e);
}
Below a screenshot:
You are drawing on Form not the Label. Instead of overriding OnPaint method of the Form, try to handle Paint event of Label controls. For example:
private void label1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
e.Graphics.DrawEllipse(Pens.Red, 0, 0, label1.Height - 1, label1.Height - 1);
}
Same procedures you're using now, calling them from a control's Paint() event.
It's the same if you're creating a Custom control. Use the overridden OnPaint() event in this case.
In the control's Paint() event, call one/more methods to draw a shape on the control's surface.
private void label1_Paint(object sender, PaintEventArgs e)
{
DrawCircle1(e.Graphics, label1.ClientRectangle);
}
private void label2_Paint(object sender, PaintEventArgs e)
{
DrawCircle2(e.Graphics, label2.ClientRectangle);
}
Use the Control's ClientRectangle bounds to derive the size of the figure.
Here, the ClientRectangle is reduced by 1 when using Graphics.DrawEllipse() and by 2 when using Graphics.DrawPath(). The two methods calculate the pen size in relation to the drawing area in a slightly different manner.
private void DrawCircle1(Graphics g, RectangleF canvas)
{
canvas.Inflate(-2, -2);
g.SmoothingMode = SmoothingMode.AntiAlias;
using (GraphicsPath path = new GraphicsPath())
using (Pen p = new Pen(Color.Blue, 2)) {
path.StartFigure();
path.AddArc(canvas, 0, 360);
path.CloseFigure();
g.DrawPath(p, path);
}
}
private void DrawCircle2(Graphics g, RectangleF canvas)
{
canvas.Inflate(-1, -1);
g.SmoothingMode = SmoothingMode.AntiAlias;
using (Pen p = new Pen(Color.Red, 2)) {
g.DrawEllipse(p, canvas);
}
}
I have drawn a grid on form 8 in on paint event of the form as shown below. I have written a class drawRules in which I mentioned to draw the vertical and horizontal lines based on input from the form.
protected override void OnPaint(PaintEventArgs e)
{
Graphics g;
g = e.Graphics;
Pen linePen = new Pen(System.Drawing.Color.CornflowerBlue);
Int32 Num_of_Lines;
Int32 gridLength;
Int32 gridWidth;
bool IsIntValue = Int32.TryParse(Form7.setValue2, out Num_of_Lines);
bool IsIntValue1 = Int32.TryParse(Form7.setValue3, out gridWidth);
bool IsIntValue2 = Int32.TryParse(Form7.setValue4, out gridLength);
this.Size = new Size(Num_of_Lines * gridWidth, Num_of_Lines * gridLength);
if (IsIntValue)
{
if (IsIntValue1)
{
if (IsIntValue2)
{
drawRules.verticalRule vr1 = new drawRules.verticalRule(g, gridWidth, gridLength, Num_of_Lines);
//Draw horizontal line
drawRules.horizontalRule hr1 = new drawRules.horizontalRule(g, gridWidth, gridLength, Num_of_Lines);
}
linePen.Dispose();
base.OnPaint(e);
}
}
}
after this I want to draw circles wherever mouse is clicked for which I mentioned the mouse click event
private void Form8_MouseClick_1(object sender, MouseEventArgs e)
{
int r1 = e.X;
int r2 = e.Y;
Graphics g2;
g2 = this.CreateGraphics();
drawRules newclass1 = new drawRules();
newclass1.addcoordinate(r1, r2, g2);
}
addcoordinate1 is a method in drawRules class which is called to draw circle. Also, i am writing those coordinates in a file
public void addcoordinate(int r1, int r2, Graphics g2)
{
int gridWidth;
int gridLength;
int Num_of_Lines;
bool IsIntValue = Int32.TryParse(Form7.setValue2, out Num_of_Lines);
bool IsIntValue1 = Int32.TryParse(Form7.setValue3, out gridWidth);
bool IsIntValue2 = Int32.TryParse(Form7.setValue4, out gridLength);
Rectangle rectangle = new Rectangle();
PaintEventArgs arg = new PaintEventArgs(g2, rectangle);
Pen redPen1 = new Pen(Color.Red, 3);
DrawCircle(arg, redPen1, r1, r2, 8, 8);
System.IO.StreamWriter objWriter;
objWriter = new System.IO.StreamWriter("test.txt", true);
objWriter.Write(r1);
objWriter.Write(" ");
objWriter.Write(r2);
objWriter.WriteLine();
objWriter.Close();
}
public void DrawCircle(PaintEventArgs e, Pen redpen1, int x, int y, int
width, int height)
{
e.Graphics.DrawEllipse(redpen1, x - width / 2, y - height / 2, width, height);
redpen1.Dispose();
}
Now, I want to delete that circle, for which mouse is double clicked inside circle's area.
Please suggest how to delete the circles without deleting the grids behind. I will be grateful, if someone help me in this.
Don't draw in the code that gets executed when clicking the Button.
Save the coordinates and let the OnPaint methode handle it.
To remove your cirlces just remove them from the list.
private List<Point> circleCoordinates = new List<Point>();
public Form1()
{
InitializeComponent();
}
public void addcoordinate(int r1, int r2)
{
this.circleCoordinates.Add(new Point(r1, r2));
}
protected override void OnPaint(PaintEventArgs e)
{
// linedrawing goes here
foreach (Point point in this.circleCoordinates)
{
e.Graphics.DrawEllipse(Pens.Black, new Rectangle(point, new Size(10, 10)));
}
base.OnPaint(e);
}
private void Form1_MouseDoubleClick(object sender, MouseEventArgs e)
{
for (int i = this.circleCoordinates.Count() - 1; i >= 0; i--)
{
Rectangle ellipseRectangle = new Rectangle(this.circleCoordinates[i].X - 5, this.circleCoordinates[i].Y - 5, 10, 10)
GraphicsPath path = new GraphicsPath();
path.AddEllipse(ellipseRectangle);
if(path.IsVisible(e.Location))
{
this.circleCoordinates.RemoveAt(i);
}
//invalidate form to trigger repainting
this.Invalidate();
}
I have problem with my C# winform project.
I have function that draw squares:
public void DrawingSquares(int x, int y)
{
System.Drawing.Graphics graphicsObj;
graphicsObj = this.CreateGraphics();
Pen myPen = new Pen(System.Drawing.Color.Black, 5);
Rectangle myRectangle = new Rectangle(x, y, 100, 100);
graphicsObj.DrawRectangle(myPen, myRectangle);
}
private void button1_Click(object sender, EventArgs e)
{
z = Convert.ToInt16(textBox1.Text)-1;
k = Convert.ToInt16(textBox2.Text)-1;
DrawAllSquares();
}
private void DrawAllSquares()
{
int tempy = y;
for (int i = 0; i < z; i++)
{
DrawingSquares(x, y);
for (int j = 0; j < k - 1; j++)
{
tempy += 50;
DrawingSquares(x, tempy);
}
x += 50;
tempy = y;
}
}
In my project, I have a function that I use to move button around the form at runtime, but when the button is moved onto the drawing the drawing is deleted.
What can I do to make the drawing permanent?
If you need permanently (in terms of application life time), by any means, you need to use it inside you Control's (the Control where rectangle is have to be drawn), OnPaint method.
If you need an animation too: it could be resolved by using a timer and changing coordinates that you pass like a parameters to your DrawSquares.
Hope this helps.
EDIT
A pseudocode:
public class MyControl : Control
{
public override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
DrawingSquares(e.Graphics, valueX, valueY);
}
public void DrawingSquares(Graphics graphicsObj, int x, int y)
{
Pen myPen = new Pen(System.Drawing.Color.Black, 5);
Rectangle myRectangle = new Rectangle(x, y, 100, 100);
graphicsObj.DrawRectangle(myPen, myRectangle);
}
}
valueX and valueY are relative X and Y coordinates where you want the rectangle to be drawn.
These coordinates can be constant values, or you can change them from some timer and call Invalidate() on MyControl, so paint will be executed.
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);
}
}