WynApse Home Page
Home    Blog    About    Contact       
Latest Article:


My Tags:
My Sponsors:






Lifetime Member:

Clipping Silverlight Animated Rectangles






Beta 1 Notes

This example didn't require much beyond the basic 'plumbing' of Beta 1 previously discussed. The biggest things to change were replacing the trigger sections with <Canvas.Resources> sections, and removing BeginTime="1" from animations. Beyond that, it pretty much worked as written on March 18, 2007.

Original Text

In the GlyphMap Application, I used clipped sliding canvases as menus, then in my Samples and Code Application, I used sliding panels as "Eyelids" for exposing menu choices. I thought explaining the clipping of those might be useful to someone, particularly since I had trouble with them the first time.

I will add that during the building of the xaml for this article, I realized that the xaml in the other two above mentioned items has some unnecessary canvas declarations. I will probably NOT modify the GlyphMap one, since it is a stand-alone application, but the next menu changes will reflect some streamlining learned here.

Setup

In the examples shown on the canvas above, I've made the target rectangle one pixel larger all the way around to bracket the clipped area of the canvases.

The Problem

In the case of the GlyphMap utility, I wanted the menus to slide out from the small vertical button. It's simple to slide an object across the canvas, but I didn't want the menu to slide in from off-screen across a portion of the artwork and then finish where I wanted it.

On the canvas above, there is a Rectangle with the word "Slide" in it, and a hyperlink to the left labeled "Slide". If you click the hyperlink, the panel slides from it's position to the right and stops inside the target rectangle. Clicking it again will cause the rectangle to slide back out to it's original position. This is a very cool feature of WPF/E and has it's uses, and the xaml is pretty simple for that. The problem comes in if I want to have the rectangle slide in, but only appear as the sliding rectangle goes inside the target.

The Solution

Also on the canvas above is a "HideSlide" hyperlink. This link executes code to slide a rectangle from just to the right of the target into the target. If you do this, you'll see that the sliding rectangle is only displayed inside the target area.

If you compare the xaml for the clipped and unclipped slide, they're very similar:
  • Both contain an outer canvas to allow the Triggers to function
  • Both contain an inner canvas that contains the rectangle and text so they move together
  • Both contain a "Canvas.Triggers" section with two storyboards, one for sliding into the target and one for sliding out of the target
  • In both cases, one axis is being slid, e.g. the rectangle is being moved left-to-right or right-to-left to place it
Only one difference exists between the two:
  • The clipped slide has a Canvas.Clip section at the bottom that keeps anything from being visible outside the target rectangle
The DoubleAnimation animates the Canvas.Left to move the rectangles into and out of the target. I don't want to animate two axes here, so I have the rectangles initially located on the same horizontal plane as the target. That way I simply slide them into place. The clipped one only appears as it starts to move into the target, and as you can see, there's even text displayed over the top of the area the rectangle is 'parked' in.

It's possible to park the rectangles somewhere else on the canvas, but the Canvas.Left or Canvas.Top of the axis NOT being animated will have to be changed prior to the animation to make it work. It's just much simpler to keep the rectangle parked so it only has to be slide one way or another into position rather than do extra manipulation, and since it's hidden when it's parked, it's a don't-care item.

Part II -- Vertical Slide

I added a Part II to the canvas that's enabled with the Part II checkbox. In Part II I have right-to-left (RTL) and left-to-right (LTR) hyperlinks that move a "L-R" rectangle in and out of the target. Since both of these pieces of code move the same rectanglular canvas, it's possible to move it from right-to-left into the target using the RTL hyperlink then the LTR hyperlink will remove it from right-to-left as well, removing it from the target location... interesting effect enabled by those two pieces of code sharing the in/out flag.

Top-to-Bottom (TTB) and Bottom-to-Top (BTT) move a different rectangular canvas labeled "T-B" in and out of the target area very similarly to the LTR/RTL links.

If you play with the xaml for this, and point the TTB/BTT storyboards at the same target as the RTL/LTR, you'll see a demonstration of why the rectangles need to either be moved or kept on the same plane as the animation. If you slide the rectangle into the target using RTL/LTR, then the TTB/BTT will operate on it because it is located in a vertical plane with the target, but once it is removed with TTB/BTT, then it appears to not be working with the LTR/RTL links because it is moving in the area that is clipped. If this is confusing to you (as it was to me initially), simply remove the clipping from the canvas and it will become instantly obvious what the problem is :)

Part II -- Shutter

While playing with the rectangular animations, I wondered if I could move a rectangle in from a corner, and found that I could have both a Canvas.Left and Canvas.Top target to the same rectangle in a Storyboard.

That led to a series of drawings where I tried to map out a 'shutter' effect using 4 rectangles.

You can envision this as picturing a rectangle (Number 1 in my shutter) parked directly above the target. When it is animated, it moves down and to the left to stop with it's lower right-hand corner in the center of the target rectangle.

Rectangle 2 is parked directly to the right of the target and moved up and to the left, etc.

The reality is that all 4 of them begin life on the canvas parked to the right of the target. I did this on purpose to demonstrate that if I'm moving both axes, it doesn't matter where they begin. Once you animate them, however, they end up being parked in the logical locations as explained. Once again, if you remove the clipping this is visible, and actually takes some of the mystery out of the shutter effect :)

Conclusion

I hope I didn't muddy the water by going beyond a simple example, but I figured if I separated the 3 major pieces with their own canvases and progressed from one to the other it would be ok to get more elaborate. As always, the JavaScript and xaml are included below, and if you have any questions, please don't hesitate to write!

JavaScript for producing this page:

<script type="text/javascript">
var fSlideIn=0;
var fHideSlideIn=0;
var fLTRIn=0;
var fTTBIn=0;
var fShutterOpen=0;

// Basic non-hidden rectangle slide
function Slide(sender, args) {
if (fSlideIn==0)
{
sender.findName("SlideIn").Begin();
}
else
{
sender.findName("SlideOut").Begin();
}
}

// Non-hidden slide complete
// Set/Reset the flag appropriately
function SlideDone(sender, args) {
if (fSlideIn == 0)
{
fSlideIn = 1;
}
else
{
fSlideIn = 0;
}
}

// Hidden rectangle slide
function HideSlide(sender, args) {
if (fHideSlideIn==0)
{
sender.findName("HideSlideIn").Begin();
}
else
{
sender.findName("HideSlideOut").Begin();
}
}

function HideSlideDone(sender, args) {
if (fHideSlideIn == 0)
{
fHideSlideIn = 1;
}
else
{
fHideSlideIn = 0;
}
}

// Checkbox to show/hide the bottom part
function CheckBox(sender, args){
// 95 is checked
if (sender.findName("Checkbox").Indices=="95")
{
sender.findName("Checkbox").Indices="133";
sender.findName("PartII").Opacity=0;
}
else
{
sender.findName("Checkbox").Indices="95";
sender.findName("PartII").Opacity=1;
}
}

// Right-to-Left Slide code
function RTLSlide(sender, args) {
if (fLTRIn==0)
{
sender.findName("RTLIn").Begin();
}
else
{
sender.findName("RTLOut").Begin();
}
}

function RTLDone(sender, args) {
if (fLTRIn == 0)
{
fLTRIn = 1;
}
else
{
fLTRIn = 0;
}
}

// Left-to-Right Slide code
function LTRSlide(sender, args) {
if (fLTRIn==0)
{
sender.findName("LTRIn").Begin();
}
else
{
sender.findName("LTROut").Begin();
}
}

function LTRDone(sender, args) {
if (fLTRIn == 0)
{
fLTRIn = 1;
}
else
{
fLTRIn = 0;
}
}

// Top-to-Bottom Slide code
function TTBSlide(sender, args) {
if (fTTBIn==0)
{
sender.findName("TTBIn").Begin();
}
else
{
sender.findName("TTBOut").Begin();
}
}

function TTBDone(sender, args) {
if (fTTBIn == 0)
{
fTTBIn = 1;
}
else
{
fTTBIn = 0;
}
}

// Bottom-to-Top Slide code
function BTTSlide(sender, args) {
if (fTTBIn==0)
{
sender.findName("BTTIn").Begin();
}
else
{
sender.findName("BTTOut").Begin();
}
}

function BTTDone(sender, args) {
if (fTTBIn == 0)
{
fTTBIn = 1;
}
else
{
fTTBIn = 0;
}
}

// 4-rectangle Shutter code
function Shutter(sender, args) {
if (fShutterOpen==0)
{
sender.findName("ShutterClose").Begin();
}
else
{
sender.findName("ShutterOpen").Begin();
}
}

function ShutterDone(sender, args) {
if (fShutterOpen == 0)
{
fShutterOpen = 1;
}
else
{
fShutterOpen = 0;
}
}


</script>


XAML for producing this page:

<Canvas
xmlns="http://schemas.microsoft.com/client/2007"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">


<!-- Target Rectangle for upper part -->
<Rectangle Canvas.Left="199" Canvas.Top="19" Width="102" Height="102" Stroke="SteelBlue" />

<!-- Hyperlinks to activate upper part actions -->
<TextBlock Canvas.Left="20" Canvas.Top="60" FontSize="14" Foreground="Black" Text="Slide" Cursor="Hand" MouseLeftButtonDown="Slide" />
<TextBlock Canvas.Left="340" Canvas.Top="60" FontSize="14" Foreground="Black" Text="HideSlide" Cursor="Hand" MouseLeftButtonDown="HideSlide" />

<!-- Checkbox to show the bottom part -->
<!-- 133 unchecked box, 95 checked box, 123 unchecked button, 126 checked button -->
<Canvas Canvas.Top="140" Canvas.Left="20" Cursor="Hand"
MouseLeftButtonDown="CheckBox" >
<Glyphs x:Name="Checkbox" Canvas.Left="0" Canvas.Top="2"
Fill="Black" FontUri="wingding.ttf" FontRenderingEmSize="16" Indices="133" />
<TextBlock x:Name="CheckBoxText" FontSize="14" Canvas.Left="17" Foreground="Black" Text="Part II" />
</Canvas>

<!-- Non-Clipped slide -->
<!-- Container canvas for the triggers -->
<Canvas Canvas.Left="200" Canvas.Top="20" Width="100" Height="100">

<!-- Canvas to contain the rectangle and text to slide non-hidden. Start it to the left of the target -->
<Canvas x:Name="SlideRectangle" Canvas.Top="0" Canvas.Left="-100" Width="100" Height="100" >
<Rectangle Canvas.Top="0" Canvas.Left="0" Width="100" Height="100" Fill="CornSilk" Stroke="Black" />
<TextBlock Canvas.Top="40" Canvas.Left="40" FontSize="14" Foreground="Black" Text="Slide"/>
</Canvas>


<Canvas>
<Canvas.Resources>
<!-- First slide the rectangle into the target -->
<Storyboard x:Name="SlideIn" Completed="SlideDone">
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Left)" Storyboard.TargetName="SlideRectangle" From="-100" To="0" Duration="0:0:1" />
</Storyboard>

<!-- Then slide the rectangle out of the target -->
<Storyboard x:Name="SlideOut" Completed="SlideDone">
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Left)" Storyboard.TargetName="SlideRectangle" From="0" To="-100" Duration="0:0:1" />
</Storyboard>
</Canvas.Resources>
</Canvas>
</Canvas>
<!-- End of Non-Clipped slide -->

<!-- Clipped Slide -->
<!-- Container canvas for triggers and clipping -->
<Canvas Canvas.Left="200" Canvas.Top="20" Width="100" Height="100">

<!-- Canvas to contain the rectangle and text to slide hidden. Start it to the right of the target and hidden -->
<Canvas x:Name="HideSlideRectangle" Canvas.Top="0" Canvas.Left="100" Width="100" Height="100">
<Rectangle Canvas.Top="0" Canvas.Left="0" Width="100" Height="100" Fill="CornSilk" Stroke="Black" />
<TextBlock Canvas.Top="40" Canvas.Left="15" FontSize="14" Foreground="Black" Text="Hide Slide"/>
</Canvas>

<Canvas>
<Canvas.Resources>
<Storyboard x:Name="HideSlideIn" Completed="HideSlideDone">
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Left)" Storyboard.TargetName="HideSlideRectangle" From="100" To="0" Duration="0:0:1" />
</Storyboard>

<!-- Then slide the rectangle out of the target -->
<Storyboard x:Name="HideSlideOut" Completed="HideSlideDone">
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Left)" Storyboard.TargetName="HideSlideRectangle" From="0" To="100" Duration="0:0:1" />
</Storyboard>
</Canvas.Resources>
</Canvas>

<!-- Clip the menu to the 100X100 area -->

<Canvas.Clip>
<RectangleGeometry Rect="0, 0, 100, 100"/>
</Canvas.Clip>

</Canvas>
<!-- End of Clipped Slide -->


<!-- Canvas for Part II to enable show/hide on it -->
<Canvas x:Name="PartII" Opacity="0">

<!-- Target rectangle for lower part -->
<Rectangle Canvas.Left="199" Canvas.Top="199" Width="102" Height="102" Stroke="SteelBlue" />

<!-- Hyperlinks to activate lower part actions -->
<TextBlock Canvas.Left="350" Canvas.Top="240" FontSize="14" Foreground="Black" Text="RTL" Cursor="Hand" MouseLeftButtonDown="RTLSlide" />
<TextBlock Canvas.Left="130" Canvas.Top="240" FontSize="14" Foreground="Black" Text="LTR" Cursor="Hand" MouseLeftButtonDown="LTRSlide" />
<TextBlock Canvas.Left="240" Canvas.Top="140" FontSize="14" Foreground="Black" Text="TTB" Cursor="Hand" MouseLeftButtonDown="TTBSlide" />
<TextBlock Canvas.Left="240" Canvas.Top="350" FontSize="14" Foreground="Black" Text="BTT" Cursor="Hand" MouseLeftButtonDown="BTTSlide" />
<TextBlock Canvas.Left="20" Canvas.Top="350" FontSize="14" Foreground="Black" Text="Shutter" Cursor="Hand" MouseLeftButtonDown="Shutter" />

<!-- Outer canvas to allow triggering and clipping -->

<Canvas Canvas.Left="200" Canvas.Top="200" Width="100" Height="100">

<!-- Canvas for rectangle and text of the Left-Right action -->
<Canvas x:Name="RectangleRTL" Canvas.Top="0" Canvas.Left="100" Width="100" Height="100" >
<Rectangle Canvas.Top="0" Canvas.Left="0" Width="100" Height="100" Fill="CornSilk" Stroke="Black" />
<TextBlock Canvas.Top="40" Canvas.Left="40" FontSize="14" Foreground="Black" Text="L-R"/>
</Canvas>

<!-- Canvas for rectangle and text of the Top-Bottom action -->
<Canvas x:Name="RectangleTTB" Canvas.Top="-100" Canvas.Left="0" Width="100" Height="100" >
<Rectangle Canvas.Top="0" Canvas.Left="0" Width="100" Height="100" Fill="CornSilk" Stroke="Black" />
<TextBlock Canvas.Top="40" Canvas.Left="40" FontSize="14" Foreground="Black" Text="T-B"/>
</Canvas>

<!-- Canvases for rectangle and text of the 4 rectangles comprising the shutter -->
<Canvas x:Name="RectangleOne" Canvas.Top="0" Canvas.Left="100" Width="100" Height="100" >
<Rectangle Canvas.Top="0" Canvas.Left="0" Width="100" Height="100" Fill="CornSilk" Stroke="Black" />
<TextBlock Canvas.Top="75" Canvas.Left="80" FontSize="14" Foreground="Black" Text="1"/>
</Canvas>


<Canvas x:Name="RectangleTwo" Canvas.Top="0" Canvas.Left="100" Width="100" Height="100" >
<Rectangle Canvas.Top="0" Canvas.Left="0" Width="100" Height="100" Fill="CornSilk" Stroke="Black" />
<TextBlock Canvas.Top="75" Canvas.Left="10" FontSize="14" Foreground="Black" Text="2"/>
</Canvas>


<Canvas x:Name="RectangleThree" Canvas.Top="0" Canvas.Left="100" Width="100" Height="100" >
<Rectangle Canvas.Top="0" Canvas.Left="0" Width="100" Height="100" Fill="CornSilk" Stroke="Black" />
<TextBlock Canvas.Top="10" Canvas.Left="10" FontSize="14" Foreground="Black" Text="3"/>
</Canvas>


<Canvas x:Name="RectangleFour" Canvas.Top="0" Canvas.Left="100" Width="100" Height="100" >
<Rectangle Canvas.Top="0" Canvas.Left="0" Width="100" Height="100" Fill="CornSilk" Stroke="Black" />
<TextBlock Canvas.Top="10" Canvas.Left="80" FontSize="14" Foreground="Black" Text="4"/>
</Canvas>

<Canvas>
<Canvas.Resources>

<!-- Storyboards for Right-to-Left link -->
<Storyboard x:Name="RTLIn" Completed="RTLDone">
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Left)" Storyboard.TargetName="RectangleRTL" From="100" To="0" Duration="0:0:1" />
</Storyboard>

<Storyboard x:Name="RTLOut" Completed="RTLDone">
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Left)" Storyboard.TargetName="RectangleRTL" From="0" To="100" Duration="0:0:1" />
</Storyboard>

<!-- Storyboards for Left-to-Right link -->
<Storyboard x:Name="LTRIn" Completed="LTRDone">
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Left)" Storyboard.TargetName="RectangleRTL" From="-100" To="0" Duration="0:0:1" />
</Storyboard>


<Storyboard x:Name="LTROut" Completed="LTRDone">
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Left)" Storyboard.TargetName="RectangleRTL" From="0" To="-100" Duration="0:0:1" />
</Storyboard>

<!-- Storyboards for Top-to-Bottom link -->
<Storyboard x:Name="TTBIn" Completed="TTBDone">
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Top)" Storyboard.TargetName="RectangleTTB" From="-100" To="0" Duration="0:0:1" />
</Storyboard>


<Storyboard x:Name="TTBOut" Completed="TTBDone">
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Top)" Storyboard.TargetName="RectangleTTB" From="0" To="-100" Duration="0:0:1" />
</Storyboard>

<!-- Storyboards for Bottom-to-Top link -->
<Storyboard x:Name="BTTIn" Completed="TTBDone">
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Top)" Storyboard.TargetName="RectangleTTB" From="100" To="0" Duration="0:0:1" />
</Storyboard>


<Storyboard x:Name="BTTOut" Completed="TTBDone">
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Top)" Storyboard.TargetName="RectangleTTB" From="0" To="100" Duration="0:0:1" />
</Storyboard>

<!-- Storyboards for Shuttle -->
<Storyboard x:Name="ShutterClose" Completed="ShutterDone">
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Top)" Storyboard.TargetName="RectangleOne" From="-100" To="-50" Duration="0:0:1" />
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Left)" Storyboard.TargetName="RectangleOne" From="0" To="-50" Duration="0:0:1" />
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Top)" Storyboard.TargetName="RectangleTwo" From="0" To="-50" Duration="0:0:1" />
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Left)" Storyboard.TargetName="RectangleTwo" From="100" To="50" Duration="0:0:1" />
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Top)" Storyboard.TargetName="RectangleThree" From="100" To="50" Duration="0:0:1" />
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Left)" Storyboard.TargetName="RectangleThree" From="0" To="50" Duration="0:0:1" />
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Top)" Storyboard.TargetName="RectangleFour" From="0" To="50" Duration="0:0:1" />
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Left)" Storyboard.TargetName="RectangleFour" From="-100" To="-50" Duration="0:0:1" />
</Storyboard>


<Storyboard x:Name="ShutterOpen" Completed="ShutterDone">
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Top)" Storyboard.TargetName="RectangleOne" From="-50" To="-100" Duration="0:0:1" />
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Left)" Storyboard.TargetName="RectangleOne" From="-50" To="0" Duration="0:0:1" />
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Top)" Storyboard.TargetName="RectangleTwo" From="-50" To="0" Duration="0:0:1" />
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Left)" Storyboard.TargetName="RectangleTwo" From="50" To="100" Duration="0:0:1" />
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Top)" Storyboard.TargetName="RectangleThree" From="50" To="100" Duration="0:0:1" />
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Left)" Storyboard.TargetName="RectangleThree" From="50" To="0" Duration="0:0:1" />
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Top)" Storyboard.TargetName="RectangleFour" From="50" To="00" Duration="0:0:1" />
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Left)" Storyboard.TargetName="RectangleFour" From="-50" To="-100" Duration="0:0:1" />
</Storyboard>
</Canvas.Resources>
</Canvas>

<!-- Clip the menu to the 100X100 area -->

<Canvas.Clip>
<RectangleGeometry Rect="0, 0, 100, 100"/>
</Canvas.Clip>

</Canvas>

</Canvas>
<!-- End of Canvas for Part II to enable show/hide on it -->

</Canvas>
Copyright © 2006-2017, WynApse