Welcome to MSDN Blogs Sign in | Join | Help

Lying to the layout system for a good cause [Bringing LayoutTransform to Silverlight 2!]

People who want to rotate visual elements in Silverlight 2 are likely to use RotateTransform within RenderTransform - but they may not always get the results they expect! For example, using RenderTransform to achieve the following effect:

Sweet

Actually renders like this:

Whoops

But the problem isn't with RenderTransform - it's with using the wrong tool for the job! By design, RenderTransform applies its transformations (a rotation in this case) after the layout system has performed its measure/arrange pass. So when the elements in the example are being measured and arranged, the text is still horizontal. It's only after everything has been positioned that the text is finally rotated - and ends up in the "wrong" place. While it's possible to correct for this discrepancy by hard-coding all the relevant offsets in the XAML (very brittle and error-prone) or by adjusting all the offsets in code (only slightly more flexible - and a lot more work), these aren't great alternatives.

The right tool for the job is LayoutTransform which applies its transformations before the layout pass. With LayoutTransform, the text in the example is already rotated by the time the elements are measured and arranged, and the desired effect can be achieved quite simply.

But there's a catch: LayoutTransform doesn't exist in Silverlight 2 (Beta 1)...

However, there's no reason to let that stop us. Rotation is rotation whenever it happens, so maybe there's a way to get the already-optimized RenderTransform implementation to do the real work earlier in the layout pass. Unfortunately, we can't change when RenderTransform is applied.

But it turns out that we can tell a very carefully crafted set of lies to the layout system during the measure/arrange pass in order to convince it to lay things out as if it supported LayoutTransform - then we let RenderTransform do the work of actually rotating the content. The result is that we've got something that looks like LayoutTransform and behaves like LayoutTransform - so it might as well be LayoutTransform! :)

I've done just this and the result is something I've called LayoutTransformControl. The complete implementation can be found in LayoutTransformControl.cs in the attached ZIP. The XAML for LayoutTransformControl is quite simple and follows the well-known WPF Decorator model (ex: Border, Viewbox):

<local:LayoutTransformControl Angle="15">
    <TextBlock Text="I am rotated 15 degrees!"/>
</local:LayoutTransformControl>

Note: This assumes the "local" namespace prefix has been mapped to an assembly containing the LayoutTransformControl implementation:

xmlns:local="clr-namespace:LayoutTransformControlSample;assembly=YourAssemblyName"

In fact, the first picture of this post (the one that looked right!) was done with LayoutTransformControl. But it's easy to get simple scenarios right... So I also wrote a sample application that lets you interactively change the rotation angle and swap in different content:

LayoutTransformControl Sample Application

Attribution: The XAML example came from a post on designerslove.net; the image is from the set of stock Windows Vista wallpapers.

But it's easy to get a sample right... So I also wrote a test harness to exercise a handful of interesting elements in most of the interesting constraint scenarios and show RenderTransform along with LayoutTransformControl:

LayoutTransformControl Test Matrix on Silverlight

But the rules of layout are sufficiently complex and subtle that it's hard to tell if LayoutTransformControl is behaving properly without knowing how it's supposed to behave... So the test harness (and LayoutTransformControl!) also runs under WPF where LayoutTransform is supported and can be used to visually verify that LayoutTransformControl is doing what it should by comparing the bottom two rows:

LayoutTransformControl Test Matrix on WPF

Whew! :)

So - after all that posturing and seemingly comprehensive test coverage, you might expect me to be confident that LayoutTransformControl behaves correctly under all circumstances. Well... no. LayoutTransform is conceptually simple, but exhibits all kinds of weird and unexpected behaviors in practice. I've lost count of the number of times I had to stare at a bit of LayoutTransform output and figure out why it's correct - sometimes more than once for the same output! Add to that the fact that LayoutTransformControl is doing everything outside the core layout system, and I'm kind of surprised any of this works... :)

I do believe that LayoutTransformControl behaves correctly in all scenarios I've subjected it to, but I would not be surprised at all to hear about other scenarios where it breaks down. If you think you've found such a scenario, please let me know and I'll try to figure out what might be going on. (But before you do, please check the behavior on WPF - that's the first thing I'll do anyway!)

Notes:

  • This release of LayoutTransformControl supports only rotation because that's by far the most common scenario. However, I'm prepared to add support for scale, skew, and matrix if folks find this useful. (Party trivia: Translation has no effect during LayoutTransform.)
  • For the most part, using LayoutTransformControl is simply a matter of wrapping the desired content and setting the angle. But it's important to note that some properties will need to be moved from the child control to LayoutTransformControl. For example, when positioning things within a Grid, the Grid.Column and Grid.Row attached properties need to be set on LayoutTransformControl in order to be recognized by the parent Grid.
  • Examination of the test matrices above reveals two things that deserve more explanation:
    • Thing 1: The sizing of the Buttons in the Silverlight test matrix is different than in the WPF matrix. At first glance, the sizing seems to be wrong, but that's not actually the case. The default Button style in Silverlight Beta 1 has a bug that interferes with proper layout (and is completely unrelated to LayoutTransformControl). This can be seen with a simple example; adding the following to a new project should show a small, centered Button just like it does on WPF:
      <Button Content="Button" HorizontalAlignment="Center" VerticalAlignment="Center"/>
      However, on Silverlight Beta 1 the result is a very large Button instead. The problem is that the template in the default style includes the following:
      <Path Stretch="Fill" .../>
      The Stretch on that Path element causes the Button to measure much larger than it should (and a similar issue with the Line element affects the default ListBoxItem style). It's because the Button behaves as it does that sizing with LayoutTransformControl looks like it does. It's comforting to note that applying the Silverlight Button style to a WPF Button and using the real LayoutTransform gives the same wrong-looking behavior.
    • Thing 2: The rotated rectangle used by LayoutTransformControl for its child content is slightly different than that used by LayoutTransform under certain circumstances. This causes the child control to layout in a slightly longer, narrower rectangle and can be seen in the first column of the WPF test matrix. Perhaps surprisingly, this difference is deliberate. When determining the parameters of the rotated rectangle, the WPF algorithm finds the largest rectangle possible (as measured by area). It just so happens that the algorithm I came up with manages to find an even larger rectangle in certain cases. :) As far as I've seen, the algorithm in LayoutTransformControl is never worse - and sometimes better - so I decided it's a winner.
  • The example elements in the test matrix were chosen as follows:
    • Button - Represents the classic layout-friendly control which has a preferred size, but is willing to go smaller or larger as space permits
    • ContentControl - Containing a Border and a TextBlock, this simple Button-like layout acts like a Button should on Silverlight
    • TextBlock - Interesting for layout because it refuses to render smaller than the size it asked for
  • I've omitted any technical discussion of how LayoutTransformControl works from this blog post. For now, curious readers can refer to the commented source code. If there's interest, I'll write a follow-up post explaining what's really going on under the covers.

LayoutTransformControl was an interesting project that demonstrates once again the power and versatility of the WPF/Silverlight layout system. LayoutTransform and RenderTransform are like peanut butter and jelly - and I'm glad to help reunite them on Silverlight. If you've got a layout problem and RenderTransform isn't doing what you need, maybe LayoutTransformControl is the solution!

Published Tuesday, May 27, 2008 10:52 AM by Delay
Filed under: ,

Attachment(s): LayoutTransformControl.zip

Comments

# Silverlight 2 LayoutTransformControl

Tuesday, May 27, 2008 7:22 PM by 川西 裕幸のブログ

SilverlightのコントロールにはRenderTransformはありますが、LayoutTransformはありません。前者はレイアウトされた後で回転などの座標変換が適用されるので、レイアウトが正しく行われません。後者はレイアウト前に適用されるので、座標変換後のコントロールに対しレイアウトが正しく行われます。

# Silverlight Cream for May 28, 2008 -- #286

Wednesday, May 28, 2008 9:52 AM by Community Blogs

Two posts on the Layout system this morning: Dave Relyea and David Anson. From SilverlightCream.com

# LayoutTransform control in Silverlight 2

Monday, June 02, 2008 2:53 PM by XAML, WPF, Silverlight, .NET, Office 2007, Windows

I found a very good post: How to do LayoutTransform in Silverlight. Currently Silverlight supports RenderTransform

# The layout system lies have become a bit more elaborate [LayoutTransform functionality updated and enhanced for Silverlight 2 Beta 2!]

Thursday, July 03, 2008 5:02 AM by Delay's Blog

In the introductory post for LayoutTransformControl , I showed a trivial use case to demonstrate the

# re: Lying to the layout system for a good cause [Bringing LayoutTransform to Silverlight 2!]

Thursday, July 10, 2008 1:13 AM by kettch

Has this been tested on Beta 2? I don't know enough about how dependency properties and PropertyMetadata to debug this properly. In the LayoutTransformControl.cs file, I'm getting the following:

Error 1 The best overloaded method match for 'System.Windows.DependencyProperty.Register(string, System.Type, System.Type, System.Windows.PropertyMetadata)' has some invalid arguments C:\Software Projects\Samples\SilverlightLayout\LayoutTransformControlSilverlight\LayoutTransformControl.cs 36 69 LayoutTransformControlSilverlight

Error 2 Argument '4': cannot convert from 'System.Windows.PropertyChangedCallback' to 'System.Windows.PropertyMetadata' C:\Software Projects\Samples\SilverlightLayout\LayoutTransformControlSilverlight\LayoutTransformControl.cs 37 80 LayoutTransformControlSilverlight

And then again when it tries to set the AngleProperty dependency.

# re: Lying to the layout system for a good cause [Bringing LayoutTransform to Silverlight 2!]

Thursday, July 10, 2008 1:50 AM by kettch

My apologies. I came to this post from somewhere else, and didn't notice that you'd posted an updated version.

# re: Lying to the layout system for a good cause [Bringing LayoutTransform to Silverlight 2!]

Friday, September 26, 2008 2:21 PM by coughlinj

Hey Delay!

This was an amazing post.  I've made use of it and have noticed that it broke with RC0.  They have made the constructors of Transform internal.  Please let us know if you plan to update this yourself.

Otherwise if you post a technical discussion of how LayoutTransformControl works it would make my tracing into the code a little easier.

Either way thanks a bunch for the control it was working like a charm in Beta 2 and I'm sure I'll get it going again for the RC!

# re: Lying to the layout system for a good cause [Bringing LayoutTransform to Silverlight 2!]

Friday, September 26, 2008 2:36 PM by Delay

coughlinj,

Thank you for the kind words! I'm aware of the breaking changes with RC0 and hope to release an update of LayoutTransformControl very soon. I'd actually begun working on this some time ago, but was blocked by a couple of problems that have since been fixed.

Thanks for your patience!

# Maintaining pretenses with the layout system [LayoutTransform functionality updated for Silverlight 2!]

Monday, September 29, 2008 4:07 AM by Delay's Blog

In the introductory post for LayoutTransformControl and the feature-enhancing follow-up , I gave a variety

# An unexceptional layout improvement [Two LayoutTransformControl fixes for Silverlight 2!]

Tuesday, November 18, 2008 1:35 PM by Delay's Blog

I'd almost finished patting myself on the back for managing to implement WPF's LayoutTransform on Silverlight

# Having problems with layout? Switch to Plan B! [LayoutTransformControl scenarios for WPF]

Thursday, November 20, 2008 1:52 PM by Delay's Blog

When I first wrote about adding full LayoutTransform fidelity to Silverlight with my LayoutTransformControl

# A rose by any other name... [LayoutTransformControl on track to ship in the Silverlight Toolkit under the name LayoutTransformer!]

Monday, March 02, 2009 3:40 PM by Delay's Blog

I'm a believer in the power of LayoutTransform - so much so that I wrote a control to graft this capability

# A bit more than meets the eye [Easily animate LayoutTransformer with AnimationMediator!]

Thursday, April 09, 2009 4:04 PM by Delay's Blog

I came across a question on the Silverlight Toolkit support forum yesterday asking how to animate the

Anonymous comments are disabled
 
Page view tracker