Delay's Blog is the blog of David Anson, a Microsoft developer who works with the Silverlight, WPF, Windows Phone, and web platforms.
http://dlaa.me/
@DavidAns
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:
Actually renders like this:
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:
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:
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:
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:
<Button Content="Button" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<Path Stretch="Fill" .../>
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!
If you've made much use of data binding in WPF or Silverlight, you've probably come across the IValueConverter interface. IValueConverter sits between the data source and destination and gives the developer a chance to examine/alter/replace the data as it flows through the converter. It's been my experience that IValueConverter is a powerful and versatile tool for application developers.
As part of a recent project, I wanted to display some of an object's properties in a simple list. Specifically, I had an instance and I wanted to display a list of "Property Name: Property Value" entries for each property of that object. My background with ListBox led me to naturally think of using ItemsControl for the basis of my property viewer; the ItemsControl content model (with its support for DataTemplates) seemed like a natural fit.
Aside: One of the key things XAML enables is a distinct separation of implementation from representation. By explicitly separating most aspects of how an application looks (XAML) from how it works (code) as part of the developer/designer workflow, WPF and Silverlight help to enforce a level of encapsulation (in the object-oriented programming sense) that makes programs easier to write and maintain. In the ideal world, a program's functionality is entirely expressed in its code - and so it's possible for others to completely change that application's appearance without knowing or caring how it's implemented. Just like the web has CSS restyling contests, WPF has reskinning competitions!
So I knew I wanted to use ItemsControl and I knew I wanted to keep the UI aspects of the property viewer in XAML-space (what it looked like, what properties it displayed, etc.). I started looking at how IValueConverter might help and that led to the solution I describe here. PropertyViewer ends up being quite simple to use - and a good demonstration of the power of IValueConverter!
Here's the comment header from the code:
/// <summary> /// IValueConverter implementation that expands some/all of an object's /// public properties as a collection of bindable name/value instances. /// </summary> /// <remarks> /// * Bindable instances are of type PropertyDetails, a contained class /// that exposes a property's Name and Value as properties. /// * All public properties are enumerated by default; ConverterParameter /// can be used to specify which properties will be enumerated (and in /// which order) by passing a space-delimited list of names. /// * If DisplayNameAttribute is associated with a property, DisplayName /// will be used instead of the property name. /// * PropertyViewer can be used for simple data visualization, debugging /// of Bindings, and more. /// </remarks> /// <example> /// For ItemsControl/ListBox (accessing the object via DataContext): /// ItemsSource="{Binding Converter={StaticResource PropertyViewer}}" /// As above, but with a custom property list: /// ItemsSource="{Binding Converter={StaticResource PropertyViewer}, /// ConverterParameter='PropertA PropertyB'}}" /// For ItemsControl/ListBox, but specifying the object via Source: /// ItemsSource="{Binding Source={StaticResource ObjectInstance} /// Converter={StaticResource PropertyViewer}}" /// </example> public class PropertyViewer : IValueConverter { ... }
Like the comment says, it's easy to hook up a PropertyViewer:
<ItemsControl ItemsSource="{Binding Converter={StaticResource PropertyViewer}}"/>
Customizing the PropertyViewer is also easy:
<ItemsControl ItemsSource="{Binding Converter={StaticResource PropertyViewer}, ConverterParameter='Species EatsBugs RelativeMass'}">
Because the end result is a collection of objects, all the existing ItemsControl knowledge and techniques can be used to completely customize the look and feel of the property viewer!
The examples above came from a simple application I wrote to demonstrate PropertyViewer in action. By design, the exact same XAML and code work on both WPF and Silverlight. To prove it, I attached the complete source code for the combined Visual Studio 2008 solution to this blog (download it from the link at the bottom of this post). The sample displays a list of animals, one default PropertyViewer, and one customized PropertyViewer. Select any animal to find out more about it (note that the PropertyViewer updates automatically when the data source changes).
Here's what it looks like on WPF:
And on Silverlight:
Thanks to the power of IValueConverter, it's easy to use PropertyViewer to display an object's properties in a customizable way. Additionally, PropertyViewer can help troubleshoot data bindings: just drop one in the target location and you can see the available properties along with their current values. I originally wrote this for my project - but now I hope you can use it in yours!