I'm trying to double buffer a winforms panel element. I use the panel for drawing shapes. Right now, I have something along the lines of:
class BufferPanel : Panel {
public BufferPanel() {
this.DoubleBuffer = true;
}
}
Mouse movement triggers the panel Refresh(). Some shapes are drawn in when Paint is triggered This is where I have a problem. These shapes are only drawn for a split second after mouse movement triggers a Refresh, then completely vanish. It's like they are only drawn on one buffer or something along those lines. This appears to only happen with Paint. For example, I can copy/paste the shapes into the mouse move method, and everything will work fine. Any thoughts on why?
Posted from comment:
Sounds like you aren't using the e.Graphics object from the Paint event or OnPaint override. Avoid using CreateGraphics.
Related
I have a control on my app that can be resized by the user, it has some button anchored to the top-right side and also a scrollbar.
The problem is that when the control is resized, the controls anchored to the right also changes the position, and only after a few ms the controls goes into the right place. So it looks like the child controls "shakes" while the parent control is resized.
I already tried all kind of things, like using SuspendLayout and ResumeLayout on the parent control, setting double buffering and other styles on each control to true, setting WS_EX_COMPOSITED bit, but nothing seems to make this issue go away.
This issue is present on other apps too, and is pretty annoying.
So is there anyway to fix that on .net?
Maybe making it render everything to a backbuffer, and then when everything is finished render it to screen?
I would create a new event that fires after resize is done, using a little timer magic stopping and starting a timer with an interval about 50ish ms on each resize event you can create this fake ResizeEnd kind of event.
On the first resize event I would stop the drawing of the UI using the dllimport call (dont recall which it was) to stop drawing the contents of your window or control. Then when the resize is done, enable drawing again using the same dllimport call.
The effect would be that it will only redraw itself after resize is done or every 50ms if you pause while resizing.
ResizeEnd: WinForms - action after resize event
SuspendDrawing: How do I suspend painting for a control and its children?
override the below virtual method from namespace using System.Drawing;
protected override Point ScrollToControl(Control activeControl)
{
return AutoScrollPosition;
}
should solve the problem !
I have problem with showing a diagram which is too big to see on one panel.
I have to scrollbars which should change the point of view on the diagram, but when i want to scroll the picture, the shapes are moving on the different position, everything getting crushed.
it looks like this here link
when i show it,and like this here link when i try to look on the bottom of the graph
it looks like application drawing the shapes every time when i scroll the panel, and when i go on the bottom of picture the point on the top left corner still is (0,0) not (0,500)
I have algorithm, which giving value of location on the panel and nr id of object to the array, then i have the loop which drawing it, getting info from dictionary about the object and his position from array.
How this problem can be solved ?
Thx for any advice's
Edited
I dont want to draw it again i want to draw one big graph, something like this(link in the comment), but i know that user can make for example 50 objects(shapes) and that kind of big graph cant be seen on the small panel so we have to have a chance to scroll and see the bottom of grapf, left side or this side which he want.
I'll try to give more details about application.
When you lunch it, you see the control panel(form1), where you adding events/functions/xor/or every of this option have their own shape on the graph.
So user adds for example event with text, pressing the button add which creates the object and adding it to the collection. He can adds events/functions,xor/or as many as he wants.
Ok, when he adds everything what he wanted, and now he would like to see graph so he pressing the button "generate the diagram", now the application is showing the next windwow with panel and scrollbars. You can see the window in links.
In the second form after this line
private void panel1_Paint(object sender, PaintEventArgs e){
I have algorithm which is putting the coordinate values to table, then forech loop is taking from dictionary ( collection):
text which should be shown on diagram in the middle of shape,
value which determine a type of shape on the panel.
from arrays loop taking the coordinate values.
This how its work, I can also put a code in here when its needed.
The standard mistake is forgetting to offset your drawing by the scroll position. Use the panel's AutoScrollPosition property, like this:
void panel1_Paint(object sender, PaintEventArgs e) {
e.Graphics.TranslateTransform(panel1.AutoScrollPosition.X, panel1.AutoScrollPosition.Y);
e.Graphics.DrawLine(Pens.Black, 0, 0, 300, 2000);
}
The Panel control is in general pretty cranky about painting, it was designed to be a container control. You typically also want it double-buffered and to force a repaint when it is being resized. Setting the DoubleBuffered and ResizeRedraw properties requires deriving your own control from Panel.
It looks like application drawing the shapes every time when i scroll the panel
Why don't you erase the drawing area and draw the shapes again?
Maybe you can post a code snippet?
The scenario is that i want the user to create a shape in a small panel that opens (the added shape can later be placed on the canvas), but for a better reference, i want the user to be able to move the semi-transparent panel somewhere on the canvas and then draw with the accurate reference.
Please tell me:
Which panel type to use
How to make it moving by clicking the mouse on the move button (not the whole panel as dragging will be used for drawing lines) and move it around.
How to make it semi transparent.
How to make it appear and disappear (this should be pretty simple)
How to somehow limit its movement inside the canvas so it cannot move on the ribbon
And I really really hope there will be something built-in in WPF that i'll be able to use, and i will not have to do it the hard way i.e. create a rectangle, and do customized hit testing on it to allow the user to draw on top of that rectangle, make that rectangle transparent, and add graphics items for the buttons and controls on that rectangle "panel".
I am asking this because i have never seen such feature in any Windows application and i have no idea what to use for this purpose and how to implement it. The closest thing to what i want is in Adobe Acrobat Pro, which is the small preview of the page that appears when i double click with the middle mouse button. It doesn't move, nor it is transparent or can be drawn upon, but scale and shape wise i want something similar.
You should be able to place a second Canvas inside of your main canvas, and place whatever UserControl you'd like with your "view" inside of it.
You'll have to handle the mouse click/drag for moving it around yourself, but otherwise, it should be very straightforward.
I have a UserControl with a few buttons on it. I want to override OnPaint and paint some other stuff on the control.
So I override OnPaint.
Whenever OnPaint gets called the ClipRectangle is always {0,0,0,0} and so nothing I do gets drawn on the control.
What am I doing wrong?
Ok, Ive sussed it now. Its a bit silly. I had a TableLayoutPanel that was docked to the control. That seems to clip out all the areas that we can paint on.
I created a new control that derives from TableLayoutPanel and used this control instead. Its OnPaint gets the full clip rect.
However, that wasnt any use anyway... It seems I have misunderstood how Windows painting works. I thought I would be able to paint directly over the top of my controls, but this isnt the case. The controls get placed on top of my painting.
Gonna have to mess about with panels to get this working I reckon..
I have a canvas object and I am sprinkling fantastic controls all over it. I'm using a ScaleTransform object to scale the canvas so I can zoom in/out.
I have wired up the controls so that I can drag them around, and the drag and drop works well by using MouseLeftButtonDown, MouseLeftButtonUp, and MouseMove. Now, I want to work on enabling an event when I click only on the Canvas. When I read the docs for the canvas object, I see that the MouseLeftButtonDown event only fires when it's over a UIElement.
Occurs when the left mouse button is
pressed (or when the tip of the stylus
touches the tablet PC) while the mouse
pointer is over a UIElement.
(Inherited from UIElement.)
Unfortunately, I want the opposite behavior. I want to know when the mouse is clicked on the Canvas while the mouse pounter isn't over any controls. Since I'm new to Silverlight, I could be doing this the wrong way. Is there something I have overlooked? Am I going about this the wrong way? I'm looking for a little help, and perhaps a lot of direction.
I'm no Silverlight guru, but could you add a transparent UIElement to the Canvas, below all other UIElements, and use it to determine if the user has clicked outside of any of the other drag/drop-able elements.
You want to know when a click happens on the Canvas and isn't over other controls?
The most natural thing is to capture the Canvas's MouseLeftButtonDown. Inside of that event, take a peak to see where the click happened. Then peak at the UIElements under the click. I recommend you keep everything in absolute coordinates to keep things straight. Something like:
void Page_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Point p = e.GetPosition(null);
var elements = VisualTreeHelper.FindElementsInHostCoordinates(p, App.Current.RootVisual);
foreach (var element in elements)
{
//Figure out if you're over a particular UI element
}
}
I think you may be interpreting the documentation wrong. According to MSDN, Canvas itself is an implementation of a UIElement:
System.Windows.UIElement
System.Windows.FrameworkElement
System.Windows.Controls.Panel
System.Windows.Controls.Canvas
From my experience, and correct me if I'm wrong, MouseLeftButtonDown usually only fires for the top-most UIElement clicked. So if you implement MouseLeftButtonDown for your Canvas, it should only fire when the Canvas is clicked, and NOT when the buttons are clicked. I'd say try it out first.
In WPF, I think this would be easily solved by routed events. However, Silverlight didn't get this feature. You may want to check out VisualTreeHelper.FindElementsInHostCoordinates. This article covers it a little bit.
http://www.andybeaulieu.com/Default.aspx?tabid=67&EntryID=95