-
-
Notifications
You must be signed in to change notification settings - Fork 2.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Too many ClipBorder controls cause rendering bugs #4524
Comments
Hey @RayCarrot the ClipBorder, yes, it's a special control, maybe it will gone sometimes. But to help you, here some solution hints.
<Grid>
<Grid.Resources>
<Style x:Key="ScrollBarItemsControlStyle" TargetType="{x:Type ItemsControl}">
<Setter Property="BorderThickness" Value="0" />
<Setter Property="VirtualizingPanel.IsVirtualizing" Value="True" />
<Setter Property="VirtualizingPanel.IsVirtualizingWhenGrouping" Value="True" />
<Setter Property="ScrollViewer.CanContentScroll" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ItemsControl}">
<Border x:Name="Border"
Margin="0"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ScrollViewer Margin="0"
Focusable="False"
Padding="{TemplateBinding Padding}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}"
CanContentScroll="{TemplateBinding ScrollViewer.CanContentScroll}">
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<ItemsControl ItemsSource="{Binding Source={x:Static local:MainWindow.Items}}"
Style="{StaticResource ScrollBarItemsControlStyle}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Auto"
VirtualizingPanel.CacheLengthUnit="Item"
VirtualizingPanel.ScrollUnit="Item"
VirtualizingPanel.VirtualizationMode="Recycling">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentControl Content="{Binding}">
<ContentControl.Template>
<ControlTemplate>
<Grid Width="64" Height="64">
<mah:ClipBorder Background="Red" x:Name="Back" CornerRadius="20" Margin="2">
<Grid Background="Transparent">
<TextBlock Text="{Binding}"
VerticalAlignment="Center"
HorizontalAlignment="Center" />
</Grid>
</mah:ClipBorder>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Back" Property="Background" Value="Green" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</ContentControl.Template>
</ContentControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Grid>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<controls:VirtualizingWrapPanel IsItemsHost="True"
ItemSize="64,64"
MouseWheelDelta="1"
Orientation="Horizontal"
ScrollLineDeltaItem="1"
MouseWheelDeltaItem="1"
SpacingMode="None"
StretchItems="False" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ControlTemplate>
<Grid Width="64" Height="64">
<Border Background="Red" x:Name="Back" CornerRadius="20" Margin="2">
<Grid Background="Transparent">
<TextBlock Text="{Binding}"
VerticalAlignment="Center"
HorizontalAlignment="Center" />
</Grid>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Back" Property="Background" Value="Green" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate> |
Thanks for the quick reply! I do want to clarify that the code I posted is just an example to easily be able to reproduce the issue. The ClipBorder can be helpful over setting the corner radius of a normal Border since it also clips the contents, like if there's an image inside or some other content that shouldn't leak out. That's why I use it in several ControlTemplates just like how it is in this library. Although I don't display 1000 items in a list like in this example it is true that there are some lists which I do not virtualize, mainly because of how they're sized or grouped. I suppose I could look into improving that. Thanks for linking the VirtualizedWrapPanel control! I didn't know such a thing existed - that will be very helpful! So yeah, I was hoping the issue could be fixed directly in the ClipBorder, but if it's more of a limitation with how the clipping works in large numbers in WPF then feel free to close the issue and I'll look into restructuring some things in my app and relying on virtualization more. |
"So yeah, I was hoping the issue could be fixed directly in the ClipBorder, but if it's more of a limitation with how the clipping works in large numbers in WPF then feel free to close the issue and I'll look into restructuring some things in my app and relying on virtualization more." Fir this I must go deeper in ClipBorder. Fun fact is, if I use Button with the default MahApps style which has ClipBorder inside, then there is no issue 🙄 |
@RayCarrot So, after some investigation, the problem is really to set the clip inside the ArrangeOverride method. I have tried to get another solution, doing the clip stuff a little bit different, but with the same result and now it works also without the virtualization. I use now this converter, to generate the clip for the inner grid root element public class ClipGeometryConverter : IMultiValueConverter
{
/// <inheritdoc />
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values.Length != 5)
{
return DependencyProperty.UnsetValue;
}
if (!(values[0] is double width)
|| !(values[1] is double height)
|| !(values[2] is CornerRadius cornerRadius)
|| !(values[3] is Thickness borderThickness)
|| !(values[4] is Thickness padding))
{
return DependencyProperty.UnsetValue;
}
if (width <= 0 || height <= 0)
{
return DependencyProperty.UnsetValue;
}
var clipGeometry = new StreamGeometry();
var childBorderInfo = new ClipBorder.BorderInfo(cornerRadius, borderThickness, padding, false);
using (var ctx = clipGeometry.Open())
{
ClipBorder.GenerateGeometry(ctx, new Rect(0, 0, width, height), childBorderInfo);
}
// Freeze the geometry for better perfomance
clipGeometry.Freeze();
return clipGeometry;
}
/// <inheritdoc />
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
} Then I use the converter in that way <ItemsControl Margin="10" ItemsSource="{Binding Source={x:Static local:MainWindow.Items}}"
Style="{StaticResource ScrollBarItemsControlStyle}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Auto"
VirtualizingPanel.CacheLengthUnit="Item"
VirtualizingPanel.ScrollUnit="Item"
VirtualizingPanel.VirtualizationMode="Recycling">
<ItemsControl.Resources>
<local:ClipGeometryConverter x:Key="ClipGeometryConverter" />
</ItemsControl.Resources>
<ItemsControl.ItemTemplate>
<DataTemplate>
<myControls:ClipBorder x:Name="Border"
BorderThickness="2"
BorderBrush="Black"
CornerRadius="20"
Margin="2">
<Border.Style>
<Style TargetType="{x:Type Border}">
<Setter Property="Background" Value="Red" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Green" />
</Trigger>
</Style.Triggers>
</Style>
</Border.Style>
<Grid x:Name="Root">
<Grid.Clip>
<MultiBinding Converter="{StaticResource ClipGeometryConverter}">
<Binding ElementName="Root" Path="ActualWidth" Mode="OneWay" />
<Binding ElementName="Root" Path="ActualHeight" Mode="OneWay" />
<Binding ElementName="Border" Path="CornerRadius" Mode="OneWay" />
<Binding ElementName="Border" Path="BorderThickness" Mode="OneWay" />
<Binding ElementName="Border" Path="Padding" Mode="OneWay" />
</MultiBinding>
</Grid.Clip>
<TextBlock Text="{Binding}"
VerticalAlignment="Center"
HorizontalAlignment="Center" />
</Grid>
</myControls:ClipBorder>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<!-- <WrapPanel /> -->
<controls:VirtualizingWrapPanel IsItemsHost="True"
ItemSize="64,64"
MouseWheelDelta="1"
Orientation="Horizontal"
ScrollLineDeltaItem="1"
MouseWheelDeltaItem="1"
SpacingMode="None"
StretchItems="False" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl> Here is the complete project with the changed ClipBorder: WpfApp3.zip It is not so elegant/easy/whatever but has no side effect with many controls. Maybe I can use this in MahApps as default. |
Thank you so much That converter seems to work really well 🙂 No more rendering issues as far as I can tell! Seeing this reminded me of a similar converter in the Material Design in XAML repo which I remember seeing before. Would be awesome if this could be integrated into the MahApps.Metro library at some point since this approach seems to cause fewer issues. |
…n to fix rendering bugs MahApps/MahApps.Metro#4524
Describe the bug
I use the
ClipBorder
control in various control styles in my app to allow for rounded corners. However after doing this I noticed that some of the controls which use it start experiencing rendering issues (rounded corners not applying, background color not changing when it should etc.) when there are too many of them in total.After a lot of experimenting I narrowed it down to the
ClipBorder
control causing it if there are too many in use at once. It appears to be specifically this line of code causing it, so it's somehow related to the clip being set.Steps to reproduce
The issue can easily be reproduced with this XAML:
and then a static property like this for the items to bind to:
This will display a big list of red squares which should be rounded. However as you will notice it breaks after rendering 145 of them.
It's not only the rounded corners that break, but other things too. For example say we made it so the color of the squares should change from red to green on mouse over:
Now this works on all squares up to 145 where this also breaks.
Environment
The text was updated successfully, but these errors were encountered: