As you know Silverlight 3 doesn’t support VisualBrush, which can make things like this pretty tricky.
Here’s my attempt at a workaround
It’s a control called VisualImage which can be pointed at any element and exposes it as a WriteableBitmap. You could bind an Image to this to create a reflection effect like this (don’t forget to look at Jeff Prosise’s sample):
You could even bind it to an ImageBrush… if it supported binding. To work around this, VisualImage can be bound to the ImageBrush instead. One application of this is for clipped, rounded corners on any element (here’s the WPF way):
You can try a live sample here (source on CodePlex).
Although VisualImage is a Control, it doesn’t render anything itself – it just acts as an intermediary between your visual and whatever you want to bind it to. Here’s everything you need (also available here):
/// <summary> /// VisualImage /// </summary> public class VisualImage : Control { #region Visual DependencyProperty public static readonly DependencyProperty VisualProperty = DependencyProperty.Register( "Visual", typeof( FrameworkElement ), typeof( VisualImage ), new PropertyMetadata( OnVisualChanged ) ); public FrameworkElement Visual { get { return (FrameworkElement)GetValue( VisualProperty ); } set { SetValue( VisualProperty, value ); } } private static void OnVisualChanged( DependencyObject obj, DependencyPropertyChangedEventArgs args ) { var visualImage = obj as VisualImage; visualImage.OnVisualChanged( args ); } private void OnVisualChanged( DependencyPropertyChangedEventArgs args ) { if ( args.OldValue != null ) ( (FrameworkElement)args.OldValue ).SizeChanged -= VisualImage_SizeChanged; if ( args.NewValue != null ) { var visual = (FrameworkElement)args.NewValue; visual.SizeChanged += VisualImage_SizeChanged; PrepareBitmap( (int)visual.RenderSize.Width, (int)visual.RenderSize.Height ); } } private void VisualImage_SizeChanged( object sender, SizeChangedEventArgs e ) { PrepareBitmap( (int)e.NewSize.Width, (int)e.NewSize.Height ); } #endregion // Visual DependencyProperty #region Bitmap DependencyProperty public static readonly DependencyProperty BitmapProperty = DependencyProperty.Register( "Bitmap", typeof( WriteableBitmap ), typeof( VisualImage ), null ); public WriteableBitmap Bitmap { get { return (WriteableBitmap)GetValue( BitmapProperty ); } set { SetValue( BitmapProperty, value ); } } #endregion // Bitmap DependencyProperty #region ImageBrush DependencyProperty public static readonly DependencyProperty ImageBrushProperty = DependencyProperty.Register( "ImageBrush", typeof( ImageBrush ), typeof( VisualImage ), null ); public ImageBrush ImageBrush { get { return (ImageBrush)GetValue( ImageBrushProperty ); } set { SetValue( ImageBrushProperty, value ); } } #endregion // VisualBrush DependencyProperty /// <summary> /// Initializes a new instance of the <see cref="VisualImage"/> class. /// </summary> public VisualImage() { } /// <summary> /// Prepares the bitmap. /// </summary> /// <param name="width">The width.</param> /// <param name="height">The height.</param> private void PrepareBitmap( int width, int height ) { Bitmap = new WriteableBitmap( width, height ); Invalidate(); } /// <summary> /// Invalidates the VisualImage and causes WriteableBitmap to be refreshed. /// </summary> public void Invalidate() { if ( Bitmap != null && Visual != null ) { Array.Clear( Bitmap.Pixels, 0, Bitmap.Pixels.Length ); Bitmap.Render( Visual, this.RenderTransform ); Bitmap.Invalidate(); if ( ImageBrush != null && ImageBrush.ImageSource != Bitmap ) { ImageBrush.ImageSource = Bitmap; } } } }
For performance reasons it only refreshes the WriteableBitmap when the target Visual’s size changes. You can call the Invalidate() method to force a refresh (consider calling it from CompositionTarget.Rendering if you want it to refresh every frame).
Here’s how to get rounded corners on anything (similar to WPF technique, with added VisualImage and named ImageBrush):
<Grid HorizontalAlignment="Center" VerticalAlignment="Center"> <Border x:Name="mask" Background="White" CornerRadius="20" Padding="10"/> <local:VisualImage Name="visualImage" Visual="{Binding ElementName=mask}" ImageBrush="{Binding ElementName=brush}"/> <Image Source="http://farm2.static.flickr.com/1429/1430528819_edb63b79a6.jpg"> <Image.OpacityMask> <ImageBrush x:Name="brush"/> </Image.OpacityMask> </Image> </Grid>
And here’s a reflection:
<TextBlock x:Name="myText" FontSize="96">Hello</TextBlock> <local:VisualImage Name="reflectImage" Visual="{Binding ElementName=myText}"/> <Image Source="{Binding Bitmap, ElementName=reflectImage}" RenderTransformOrigin="0.5,0.2"> <Image.RenderTransform> <ScaleTransform ScaleY="-0.8"/> </Image.RenderTransform> <Image.OpacityMask> <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1"> <GradientStop Offset="0" Color="#00FFFFFF"/> <GradientStop Offset="1" Color="#80FFFFFF"/> </LinearGradientBrush> </Image.OpacityMask> </Image>
7 responses so far ↓
WPF – Easy rounded corners for anything « Chris Cavanagh’s Blog // September 24, 2009 at 8:26 pm |
[...] Silverlight VisualBrush and rounded corners « Chris Cavanagh’s Blog // September 24, 2009 at 8:23 pm | Reply [...]
WPF Rounded Corners – and holes! « Chris Cavanagh’s Blog // September 24, 2009 at 8:27 pm |
[...] March 16, 2009 · 2 Comments UPDATE: Silverlight 3 joins in the fun! [...]
Dew Drop – September 25, 2009 | Alvin Ashcraft's Morning Dew // September 25, 2009 at 7:08 am |
[...] Silverlight VisualBrush and rounded corners (Chris Cavanagh) [...]
VisualBrush and Rounded corners | Silverlike - A Free Microsoft Silverlight 3 Directory // September 27, 2009 at 11:36 pm |
[...] is a very useful features in RIA. However, it’s only available in WPF. Chris Cavanagh has a work around to simulate VisualBrush using WriteableBitmap. You are able to apply this [...]
Tushar // December 7, 2009 at 5:21 pm |
Chris
Can this be used to put rounded corners on ScrollViewers too?
Chris Cavanagh // December 7, 2009 at 5:28 pm |
Tushar – You should be able to apply it to any element
Tushar // December 7, 2009 at 5:29 pm |
Thanks!
I only asked cos all the examples included Images or ImageBrushes.