17

I need to float out some content out of the ListBox as specified in a DataTemplate for an ListBox.ItemTemplate. I am using RenderTransform but the content gets clipped on ListBox boundaries. ClipToBounds is False for the entire visual tree.

I have read somewhere that WPF internally performs some clipping even when none is specified with dedicated clipping properties. I have also found out that using Canvas can sometimes cure the clipping problem but it does not help here.

How can I overcome this problem? Here is some XAML that I want to fix. Please note the entire left part of rectangle is missing.

    <ListBox>
        <ListBox.ItemTemplate>
            <DataTemplate>
                <Rectangle Fill="Red" Stroke="Green" StrokeThickness="4" Width="100" Height="50">
                    <Rectangle.RenderTransform>
                        <TranslateTransform X="-50" />
                    </Rectangle.RenderTransform>
                </Rectangle>
            </DataTemplate>
        </ListBox.ItemTemplate>

        42
    </ListBox>
wpfwannabe
  • 14,587
  • 16
  • 78
  • 129

2 Answers2

25

The ListBoxItem's are getting clipped by the ScrollViewer in the ListBox Template. To work around this I think you'll need to remove the ScrollViewer from the Template and if you need scrolling you can wrap the ListBox in a ScrollViewer

<ScrollViewer HorizontalScrollBarVisibility="Auto"
              VerticalScrollBarVisibility="Auto">
    <ListBox Margin="100,10,0,0">
        <ListBox.Template>
            <ControlTemplate TargetType="{x:Type ListBox}">
                <Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="1" SnapsToDevicePixels="true">
                    <ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                </Border>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsEnabled" Value="false">
                        <Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </ListBox.Template>
        <ListBox.ItemTemplate>
            <DataTemplate>
                <Rectangle Fill="Red" Stroke="Green" StrokeThickness="4" Width="100" Height="50">
                    <Rectangle.RenderTransform>
                        <TranslateTransform X="-50" />
                    </Rectangle.RenderTransform>
                </Rectangle>
            </DataTemplate>
        </ListBox.ItemTemplate> 42
    </ListBox>
</ScrollViewer>

Update

The ScrollViewer in the Template will generate a ScrollContentPresenter which in turn has the following GetLayoutClip

protected override Geometry GetLayoutClip(Size layoutSlotSize)
{
    return new RectangleGeometry(new Rect(base.RenderSize));
}

This class is Sealed so you can't derive from it to override this method. You would have to implement your own ScrollContentPresenter (e.g MyScrollContentPresenter) and probably your own ScrollViewer that uses MyScrollContentPresenter as well to make this work (and if you return null in this method I think that some items below the bounds of the ListBox could become visible as well)

Fredrik Hedblad
  • 83,499
  • 23
  • 264
  • 266
  • You seem to be spot on! But I need that `ScrollViewer` in its original place. Do you know what is so special in `ScrollVeiwer`'s internal implementation that hurts me? Is this something that can be overriden in a derived class? I have read somewhere that `GetLayoutClip` is the culprit but I can't make it work my way. – wpfwannabe Feb 01 '11 at 07:09
  • 1
    @wpfwannabe: Updated my answer. You are right that `GetLayoutClip` is the problem. Unfortunately, this time it's in a Sealed class (`ScrollContentPresenter`) so you can't derive from it. As far as I know, it would take alot of work to get this going – Fredrik Hedblad Feb 01 '11 at 10:20
  • 1
    Your answer is superb! Great help! Many thanks! In the mean time I have rearranged my layout differently with a sort of a split `ListBoxItem` which allows me to show the floating content inside the `ListBox` and thus I don't need this whole `ScrollViewer` malarkey anymore. Most of the time one can find a different solution to the same problem. This time I have had to change quite a bit of my templates to get things to work as expected but it seems to be a lot easier than having to deal with creating my own `ScrollViewer`. Thanks again! – wpfwannabe Feb 01 '11 at 10:36
  • @wpfwannabe: No problem :) Yes, your new solution seems like a much better way to go! Good luck – Fredrik Hedblad Feb 01 '11 at 10:45
-3

I stumbled upon a solution to this problem by accident while working around it. If you change the ScrollViewer's HorizontalScrollMode and VerticalScrollMode to "Disabled" within the style template, it will stop clipping in each direction respectively.

Edit: May not work for WPF. I tested with a UWP app. The fields in question are:

ScrollViewer.HorizontalScrollMode="Disabled"

ScrollViewer.VerticalScrollMode="Disabled"

Andrew
  • 43
  • 5
  • 1
    There is no such property as HorizontalScrollMode in WPF – vlaku Jan 06 '17 at 11:24
  • Sorry, should have mentioned, I had tested it and got it working in UWP apps, and I had assumed it would apply to WPF. Guess not though. – Andrew Jan 15 '17 at 04:10