How can I speed up the scrolling of UserControls in a WinForms app.?
My main form has trouble scrolling quickly on slow machines--painting for each of the small scroll increments is CPU intensive.
My form has roughly fifty UserControls (with multiple fields) positioned one below the other.
I’ve tried intercepting OnScroll and UserPaint in order to eliminate some of the unnecessary re-paints for very small scroll events, but the underlying Paint gets called anyway.
How can I streamline scrolling on slower machines?
The tried-and-true method is to use an offscreen bitmap which is updated only when the data represented by your control actually changes; then, all OnPaint needs to do is render that bitmap to the screen.
If your paint process is intensive, and since you have so many controls, you'll find this makes a massive difference to the performance of your application.
Note that using the DoubleBuffering control property won't help in your case--it does tell WinForms to render to an offscreen bitmap before rendering to the screen, but that still happens at every paint cycle since WinForms doesn't keep track of when the representation has changed.
So, you'd have to roll your own. It's not that difficult. Here's what looks like a reasonably good article on the subject.
You can also increase the size of the scroll step. For example
panel1.VerticalScroll.SmallChange = 100;
Will cause the panel to scroll it's content 100 units vertically per click of the scrollbar button. So you take bigger steps each time, that might make the experience feel better at least. And you can do the same for the horizontal scroll bar of course.
I have used tabs to eliminate scrolling.
Related
I m developing a window optimization application in C#. In that Project I have tablelayout panel. And i added pictureboxes to all cells. In application the user can add columns and rows at run-time.
But when the user dynamically add column or row, the application begins to slow down.
My question is with using panel instead of pictureboxes will speed up? (Panel backround image property do same job) Or it doesn't change anything?
I have already set double buffered proterty to true.
I have tablelayout panel. And i added pictureboxes to all cells.
If I understand you correctly, this won't work in WinForms. Here all controls are actual Windows windows, with OS-level handles and all the message pumping logic that entails. Plus the table layout triggers resizing in every single window under it, putting more pressure on it all.
Either go the less lazy route of drawing it all yourself in a memory bitmap and put it in a PictureBox to display, or use a more modern framework like WPF which doesn't have any of the afore-mentioned limitations. In fact, this is trivial in XAML.
In my graphic calculator, I am using canvas as a plane on which graphs of functions are rendered. I have noticed that whenever I input complex functions (I use bezier curves for that, so the more complex the function -> the more points I need to render for the graph to be smooth-looking) it gets quite laggy and overally doesn't perform well.
Which of these solutions are the least computationally costly / thus are the best approach?
1. (My current solution) Using huge Canvas inside a ScrollViewer that limits the view area
Cons I have found:
The whole canvas is rendered, even the non-visible part
Starts to lag when there is more than 50K elements on the canvas
2. Using just Canvas that is not so big, and management of panning / zooming through TransformMatrix
Cons I think there might be (solution not tried):
Won't it be even more laggy when it will have to redraw and recalculate everything dynammically?
3. Some other solutions?
Any help will be greatly appreciated.
If you can avoid it, you really don't want to be using discrete visuals to represent things like charts/graphs; they are very heavy, and are constantly participating in input handling and layout. I would recommend using WPF's drawing/geometry APIs directly. I would suggest implementing a custom control which performs its own rendering (see OnRender). I would also suggest implementing IScrollInfo so you can manage your own scrolling: you will always know the viewport size and position, which gives you the option of invalidating your geometry when scrolling, and then rebuilding only the parts of the graph that are actually in view. That should reduce the overhead significantly.
I have a custom control that inherits from Panel. I end up putting several DataGrids and Labels on this panel. When it gets too long it auto scrolls for me.
I really need the scrolling because it is a list of scanned in objects that will grow larger than the space on the screen will allow.
But when I scroll it flickers quite a lot. I would love to have it give me some smooth scrolling.
I have seen several "Compact Framework" double buffer examples out there, but they all are double buffering draw methods (ie graphics.DrawString). My custom control does not do any painting by itself. It just puts normal grids and labels on the panel and lets the panel paint them.
Is there a way to double buffer normal controls (again NOT custom painting)?
The Compact Framework Controls do not have the DoubleBuffered property or the underlying double-buffering mechanism. There's no way to add it either.
The only way to get double-buffering is to override the painting of the control and do your own.
Is there a preferred, fast method of scrolling within a user control in GDI+?
I've created a control that graphically renders a horizontally scrollable data plot.
Currently, my control operates by converting the position of a horizontal scroll bar into an offset into the data. The control then renders the data that exists between this starting point and an end point calculated based on the width of the control.
This method works, but is very slow. I do not wish to have to manually redraw the entire control surface upon each scroll event. Rather, I'd like to initialize the control by painting the entirety (or some portion) of the graphical data to an offscreen surface, and then virtually scroll the control surface by causing it to read the pre-rendered graphic data starting at the offset calculated by the position of the horizontal scroll bar.
Is BitBlt the only way to do this? Do I really have to manually copy graphics data from one surface to another? Can't I just take over the Paint event and cause it to read the data from the offscreen surface as it renders? This way, the copy and render action are one in the same.
Or, should I do something hokey like paint directly to a Panel control and then just literally scroll the panel itself from left to right?
CLARIFICATION:
Essentially, I want to know the correct way to scroll pre-rendered data. How does one scroll graphical data within a control? Redrawing the pre-rendered graphic is NOT a correct option.
Yes, I would try double-buffering. If you render to an off-screen bitmap, you can just scroll the bitmap around.
You can try rendering the entire graph to one big bitmap and let the scroll bars move it around. However, if your graph is extremely large, then you'll need to limit the size of the bitmap to the visible area, paint just what is visible to it, and handle scrolling virtually as you are now.
Testing on 32-bit Windows XP, I found the limitation is somewhere around 237.9 million pixels. That is certainly plenty for most controls, but it may not be enough for your application.
For the curious, here is the maximum Windows bitmap sizes we were able to create and use:
Width Height Area (pixels)
====== ====== ===========
32,767 7,261 237,921,187
25,000 9,517 237,925,000
23,792 10,000 237,920,000
20,000 11,896 237,920,000
15,861 15,000 237,915,000
15,000 15,861 237,915,000
11,896 20,000 237,920,000
10,000 23,792 237,920,000
9,517 25,000 237,925,000
7,261 32,767 237,921,187
I've searched around for an alternative way of drawing selection indicators for visual objects (like selected edges, lines etc.) without the use of ControlPaint.DrawReversibleFrame and related XOR methods. The reasons are unwanted XOR-ing "artifacts", reversibility not applying to bitmaps, no control of the actual visual look and slowness.
On the other hand I want to avoid having to repaint the whole scene (map actually) if a user decides he wants to deselect an object or two, because the repaint could be quite expensive.
So the only alternative I can see is implementing some basic drawing logic directly on a Bitmap, but with storing the previous contents of the pixels before they change. Then (in theory) I would be able to reapply old contents of, say, an selected edge rectangle if the user chooses to deselect that edge.
My question is whether you think this is a good idea or do you see some other alternatives to my problem (within the GDI+)?
Thanks in advance
If the selection indicator is just drawn on the top of the unselected object, you can use two bitmaps, draw all the unselected objects on the background one and the selection indicators on the other, and paint them both on screen.
Else, you can do the same, except that you render the selected objects instead of just indicators.
Only store the rectangles "of interest" in an off screen buffer. And repaint when the focus is lost. . . Or if you can redraw just the portion as it appears normally based on in memory data you should be fine. Otherwise it seems that you have the gist of it.