Archive for December, 2007

ClipToBounds="Maybe"

Friday, December 28th, 2007

Dear Dr. WPF,

I have set ClipToBounds=”False” on the button in the following snippet, but it still clips its visuals.

<Page xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    Background="AntiqueWhite">
  <Grid Width="150" Height="100" ShowGridLines="True">
    <Grid.ColumnDefinitions>
      <ColumnDefinition />
      <ColumnDefinition />
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
      <RowDefinition />
      <RowDefinition />
    </Grid.RowDefinitions>
    <Button Grid.Row="1" Grid.Column="1" Width="100" Height="75"
        ClipToBounds="False" Content="Test" />
  </Grid>
</Page>

How can I prevent this clipping?

Thanks,
JC
 

 
Hi JC,

Yes, ClipToBounds is a bit of a misnomer. Conceptually, you can think of the ClipToBounds property as a toggle between “True” and “Maybe”.

As I describe in this forum post, the framework uses additional criteria (see Additional Clipping Criteria for Framework Elements below) besides ClipToBounds when determining the clipping geometry for a framework element. As such, setting ClipToBounds=”False” will not prevent clipping.

In that same post, I describe a trick that will prevent the clipping from occurring. Namely, you can wrap the elements that should not be clipped (e.g., your button) in a Canvas. Since a Canvas always arranges each child at its “desired size” (see Explanation of “Desired Size” below), the element assumes there is no need for clipping and won’t even try to clip its content.

Below is your snippet modified to use a Canvas in this manner.

<Page xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    Background="AntiqueWhite">
  <Grid Width="150" Height="100" ShowGridLines="True">
    <Grid.ColumnDefinitions>
      <ColumnDefinition />
      <ColumnDefinition />
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
      <RowDefinition />
      <RowDefinition />
    </Grid.RowDefinitions>
    <Canvas Grid.Row="1" Grid.Column="1" >
      <Button Width="100" Height="75" Content="Test" />
    </Canvas>
  </Grid>
</Page>

Additional Clipping Criteria for Framework Elements

If you are curious about the other criteria that is used to determine a clipping geometry, it is quite simply based on the “desired size” of the child. As I explain in this post, if a child’s desired size is larger than the size of the rect used to arrange the child, then the arrange rect actually becomes a “clipping rect” for the child element. More specifically, when the element is rendered, it’s GetLayoutClip() method will use the arrange size to determine a clipping geometry.

Explanation of “Desired Size”

WPF uses a 2-pass layout cycle in which a parent element first measures and then arranges each child. During the measure pass, the parent calls the Measure() method (inherited from UIElement) of each child and the child responds, in turn, by measuring each of its own children. This logic is located within the MeasureOverride() function of the child. This results in a recursive drill-down into the visual tree for the measure pass. Once it has measured its children, the child returns its “desired size” based on those measurements. This resultant desired size is the value returned from the MeasureOverride() function and is thereafter accessible on the child via its DesiredSize property.

Sidenote: There is a similar recursive drill-down for the arrange pass. The arranging of child elements happens within ArrangeOverride(). The size returned from ArrangeOverride() becomes the element’s “render size” and is accessible via its RenderSize property.

Supporting ClipToBounds=”False” in Custom Elements

If you would like to write elements that actually support ClipToBounds=”False”, it is actually very simple. Just override the GetLayoutClip() method as shown in the following class:

public class MyButton : Button
{
    protected override Geometry GetLayoutClip(Size layoutSlotSize)
    {
        return ClipToBounds ? base.GetLayoutClip(layoutSlotSize) : null;
    }
}

Et voîla! This custom button will treat ClipToBounds as the boolean that it claims to be! A value of ‘true’ means clipping occurs and a value of ‘false’ means no clipping occurs.

I wish the framework developers would have taken this approach. Or an even better (although less intuitive) design would make ClipToBounds a nullable boolean with a default value of null. The null value would essentially represent the current “Maybe” behavior and a value of false would represent the approach demonstrated above.

As always, I hope this helps!

Cheers,
Dr. WPF