Condividi tramite


Why I Don't Like Canvas

In Silverlight 1.0, the only container element was Canvas. In Silverlight 2, there are a number of other layout containers, such as Grid, Border and StackPanel, and you can also make your own. Silverlight 2 applications can be completely layout-based and not rely on absolute positioning at all. Of course, you can still use Canvas. But should you? Most of the time, the answer is "no".

The usage of Canvas elements in controls and layout containers is often an anti-pattern and should be avoided. Unless you have some sort of user physics/drawing/design/charting surface or something else where thinking in any terms other than absolute positioning in a coordinate system is unnatural, you should not be using a Canvas anywhere.

Here are some of the reasons why:

Canvas always returns 0,0 for its desired size in MeasureOverride.
For layout purposes, no space will ever be allocated for a Canvas, even if the Canvas has a Width and Height set. For example, if you put a Canvas inside of a StackPanel, the StackPanel will not allocate any space for the Canvas no matter what.

Canvas returns exactly what it was given in ArrangeOverride.
This means that layout will never impose a clip on the Canvas, even if the Canvas has a Width and Height set and those exceed the space that layout is giving it. The example here is that you have a Grid, and you put a Canvas inside of it. The layout system will not clip the Canvas since it will never exceed the size it was given.

Canvas measures its children at infinity,infinity.
Children can be as big as they want to be. This may not be bad in and of itself, but if you want the children to fit into a container, you can just let layout do it for you. It is likely that you'll have to specify the sizes of a Canvas's children explicitly.

Canvas arranges its children at their desired size, and places them at their Canvas.Top/Left.
The Children of a Canvas can effectively be anywhere on the screen, and will not cause clipping (although they will be clipped by other explicit or layout clips in the ancestor chain). If this is a behavior that you need, you can use a TranslateTransform or negative Margins instead.

It may sound like I’m pretty down on Canvas. Well, yeah. They can be easily misused, and can cause more trouble than they are worth. The layout system can handle lots of things for you, and you should let it. Spend your time doing what only you can do, rather than duplicating layout functionality.

For example, let's say you are making your own little ScrollBar. We already have one you can use, but never mind, you want to make your own. You could inherit from UserControl (if you don't care about templating), and inside of the UserControl, put a Canvas with two RepeatButtons, a Rectangle (for the track) and a Thumb, and then when the Control is measured, calculate how much space you need for all of those things, and when your ScrollBar is arranged, figure out how where everything should go, and set the Canvas.Top, Canvas.Left, Width and Height properties on each of your elements. And that's what you would have had to do in Silverlight 1.0. But now, using Silverlight 2, why not let the layout system do the work for you?

Try using a Grid with three cells. Put the RepeatButtons in the first and third cells. Put a Rectangle in the second cell, then the Thumb (Grid can contain multiple children. If they are in the same cell, they will just be placed on top of each other, and can be manipluated with the Margin, HorizontalAlignment and VerticalAlignment properties.) Use the Margin property to move the Thumb around within its cell, based on the Value property.

I mentioned some of the typical reasons to use Canvas above. But are there creative uses of Canvas that take advantage of the very things that I whinge about? Of course. I can think of a few. Can you?

Comments

  • Anonymous
    May 20, 2008
    I extensively use Canvases on my experimental blog (http://www.nishantkumar.net/), mainly because I use SL 1.0. But, also because I load my blog posts as xamls, dynamically onto the child canvases. I havent thought about migrating to SL 2.0, but would user controls be useful in such a scenario?

  • Anonymous
    May 20, 2008
    I have to admit that only Canvas being available in 1.0 is in addition to being limited to Javascript one of the major things that dampens my enthusiasm for the coming-sometime-presumably-this-century first version of Silverlight Mobile - because mobile device applications really need layout controls.

  • Anonymous
    May 21, 2008
    nishantkumar - Yes, in SL 1.0 you don't have a choice. But in SL 2, you have a lot of options. The default project starts you off with a UserControl as the root element. You can definitely load dynamic content all over the place, including the root of the UserControl.

  • Anonymous
    May 21, 2008
    Kevin- If Silverlight for devices or whatever it will be called is going not be exactly the same as the "regular" flavor, I would be very surprised if it was because layout and the layout containers have been removed.

  • Anonymous
    May 21, 2008
    I have to try that out. Thanks, Dave!

  • Anonymous
    May 22, 2008
    It's kinda funniny but some of your reasons why you didn't like Canvas was some of the reasons we used Canvas for silverlightdesktop.net. You're right about over-use, but I think the Canvas is useful if you think about the things "on" the Canvas as being important. I use it kinda like a "grouping tool".

  • Anonymous
    July 26, 2008
    In Layout Fundamentals Part 1 , I started slowly, and demonstrated the need for a layout system. I touched

  • Anonymous
    August 18, 2008
    In Part 1 , we looked at how we construct a Dragging, docking, expanding panel, and added the ‘dragging’