I am working on an application that uses several large canvas controls (several thousand pixels across), layered on top of each other. The canvas controls themselves are completely invisible, but each contains a number of controls, mainly images.
My question is, is there a recommended maximum size for a canvas, or is it purely a memory issue? And also, are we better off setting the Canvas size to (0, 0) and making use of the fact that we can happily render controls outside of the bounds of the canvas?
Thanks,
G
Watch out: the maximum size of a Silverlight canvas is 32767 points. This is because the size of UIElements is not stored as floats as it is in WPF, but in 32 bit quantities of which 16 bits form the integer of size and 16 bits form the floating part of it. So make sure your canvas is not bigger than that and not going to be.
The solution which you would need to make it larger is to do the scrolling yourself and position the objects yourself. In effect you're recreating the canvas. This is called Virtualizing in WPF terms.
The memory consumption won't be bigger depending on the canvas size but depend only on the number of controls and the cumulative memory size of those controls. However, if you're going to have a lot of WPF objects, the layout phase does take considerable time with more (say, more than 1000) objects. If that's going to be a problem you need to code it yourself again and have a cache of unused WPF objects of the same type lying around (since WPF object creation is also quite slow).
From my understanding of the innards of Canvas, it should need no additional memory for being bigger.
The first thing that comes to my mind on having a non-zero sized Canvas is that it allows one to put items on it relatively to any of the four corners, which helps when e.g. resizing the container.
Related
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 am making a game where I move images around. I have two big images which should have smaller images on top of them. I often move the smaller images from one of the bigger images onto the other. However I recently added a feature where the big image gets changed up mid-game. This caused it to have priority over the smaller images, and when I move the small images, they end up being moved behind the big images.
In short, I always want the big images to be in the background and the small ones to be in the foreground. Is there a property I can set on those to make this happen?
If you have more than one user interface element in a particular grid cell, the elements are layered on top of each other. Elements added earlier in XAML appear below (lower layer) than elements added later.
You can change the layering of the elements by changing the order in which they are added to the Grid. You can also control the layering by giving each element a value for Panel.ZIndex. Elements with higher values for ZIndex will appear to be on top of elements with lower values, regardless of the order in which they were added to the Grid.
Source:
http://wpf.2000things.com/tag/zindex/
MSDN page on the ZIndex property:
http://msdn.microsoft.com/en-us/library/system.windows.controls.panel.zindex%28v=vs.110%29.aspx
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.
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 need them to be ordered neatly and be able to use animations on each image.
The animation will be a simple "grow" effect to symbolize that the image has been selected. So when a user clicks the image, that Image will grow a bit.
So, a grid? A stack? I'm a bit confused.
A WrapPanel might be one possible choice. This will avoid you having to pre-determine the number of columns of images, making that flexible depending on available width.
When an image is selected, you could simply modify its RenderTransform temporarily to increase the scale factor, which will make it grow a bit without affecting the other images around it.