I simulate map navigation and draw generated part of the map on a panel. Since image is flickering I have to use double buffering.
Here's my Panel code:
public class MapPanel : System.Windows.Forms.Panel
{
public MapPanel()
{
DoubleBuffered = true;
ResizeRedraw = true;
}
}
And I have the following method:
public void panelMap_Paint(object sender, PaintEventArgs e)
{
using (Graphics g = e.Graphics)
{
g.DrawImage(mapController.GetCurrentMap(), 0, 0, panelMap.Width, panelMap.Height);
}
}
I'm not calling this method. I have the following code in .Designer.cs:
this.panelMap.Paint += new PaintEventHandler(this.panelMap_Paint);
And call Invalidate() in MouseMove. I'm sure that this event occurs, I've checked it. Everything seems to be correct.
And then the image is not drawing. I mean, the panel is empty and seems to be transparent or colored in default control color. However, if I turn double buffering off, the image is properly drawn, but, obviously, it's flickering. Could you help me?
Remove the using statement. You are disposing of the Graphics object before it is used to draw to the screen.
As mentioned, you should also remove the Invalidate() call from the paint method.
public void panelMap_Paint(object sender, PaintEventArgs e)
{
var g = e.Graphics;
g.DrawImage(mapController.GetCurrentMap(), 0, 0, panelMap.Width, panelMap.Height);
}
You should comment out the following code
//panelMap.Invalidate();
According to MSDN
Invalidates the entire surface of the control and causes the control to be
redrawn.
Related
I have a Graphics object and a Panel. The Graphics object is instantiated with a Handler from the Panel. Then, the Panel is updated with the Paint action. Inside the Paint action, the Graphics object is used to paint. Sometimes through out the code I use the Invalidate() to update the panel.
I want to save the content of the Graphics object or the content of the Panel into a file. Whenever I try to do it, the image file is created, but blank.
Here are some snippets of the code:
I initiate Graphics object as a class variable:
Graphics GD_LD;
Then in the constructor I use the following to instantiate the object using the panel handler:
GD_LD = Graphics.FromHwnd(panelDrawLD.Handle);
Then I have the drawing function that is used on the Paint action of the Panel, were I use the Graphics object to make the all the drawings:
private void panelDrawLD_Paint(object sender, PaintEventArgs e)
{
..... some code ....
//Code example
GD_LD.FillPolygon(blackBrush, getPoints(min, sizeGP, scaleX, scaleY));
GD_LD.FillPolygon(blackBrush, getPoints(max, sizeGP, scaleX, scaleY));
..... some code ....
}
The above works fine to draw in the Panel and keep it always with the drawing.
The issue is when trying to save the Panel to a Image file:
Bitmap I_LD = new Bitmap(panelDrawLD.Size.Width, panelDrawLD.Size.Height);
panelDrawLD.DrawToBitmap(I_LD, new Rectangle(0,0, panelDrawLD.Size.Width, panelDrawLD.Size.Height));
I_LD.Save(tempPath + "I_LD.bmp",ImageFormat.Bmp);
The image file is created but without content. Just blank.
I saw some threads about this subject, but I could not adapt it to my situation.
What am I doing wrong? What's a possible solution?
What you really should be doing, is refactoring your Paint event in to a subroutine that takes a Graphics object as an argument called target. Do all your drawing against target. Then you can call that and pass e.Graphics from panelDrawLD_Paint, and call it from your other function with a Graphics you create with Graphics.FromImage(I_LD).
Also, if you create a Graphics (or any other GDI object), you MUST destroy it, or you will get a memory leak.
Like this:
private void panelDrawLD_Paint(object sender, PaintEventArgs e)
{
//e.Graphics does NOT need to be disposed of because *we* did not create it, it was passed to us by the control its self.
DrawStuff(e.Graphics);
}
private void Save()
{
// I_LD, and g are both GDI objects that *we* created in code, and must be disposed of. The "using" block will automatically call .Dispose() on the object when it goes out of scope.
using (Bitmap I_LD = new Bitmap(panelDrawLD.Size.Width, panelDrawLD.Size.Height))
{
using (Graphics g = Graphics.FromImage(I_LD))
{
DrawStuff(g);
}
I_LD.Save(tempPath + "I_LD.bmp", ImageFormat.Bmp);
}
}
private void DrawStuff(Graphics target)
{
//Code example
target.FillPolygon(blackBrush, getPoints(min, sizeGP, scaleX, scaleY));
target.FillPolygon(blackBrush, getPoints(max, sizeGP, scaleX, scaleY));
}
in a UserControl I have a PictureBox and some other controls. For the user control which contains this picturebox named as Graph I have a method to draw a curve on this picture box:
//Method to draw X and Y axis on the graph
private bool DrawAxis(PaintEventArgs e)
{
var g = e.Graphics;
g.DrawLine(_penAxisMain, (float)(Graph.Bounds.Width / 2), 0, (float)(Graph.Bounds.Width / 2), (float)Bounds.Height);
g.DrawLine(_penAxisMain, 0, (float)(Graph.Bounds.Height / 2), Graph.Bounds.Width, (float)(Graph.Bounds.Height / 2));
return true;
}
//Painting the Graph
private void Graph_Paint(object sender, PaintEventArgs e)
{
base.OnPaint(e);
DrawAxis(e);
}
//Public method to draw curve on picturebox
public void DrawData(PointF[] points)
{
var bmp = Graph.Image;
var g = Graphics.FromImage(bmp);
g.DrawCurve(_penAxisMain, points);
Graph.Image = bmp;
g.Dispose();
}
When application starts, the axis are drawn. but when I call the DrawData method I get the exception that says bmp is null. What can be the problem?
I also want to be able to call DrawData multiple times to show multiple curves when user clicks some buttons. What is the best way to achive this?
Thanks
You never assigned Image, right? If you want to draw on a PictureBox’ image you need to create this image first by assigning it a bitmap with the dimensions of the PictureBox:
Graph.Image = new System.Drawing.Bitmap(Graph.Width, Graph.Height);
You only need to do this once, the image can then be reused if you want to redraw whatever’s on there.
You can then subsequently use this image for drawing. For more information, refer to the documentation.
By the way, this is totally independent from drawing on the PictureBox in the Paint event handler. The latter draws on the control directly, while the Image serves as a backbuffer which is painted on the control automatically (but you do need to invoke Invalidate to trigger a redraw, after drawing on the backbuffer).
Furthermore, it makes no sense to re-assign the bitmap to the PictureBox.Image property after drawing. The operation is meaningless.
Something else, since the Graphics object is disposable, you should put it in a using block rather than disposing it manually. This guarantees correct disposing in the face of exceptions:
public void DrawData(PointF[] points)
{
var bmp = Graph.Image;
using(var g = Graphics.FromImage(bmp)) {
// Probably necessary for you:
g.Clear();
g.DrawCurve(_penAxisMain, points);
}
Graph.Invalidate(); // Trigger redraw of the control.
}
You should consider this as a fixed pattern.
in a UserControl I have a PictureBox and some other controls. For the user control which contains this picturebox named as Graph I have a method to draw a curve on this picture box:
//Method to draw X and Y axis on the graph
private bool DrawAxis(PaintEventArgs e)
{
var g = e.Graphics;
g.DrawLine(_penAxisMain, (float)(Graph.Bounds.Width / 2), 0, (float)(Graph.Bounds.Width / 2), (float)Bounds.Height);
g.DrawLine(_penAxisMain, 0, (float)(Graph.Bounds.Height / 2), Graph.Bounds.Width, (float)(Graph.Bounds.Height / 2));
return true;
}
//Painting the Graph
private void Graph_Paint(object sender, PaintEventArgs e)
{
base.OnPaint(e);
DrawAxis(e);
}
//Public method to draw curve on picturebox
public void DrawData(PointF[] points)
{
var bmp = Graph.Image;
var g = Graphics.FromImage(bmp);
g.DrawCurve(_penAxisMain, points);
Graph.Image = bmp;
g.Dispose();
}
When application starts, the axis are drawn. but when I call the DrawData method I get the exception that says bmp is null. What can be the problem?
I also want to be able to call DrawData multiple times to show multiple curves when user clicks some buttons. What is the best way to achive this?
Thanks
You never assigned Image, right? If you want to draw on a PictureBox’ image you need to create this image first by assigning it a bitmap with the dimensions of the PictureBox:
Graph.Image = new System.Drawing.Bitmap(Graph.Width, Graph.Height);
You only need to do this once, the image can then be reused if you want to redraw whatever’s on there.
You can then subsequently use this image for drawing. For more information, refer to the documentation.
By the way, this is totally independent from drawing on the PictureBox in the Paint event handler. The latter draws on the control directly, while the Image serves as a backbuffer which is painted on the control automatically (but you do need to invoke Invalidate to trigger a redraw, after drawing on the backbuffer).
Furthermore, it makes no sense to re-assign the bitmap to the PictureBox.Image property after drawing. The operation is meaningless.
Something else, since the Graphics object is disposable, you should put it in a using block rather than disposing it manually. This guarantees correct disposing in the face of exceptions:
public void DrawData(PointF[] points)
{
var bmp = Graph.Image;
using(var g = Graphics.FromImage(bmp)) {
// Probably necessary for you:
g.Clear();
g.DrawCurve(_penAxisMain, points);
}
Graph.Invalidate(); // Trigger redraw of the control.
}
You should consider this as a fixed pattern.
How to do Free hand Image Cropping in C# window application??
Okay, you provided very small amount of information, but I'll assume that you are using winforms. There are some tasks dealing with freehand-technique such as:
Drawing
Drag-n-dropping
Cropping
Selecting
They all are very similar. Let's assume that you have a PictureBox and want to crop an image inside it.
// Current selection
private Rectangle _cropRectangle;
// Starting point
private Point _cropStart;
// Dragging flag
private bool _isDragging;
private void pBox_MouseDown(object sender, MouseEventArgs e)
{
_cropRectangle = new Rectangle(e.X, e.Y, 0, 0);
_isDragging = true;
}
private void pBox_MouseUp(object sender, MouseEventArgs e)
{
_isDragging = false;
}
private void pBox_MouseMove(object sender, MouseEventArgs e)
{
if (!_isDragging)
return;
_cropRectangle = new Rectangle(Math.Min(_cropStart.X, e.X),
Math.Min(_cropStart.Y, e.Y),
Math.Abs(e.X - _cropStart.X),
Math.Abs(e.Y - _cropStart.Y));
pBox.Invalidate();
}
private void pBox_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawRectangle(Pens.Red, _cropRectangle);
}
What happens: I make use of three mouse events (MouseDown, MouseUp, MoseMove) and the Paint event. Basically, whenever you want to do anything from the above list, you'll have handle these four events.
I tried to keep the code short and self-explanatory. There are four event handlers working with three instance fields. The fields are used to store the current state of dragging process.
Feel free to customize the code, especially the pBox_Paint handler. Mine just draws a thin red rectangle around selected area. You might want to do something more elaborate here.
Whenever you're done with your rectangle, you can call the Crop method:
private Image Crop()
{
Bitmap croppedImage = new Bitmap(_cropRectangle.Width, _cropRectangle.Height);
using (Graphics g = Graphics.FromImage(croppedImage))
{
g.DrawImage(pBox.Image, 0, 0, _cropRectangle, GraphicsUnit.Pixel);
}
return croppedImage;
}
It creates a new Bitmap and put the selected portion of source image into it. The returned Image object might be used in any manner you like.
EDIT: trying to simplify the code I made some mistakes earlier, fixed now.
You can use Graphics.DrawImage to draw a cropped image onto the graphics object from a bitmap.
Rectangle cropRect = new Rectangle(...);
Bitmap src = Image.FromFile(fileName) as Bitmap;
Bitmap target = new Bitmap(cropRect.Width, cropRect.Height);
using(Graphics g = Graphics.FromImage(target))
{
g.DrawImage(src, cropRect,
new Rectangle(0, 0, target.Width, target.Height),
GraphicsUnit.Pixel);
}
You can also refer the full code for this....
Refer this *Link*
I want to show some graphics in a Winform app, it will be a stock's chart drawing tool. I think (but I am not sure...) I have to use a PictureBox, and using the System.Drawing.Graphics class' drawing primitives to draw the chart. I have started coding it, now it works more-or-less, but I have a problem with the resizing feature, as follows: when I resize the entire form, I see that the program shows the graphics then inmediately clear it. When I stop the mouse movement (without to release the mouse button) the graphics disappears!?!?
I made a small test environment to demo the bug:
Using VS2005, creating a new C# Windows Forms app, adding only a PictureBox to the form.
Setting the PictureBox's anchor to left, top, right and bottom. Add two event handler, the Resize to the PictureBox, and the Paint to the Form.
namespace PictureBox_Resize {
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
Ellipse_Area = this.pictureBox1.Size;
}
private Pen penBlack = new Pen(Color.Black, 1.0f);
private Size Ellipse_Area;
private void Form1_Paint(object sender, PaintEventArgs e) {
Graphics g = this.pictureBox1.CreateGraphics();
g.DrawEllipse(penBlack, 0, 0, Ellipse_Area.Width, Ellipse_Area.Height);
}
private void pictureBox1_Resize(object sender, EventArgs e) {
Control control = (Control)sender;
Ellipse_Area = control.Size;
this.pictureBox1.Invalidate();
}
}
}
This small app shows the problem. It only draws an ellipse, but of course my drawing code is much more complicated one...
Any idea why the ellipse disappears when I resize the Form????
Why are you using a PictureBox? I would create a UserControl for your chart and draw the ellipse in its Paint method, just using its current size. In its constructor, set it up for double buffering and all painting in the paint method.
this.SetStyle(ControlStyles.DoubleBuffer |
ControlStyles.UserPaint |
ControlStyles.AllPaintingInWmPaint,
true);
As far as I remember from my C++ days - where I did loads of such image-stuff - you need to call the repaint method - or override it to fit it for your behaviour.