Im using System.Drawing.Graphics graphicsObj for drawing objects on System.Windows.Forms.Panel. What is wise method to store informations about old drawings? To remove last drawing during MouseMove event?
Do i have to remember Pen settings and cords?
Thanks :)
// edit. I think my question is not clear. I have a Panel. I treat it like a container for drawing.
I can draw multiple lines, circles, rectangles.
Every time i press the mouse (MouseDown), im want to start drawing new object. When i move mouse (MouseMove), object currently drawed has to change (for example line, like in paint). When i release the mouse (MouseUp), i want object i drawed to became constant.
Now, i tried to invoke graphicsObj.Clear(); method on MouseMove event, but it clears all drawed objects.
So i deduced, that i have to store informations about old drawed objects. And now, i have to make lists (vectors or so) and store informations about that objects? Or there is a method to avoid it?
You should handle all painting in the Panel's Paint event, that is exactly what it's there for.
The Paint event gives you in its PaintEventArgs the Graphics object you should use. This will make sure your instructions are painted on every refresh, not just once.
If you are deriving from Panel, override the OnPaint method instead.
Edit after original question has been expanded:
You should separate the logic of storing the data from the logic of displaying your data. The Graphics object's purpose is to display existing data, not to store data.
I think making Lists or other collection of the objects you are drawing is the best solution.
You will also need flags to handle all the logic of what is a "live" object, etc.
I think you can store your last x,y into an array of Points objects as history.
Not sure I get what you are after here, but if you want some sort of memory about what paint drew.
Draw to a collections of bitmaps, then have your paint routine draw one of the bitmaps on to the control.
Related
I'm reading a lot about C# drawing and reading MSDN tutorial on GDI+ using Graphics handlers.
I want to be able to paint a graph, which nodes I have in a list but I can't use auto-placing, I need the nodes to be in a specified place and have specific appearance and so on, changing over time, that's why I stopped looking for graph libraries.
It all works great when painted for the first time but when I want something painted after something else happens in the code (not after clicking the control), I can't do it. For example:
if (somethingHappens) {
// repaint the panel adding some things
}
All I got is either nothing new happens or my earlier painting disappears.
I found some examples on OnPaint overriding and drawings disappearing when minimalized. When I need to paint something additional when something happens in an application, do I have to override it or is it completely different?
I looked for another Q&A that would include the information you need. Frankly, it's a fundamental question, how to properly deal with drawing a Forms control. MSDN topics like Overriding the OnPaint Method and Custom Control Painting and Rendering provide some detail on the right way to do this. But I'm surprised I wasn't able to find any StackOverflow Q&A that at least addresses this basic question directly (there are many which address it tangentially of course).
Anyway…
This is the basic sequence for drawing in Forms code:
Generate some data to be drawn
Invalidate the control where the data will be drawn
Handle the Paint event by actually drawing that data
Repeat the above as necessary, i.e. any time the data changes (such as "something happens", as in your question) you move back to step #1, adding whatever new data you want to your existing collection of data, then invalidating the control, and finally responding to the Paint event in your event handler.
In the case of drawing a graph, this might look something like this:
private List<Point> _points = new List<Point>();
void AddPoint(Point point)
{
_points.Add(point);
panel1.Invalidate();
}
void panel1_Paint(object sender, PaintEventArgs e)
{
if (_points.Count < 2)
{
return;
}
Point previousPoint = _points[0];
for (int i = 1; i < _points.Count; i++)
{
currentPoint = _points[i];
e.Graphics.DrawLine(Pens.Black, previousPoint, currentPoint);
previousPoint = currentPoint;
}
}
Note that the panel1_Paint() event is an event handler. Normally you would create this via the Designer by selecting the Panel object, switching to the "Events" list in the "Properties" window for the control, and double-clicking in the edit field for the Paint event.
That is of course the simplest example. You can add things like updating the data in a batched manner (i.e. don't invalidate the control until you've added a group of points), use different colors or line styles to draw, draw other elements of the graph like axes, ticks, legend, etc. The above is simply meant to illustrate the basic technique.
One final note: depending on how many points in your graph you need to draw, the above may or may not be fast enough. It should be fine up to thousands of points or so, but if you start getting to tens or hundreds of thousands or more, you'll probably find that it's useful to cache the drawing into a bitmap and draw just the bitmap. But that's a whole separate question. First, you need to make sure you understand the Forms drawing model and are using it correctly.
In a WinForm application, when subscribing to OnPaint() event, PaintEventArgs provide a ClipRectangle property that define the region to be drawn.
When the form is resized vertically or horizontally, it gives the minimum rectangle to be draw.
But when window is resized in both directions, there several regions that need to be draw (one on right, one at bottom) and OnPaint event merge them. It results in a rectangle having same size as Form (thus everything is redraw). What i'd like to have is individual regions separately (the two rectangles on the picture)
I know that GDI+ automatically clips what doesn't need to be draw (things are outside the two rectangles, not just ClipRectangle) but i'd like to minimize GDI+ calls at maximum (i already have performance issues when drawing in OnPaint event because of many GDI+ calls, this is not premature optimisation)
Painting in Windows is initiated by the WM_PAINT message handler. It must call BeginPaint() to get info about what needs to be painted. Which returns a struct of type PAINTSTRUCT, it looks like this:
typedef struct tagPAINTSTRUCT {
HDC hdc;
BOOL fErase;
RECT rcPaint; // <=== here
BOOL fRestore;
BOOL fIncUpdate;
BYTE rgbReserved[32];
} PAINTSTRUCT, *PPAINTSTRUCT;
The rcPaint member is the one that you get from Graphics.ClipRectangle. The Graphics.Clip and Graphics.ClipBounds properties are not relevant, they only work if you intentionally clip yourself by assigning the Clip property.
Clearly Windows itself does not let you find out what you are asking for. rcPaint is a RECT, a simple rectangle. Windows only keeps track of a dirty rectangle, not a region. New rectangles added by InvalidateRect() are merged with the existing one and you'll indeed easily end up with the entire client area.
The only reasonable way to tackle this problem is to pay attention to the ResizeBegin and ResizeEnd events. When you get ResizeBegin then you know that the user is dragging a window edge or corner. Knowledge that you can use to optimize the painting, skipping expensive bits that make the modal resizing loop work poorly.
I'm writing a paint application. User must be able to move with all objects after it's painted or edited. I have a brush and erase tool, so user can erase all or any part of object painted with brush. So I made an object DrawBrush that holds a System.Drawing.Region made from GraphicsPath.
But I don't know how to size it. I need to change size in every direction separately on mouse move (for example only to left)
can someone help me?
I'm able to do anything with this object (moving), but no sizing...
A region is like a fence - it simply marks out the boundary of an area. It does not "contain" any graphics, so resizing a region will have no direct/visible effect.
If you wish to be able to move or resize portions of a bitmap image within your editor, you will need to copy a piece of your main image (as specified by your region) into a temporary Bitmap. Then you can draw the tempoary bitmap back to your main image (in a different location and/or at a different size).
If you wish to be able to draw multiple objects in your painting program, and then edit them (move them around and resize them) independently later, then you will need to store each of them in a separate bitmap object and composite them together to display the final image on screen or save it to a flat bitmap format. If you don't keep all the shapes separately like this, you will lose too much information and you won't be able to edit them later.
Before you try to work out write the code to do this, you may need to think about the design of your editor - what does it need to do, and how will you achieve it? How is your "document" going to be described? (A single bitmap? many small bitmaps that are drawn at different locations? Vector paths?). If you write code before you understand how you will represent the document, you're likely to paint yourself into a corner (sorry about the pun) and get totally stuck.
I'm new to C# but not to OOP.
I'd like to make a "canvas" panel on which a user can draw shapes by mouseClick-ing but also delete them (nothing fancy, fixed sizes and whatnot, plain old pen objects). Like I said, I want the user to be able to delete whatever objects he alt-clicks on.
I'm not sure how exactly could I go about doing this. If I were using Flash, I'd probably do something like:
my_circle_object = new disc-or-whatever-etc;
canvas.addChild(my_circle_object);
my_circle_object.AddEventListener(MouseClickEvent, function_to_remove_child);
Now, since compiled languages are the devil when it comes to simple front-end UI related stuff, I'm sure It'll take 20 times more code to write this in C#. But, is there anything similar to my example?
I've spent all afternoon reading on things like GraphicsContainers, SmoothingPaint, Graphics Persistence using bitmaps etc. but I never found a simple add event method..
Thank you
The objects that you draw using the shape methods on a Graphics object (e.g. DrawLine, DrawEllipse, DrawRect, etc.) do not represent conceptual objects as far as the graphics API is concerned. Calling those functions simply draws the item to the graphics surface as a bitmap. Once that's done, there's nothing there to attach an event to.
You'll need to create your own shape types and have them draw themselves to the graphics object. You'll have to attach to the appropriate mouse events on whatever control you're using (I'm assuming a Panel) and do your own collision detection.
I am trying to write a simple program that lets me overlay a dot on top of an image when the image is clicked. I can save the X and Y data back to my database but then I will want to be able to call that information back at a later date and overlay the dots again via code unlike the first time when the user had to click the image.
I got as far as capturing the X and Y of the click no problem but I am having trouble finding examples specifically for what I am trying to do. All of the examples online seem to be for saving the image with the added graphic but I do not need to do that as it will be the same image every time.
Once I can do this, I also need to work out a way that I can detect what area of the image has been clicked. The areas I need to mark out vary in shape and size so I need to try and work out a way to 'map' these areas and then cross reference with the co-ordinates of the users click (I assume that I may need to do some clever geometry stuff for that?)
If anyone has any suggestions of what subjects/classes/methods etc. to research for either of my queries, I would very grateful.
Thanks in advance
You can use the System.Drawing namespace to achieve this.
Create a control and override OnPaint and OnPaintBackground. Store your clicks in a List
In OnPaintBackground, draw the image using DrawImageUnscaled using the graphics object which is passed to you as a parameter.
In OnPaint, loop through your points array and call graphics.FillElipse or similar to draw a little dot.
Because this isnt a retained mode graphics system, you need to keep drawing these items so this may not suit a large number of dots. In that case, you can create an in memory bitmap and get a graphics drawing object using graphics.FromImage.