Click here to Skip to main content
6,629,885 members and growing! (23,519 online)
Email Password   helpLost your password?
    Beginner License: The Code Project Open License (CPOL)

Silverlight ClipToBounds - Can I Clip It?, Yes You Can!

By Colin Eberhardt

This technical blog posts shows how to add a new property ClipToBounds to clip your UI Elements.
Silverlight
Version:4 (See All)
Posted:16 May 2009
Updated:17 May 2009
Views:3,576
Bookmarked:3 times
Technical Blog
Announcements
Loading...
 
Search    
Advanced Search
Add to IE Search
printPrint   add Share
      Discuss Discuss   Broken Article?Report  
4 votes for this technical blog.
Popularity: 2.86 Rating: 4.75 out of 5

1

2

3
1 vote, 25.0%
4
3 votes, 75.0%
5
A Technical Blog article. View entire blog here.

With Silverlight, Panels do not clip their contents by default. See the following example:

noclip

Where we have a Grid containing another Grid which itself contains an ellipse, and a Canvas which contains an ellipse:

<Grid x:Name="LayoutRoot" Background="White">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
 
    <Grid Grid.Column="0" Background="Blue" Margin="20">                        
        <Grid Background="Yellow" Margin="20,40,-20,20">
            <Ellipse Fill="LightGreen" Width="80" Height="80" Margin="-40, -40, 0, 0"/>
        </Grid>
    </Grid>
 
    <Canvas Grid.Column="1"  Background="Aqua" Margin="20" >
        <Ellipse Fill="Red" Canvas.Top="-10" Canvas.Left="-10" Width="130" Height="130"/>
    </Canvas>
</Grid>

Often this is not the desired effect (although it is actually quite a useful feature of Canvas; You can simply add a Canvas to your visual tree without explicitly or implicitly setting its Size and use it as a mechanism for absolute positioning its children).

Fortunately Silverlight provides a Clip property on UIElement, allowing you to provide the clipping geometry for the element:

<Grid Width="200" Height="100">       
    <Grid.Clip>
        <RectangleGeometry Rect="0, 0, 200, 100"/>
    </Grid.Clip>
</Grid>

The above example creates a clipping geometry which matches the rectangular geometry of the Grid itself. Clearly more funky clipping geometries can be created, allowing for really cool effects, however most of the time I simply want my Panel clipped so that its children cannot escape!

The above example has a few problems, Firstly, it is a bit long-winded having to explicitly create the geometry each time I want to clip; Secondly, if my Grid’s size is calculated from its parent’s layout, how can I define the clip geometry in my XAML?; Finally, if my Grid’s geometry changes, its clipped geometry does not change.

In order to solve this problem I created a simple little attached behaviour, which allows you to define the clipping using the attached property Clip.ToBounds as illustrated below:

<UserControl x:Class="SilverlightClipToBounds.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:util="clr-namespace:Util" Width="300" Height="200">
    <Grid x:Name="LayoutRoot" Background="White">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
 
        <Grid Grid.Column="0" Background="Blue" Margin="20" util:Clip.ToBounds="true">       
            <Grid Background="Yellow" Margin="20,40,-20,20" util:Clip.ToBounds="true">
                <Ellipse Fill="LightGreen" Width="80" Height="80" Margin="-40, -40, 0, 0"/>
            </Grid>
        </Grid>
 
        <Canvas Grid.Column="1"  Background="Aqua" Margin="20" util:Clip.ToBounds="true">
            <Ellipse Fill="Red" Canvas.Top="-10" Canvas.Left="-10" Width="130" Height="130"/>
        </Canvas>
    </Grid>
</UserControl>

The result can be seen below:

crop

And here is the code for the attached behaviour itself:

public class Clip
{
    public static bool GetToBounds(DependencyObject depObj)
    {
        return (bool)depObj.GetValue(ToBoundsProperty);
    }
 
    public static void SetToBounds(DependencyObject depObj, bool clipToBounds)
    {
        depObj.SetValue(ToBoundsProperty, clipToBounds);
    }
 
    /// <summary>
    /// Identifies the ToBounds Dependency Property.
    /// <summary>
    public static readonly DependencyProperty ToBoundsProperty =
        DependencyProperty.RegisterAttached("ToBounds", typeof(bool),
        typeof(Clip), new PropertyMetadata(false, OnToBoundsPropertyChanged));
 
 
    private static void OnToBoundsPropertyChanged(DependencyObject d,
        DependencyPropertyChangedEventArgs e)
    {
        FrameworkElement fe = d as FrameworkElement;
        if (fe != null)
        {
            ClipToBounds(fe);
 
            // whenever the element which this property is attached to is loaded
            // or re-sizes, we need to update its clipping geometry
            fe.Loaded += new RoutedEventHandler(fe_Loaded);
            fe.SizeChanged += new SizeChangedEventHandler(fe_SizeChanged);
 
        }
    }
 
    /// <summary>
    /// Creates a rectangular clipping geometry which matches the geometry of the
    /// passed element
    /// </summary>
    private static void ClipToBounds(FrameworkElement fe)
    {
        if (GetToBounds(fe))
        {
            fe.Clip = new RectangleGeometry()
            {
                Rect = new Rect(0, 0, fe.ActualWidth, fe.ActualHeight)
            };
        }
        else
        {
            fe.Clip = null;
        }
    }
 
    static void fe_SizeChanged(object sender, SizeChangedEventArgs e)
    {
        ClipToBounds(sender as FrameworkElement);
    }
 
    static void fe_Loaded(object sender, RoutedEventArgs e)
    {
        ClipToBounds(sender as FrameworkElement);
    }    
}

When the ToBounds property is associated with an element, ClipToBounds is invoked to create a rectangular clip geometry. We also add event handlers for Loaded, which is a very useful event which is fired when an element has been laid out and rendered, in other words it’s size will have been computed, and SizeChanged. In the event handlers for both we simply update the clipping geometry.

This can be seen in action here, where clicking on the Grids or Canvas increases their size, with the clipping geometry growing accordingly:

[CodeProject does not support Silverlight applets, see it in action on my blog]

You can download a demo project here: sliverlightcliptobounds.zip 

Can I Clip It?, Yes You Can!

Regards, Colin E.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Colin Eberhardt


Member
Visit my blog - Colin Eberhardt's Adventures in WPF.

I am currently working as a Lead Developer at Scott Logic, a provider of bespoke financial software and consultancy for the investment banking, stockbroking, asset management and hedge fund communities.
-
Occupation: Team Leader
Company: Scott Logic
Location: United Kingdom United Kingdom

Other popular Silverlight articles:

Article Top
You must Sign In to use this message board.
FAQ FAQ 
 
Noise Tolerance  Layout  Per page   
 Msgs 1 to 3 of 3 (Total in Forum: 3) (Refresh)FirstPrevNext
GeneralSee it in use! PinmemberHelen Warn16:21 18 Jul '09  
GeneralI works ! PinmemberBenjamin Mayrargue10:50 10 Jun '09  
Generalnice ! PinmemberBenjamin Mayrargue10:42 10 Jun '09  

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

PermaLink | Privacy | Terms of Use
Last Updated: 17 May 2009
Editor:
Copyright 2009 by Colin Eberhardt
Everything else Copyright © CodeProject, 1999-2009
Web17 | Advertise on the Code Project