Dotneteers.net
All for .net, .net for all!

Discovering Silverlight 3 – Poor Man’s Visualbrush Behavior

One of the things missing from Silverlight 3 is WPF’s Visualbrush. Visualbrush basically allows you to create a brush based on a visual element (including its visual changes), and use it for painting other elements. A common use is to create the fashionable reflection effect:

image

The above image is shamelessly stolen from Stefan Wick’s blog post.

Here is what the demo project looks like (sample requires Silverlight 3 Beta).

image

As you can see, a Visualbrush can be used not only to create reflections, but also to fill text, and other interesting things, too.

A key goal of mine was to create a good design-time experience. Unfortunately, I could only accomplish this goal partially: you will not get WYSIWYG preview in Blend, neither is databinding ideal. We also need some additional code in the codebehind - but it works, and requires only minimal XAML.

So, how does it work?

In Silverlight 3, you have Videobrush and Imagebrush. Unfortunately, these are sealed classes, and there doesn’t seem to be any way to create your own Brush (you can inherit from Brush or TileBrush, but what do do with it? Documentation is very sparse…). So, I could not just go ahead and create a custom brush.

Instead, I created a new Behavior. Behaviors and triggers are excellent new features that the Blend team is integrating into Blend 3. Basically, behaviors allow the interactive designer to add interactivity to UI elements from a pre-coded library, without writing any code. To really appreciate the power of Behaviors, check out this amazing Mix presentation: Creating Interactivity with Microsoft Expression Blend.

I am using Silverlight’s new Writablebitmap class to periodically (at every frame) render the Canvas into memory. Then, the source of an Imagebrush resource is changed to the Writablebitmap, and that’s it.

Here is the relevant code:

positionTransform.X = - AssociatedObject.Margin.Left - (double) AssociatedObject.GetValue(Canvas.LeftProperty);
positionTransform.Y = - AssociatedObject.Margin.Top - (double) AssociatedObject.GetValue(Canvas.TopProperty);

wb.Render(AssociatedObject, positionTransform);
Brush.ImageSource = wb;

Unfortunately, we have a big problem: It seems that binding to behaviors from XAML does not work in the Beta. To overcome this obstacle, I created the Brush that is used by the text and the reflection in the resources:

<ImageBrush x:Key="VisBrush" />

You can add the behavior to the Canvas (called vbSource here) by dragging it from the Assets panel onto the vbSource. By the way, the behaviors are a bit too difficult to find and use, maybe Blend should expose them more, at least in the Animation Workspace?

image

Now the only thing you need to do is set the BrushResourceName property to the resource key of the ImageBrush defined earlier, and the PoormansVisualBrush should be working properly. To save CPU cycles, you can also turn it off using the IsActive property.

note: using PoorMansVisualBrush can consume a lot of resources as the rendering of the brushed object is done twice: once to the screen, and once into the WritableBitmap. Use it sparingly and with caution!

Warning: this is just a proof-of-concept solution, and thus is sub-optimal and not fault tolerant and definitely not production quality. There are multiple ways to enhance it, such as the ability to render only every second, third, etc. frame thus saving a lot of CPU cycles.

 

 

 

 

 

 

You can download the source code of the entire solution here.


Posted Mar 26 2009, 12:43 AM by vbandi

Comments

Dew Drop - March 26, 2009 | Alvin Ashcraft's Morning Dew wrote Dew Drop - March 26, 2009 | Alvin Ashcraft's Morning Dew
on Thu, Mar 26 2009 14:24

Pingback from  Dew Drop - March 26, 2009 | Alvin Ashcraft's Morning Dew

Don Burnett's Designing For .NET Blog wrote Silverlight 3 Get’s a Poor Man’s VisualBrush Behavior
on Fri, Apr 17 2009 23:20

Behaviors are great things. Over at VBandi’s Blog he has created a behavior for Silverlight 3 that mimics

VBandi's blog wrote Discovering Silverlight 3 – Demos of new features
on Sun, May 3 2009 12:36

In early May, I gave a talk about the new features in Silverlight 3. As I’ve started to gather material

Image reflection effect in WPF with Expression Blend - Tutorial wrote Image reflection effect in WPF with Expression Blend - Tutorial
on Sat, Jul 4 2009 3:32

Pingback from  Image reflection effect in WPF with Expression Blend - Tutorial

Dan Vanderboom wrote re: Discovering Silverlight 3 – Poor Man’s Visualbrush Behavior
on Fri, Sep 11 2009 15:50

Hello András,

I tried building and running your sample with Silverlight 3 RTM and I'm not getting anything to show up in the reflection rectangle as expected.  I had to remove the third parameter to the WriteableBitmap constructor since that's changed since the beta.  Any ideas why it's not working now?

Thanks.

Dan

vbandi wrote re: Discovering Silverlight 3 – Poor Man’s Visualbrush Behavior
on Fri, Sep 11 2009 16:17

Hi Dan,

I have fixed this for the RTM, but never got around to actually writing a blog post. In the meantime, you can download the working sample at www.response.hu/.../PoorMansVisualBrushRTM.zip

Joseph Gershgorin wrote re: Discovering Silverlight 3 – Poor Man’s Visualbrush Behavior
on Thu, Oct 8 2009 9:52

Hi András, Thanks for the behavior!

I updated the Behavior to use the newer Blend 3 SDK System.Windows.Interactivity.dll, you were using one of the earlier beta behavior libraries.

I also:

* changed all the properties to dependency properties for nicer binding support when it comes to Silverlight.

* added a deatach override to clean up and unsubscribe to any events to prevent memory leaks on detachment

* Changed IsActive to IsEnabled and defaulted to true.

* added an UpdateIntervalDuration dependency property that defaults to 500 ms. By slowing down the updates you can get a chopier update result, but  this can be much nicer on the CPU.

You can get my changes here:

silverlightblend.com/.../PoorMansVisualBrush_10_08_2009.zip

Add a Comment

(required)  
(optional)
(required)  
Remember Me?