Search code examples
c#wpfdatagridclipping

Undesired clipping when resizing wpf control under certain tresshold


When developing a custom control in WPF I noticed a strange clipping behavior. When I resize the control under a certain minimum boundaries/tress-hold it decides to clip the top 24 pixels of the control for reasons I don't understand.

It would be much appreciated if somebody could explain how i can achieve the desired graphical effect without clipping behavior as illustrated.

Image with no clipping - Expected Image with top 24 pixels clipped - Unexpected

The behavior occurs with the following XAML. Note that the XAML is a stripped down to make the it compliant with standard .net 4.0 controls.

        <Style x:Key="{ComponentResourceKey ResourceId=DataGridSelectAllButtonStyle, TypeInTargetAssembly={x:Type DataGrid}}" TargetType="{x:Type Button}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Button}">
                    <Grid>
                        <Rectangle x:Name="Border" Fill="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" SnapsToDevicePixels="True"/>
                        <Polygon x:Name="Arrow" Fill="Black" HorizontalAlignment="Right" Margin="8,8,3,3" Opacity="0.15" Points="0,10 10,10 10,0" Stretch="Uniform" VerticalAlignment="Bottom"/>
                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter Property="Stroke" TargetName="Border" Value="{DynamicResource {x:Static SystemColors.ControlDarkBrushKey}}"/>
                        </Trigger>
                        <Trigger Property="IsPressed" Value="True">
                            <Setter Property="Fill" TargetName="Border" Value="{DynamicResource {x:Static SystemColors.ControlDarkBrushKey}}"/>
                        </Trigger>
                        <Trigger Property="IsEnabled" Value="False">
                            <Setter Property="Visibility" TargetName="Arrow" Value="Collapsed"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <Style x:Key="DataGridStyle1" TargetType="{x:Type DataGrid}">
        <Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
        <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
        <Setter Property="BorderBrush" Value="#FF688CAF"/>
        <Setter Property="BorderThickness" Value="1"/>
        <Setter Property="RowDetailsVisibilityMode" Value="VisibleWhenSelected"/>
        <Setter Property="ScrollViewer.CanContentScroll" Value="true"/>
        <Setter Property="ScrollViewer.PanningMode" Value="Both"/>
        <Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type DataGrid}">
                    <Border Background="Transparent" Margin="0,24,0,0" >
                        <Border BorderBrush="{TemplateBinding BorderBrush}" 
                                BorderThickness="{TemplateBinding BorderThickness}" 
                                Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}" 
                                SnapsToDevicePixels="True">
                            <ScrollViewer x:Name="DG_ScrollViewer" Focusable="false">
                                <ScrollViewer.Template>
                                    <ControlTemplate TargetType="{x:Type ScrollViewer}">
                                        <Grid>
                                            <Grid.ColumnDefinitions>
                                                <ColumnDefinition Width="Auto" />
                                                <ColumnDefinition Width="*" />
                                                <ColumnDefinition Width="Auto" />
                                            </Grid.ColumnDefinitions>
                                            <Grid.RowDefinitions>
                                                <RowDefinition Height="Auto"/>
                                                <RowDefinition Height="Auto"/>
                                                <RowDefinition Height="*"/>
                                                <RowDefinition Height="Auto"/>
                                            </Grid.RowDefinitions>


                                            <Button Grid.Row="0" Command="{x:Static DataGrid.SelectAllCommand}" Focusable="false" Style="{DynamicResource {ComponentResourceKey ResourceId=DataGridSelectAllButtonStyle, TypeInTargetAssembly={x:Type DataGrid}}}" Visibility="{Binding HeadersVisibility, ConverterParameter={x:Static DataGridHeadersVisibility.All}, Converter={x:Static DataGrid.HeadersVisibilityConverter}, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" Width="{Binding CellsPanelHorizontalOffset, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"/>
                                            <DataGridColumnHeadersPresenter Grid.Row="0" x:Name="PART_ColumnHeadersPresenter" Grid.Column="1" Visibility="{Binding HeadersVisibility, ConverterParameter={x:Static DataGridHeadersVisibility.Column}, Converter={x:Static DataGrid.HeadersVisibilityConverter}, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"/>
                                            <Grid VerticalAlignment="Top" Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="1" ClipToBounds="False">
                                                <Grid.RenderTransform>
                                                    <TranslateTransform Y="-24" />
                                                </Grid.RenderTransform>
                                                <Grid.ColumnDefinitions>
                                                    <ColumnDefinition Width="{Binding NonFrozenColumnsViewportHorizontalOffset, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"/>
                                                    <ColumnDefinition Width="*"/>
                                                </Grid.ColumnDefinitions>
                                                <Rectangle Fill="Yellow" Grid.Row="1" Grid.ColumnSpan="3" Grid.Column="1" Height="48" />
                                            </Grid>
                                        </Grid>
                                    </ControlTemplate>
                                </ScrollViewer.Template>
                                <ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                            </ScrollViewer>
                        </Border>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
        <Style.Triggers>
            <Trigger Property="IsGrouping" Value="true">
                <Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
            </Trigger>
        </Style.Triggers>
    </Style>

   <DataGrid AutoGenerateColumns="False" HorizontalAlignment="Left" Margin="12,12,0,12" Name="dataGrid1" Width="868" FrozenColumnCount="2"
              ScrollViewer.HorizontalScrollBarVisibility="Visible" CanUserSortColumns="False" CanUserReorderColumns="False" 
              AreRowDetailsFrozen="True" CanUserAddRows="False" Style="{StaticResource DataGridStyle1}"
              ScrollViewer.VerticalScrollBarVisibility="Visible">
        <DataGrid.Columns>
            <DataGridTextColumn Header="Layer Name" Width="250" IsReadOnly="True" />
            <DataGridHyperlinkColumn Header="" Width="150" IsReadOnly="True" />
            <DataGridTemplateColumn  Width="*" HeaderStyle="{StaticResource DataGridColumnHeaderStyle}" IsReadOnly="True">
                <DataGridTemplateColumn.Header>                        
                    <Grid Height="18">
                        <TextBlock Text="100.0f" Margin="0,-10" />    
                    </Grid>
                </DataGridTemplateColumn.Header>
            </DataGridTemplateColumn>

        </DataGrid.Columns>     
        <DataGrid.GroupStyle>
            <GroupStyle>
                <GroupStyle.ContainerStyle>
                    <Style TargetType="{x:Type GroupItem}">
                        <Setter Property="Template">
                            <Setter.Value>
                                <ControlTemplate TargetType="{x:Type GroupItem}">
                                    <StackPanel>

                                        <TextBlock Text="test" />
                                        <ItemsPresenter />
                                    </StackPanel>
                                </ControlTemplate>
                            </Setter.Value>                                
                        </Setter>
                    </Style>                        
                </GroupStyle.ContainerStyle>
            </GroupStyle>
        </DataGrid.GroupStyle>  
    </DataGrid>

Solution

  • So after a few days of fiddling I found the culprit to be GetLayoutClip method. This method returns clipping geometry only if the bounds of the control is smaller than the minimum bounds.

    Easiest solution to override this behavior was to make a custom control inherit from the said base control. And to construct new clipping geometry with the extra margin taken into account.