使用 CornerRadius 在边框内滚动内容
Posted
技术标签:
【中文标题】使用 CornerRadius 在边框内滚动内容【英文标题】:Scrolling Content Inside A Border With CornerRadius 【发布时间】:2009-07-01 15:48:11 【问题描述】:我正在寻求解决我一直在努力解决的 WPF 问题的帮助。我设计了一个选项卡视图,将选项卡移到左侧并垂直显示它们。这些标签位于左上角和左下角的圆角边框内。
Normal Tab http://gallery.me.com/theplatz/100006/TabGood.png?derivative=medium&source=web.png&type=medium&ver=12464623560001
滚动选项卡时遇到问题。不是圆角剪切滚动内容,而是内容实际上骑在角落的顶部,如下所示:
Overlapping Tab http://gallery.me.com/theplatz/100006/TabBad/web.png?ver=12464623500001
这是 XAML:
<Style x:Key="SidebarTabControl" TargetType="TabControl">
<Setter Property="Background" Value="#FFC6D3DE" />
<Setter Property="Padding" Value="0,20,0,0" />
<Setter Property="TabStripPlacement" Value="Left" />
<Setter Property="IsSynchronizedWithCurrentItem" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="x:Type TabControl">
<Grid KeyboardNavigation.TabNavigation="Local">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200" MinWidth="150" MaxWidth="400" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Border
CornerRadius="10,0,0,10"
Background="TemplateBinding Background">
<ScrollViewer Grid.Column="0"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Disabled"
ClipToBounds="True">
<Border Padding="TemplateBinding Padding">
<TabPanel
IsItemsHost="True"
KeyboardNavigation.TabIndex="1"
Background="Transparent">
</TabPanel>
</Border>
</ScrollViewer>
</Border>
<ContentPresenter
Grid.Column="1"
Margin="0"
ContentSource="SelectedContent" />
<GridSplitter Grid.Column="0"
HorizontalAlignment="Right"
VerticalAlignment="Stretch"
Background="StaticResource SplitterBrush"
ShowsPreview="True"
Width="1" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="SidebarTab" TargetType="TabItem">
<Setter Property="Padding" Value="10,12,2,12" />
<Setter Property="BorderThickness" Value="0,1,0,1" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="x:Type TabItem">
<Border Padding="TemplateBinding Padding"
Name="tab"
BorderThickness="TemplateBinding BorderThickness"
BorderBrush="StaticResource SidebarTabBorderBrush">
<ContentPresenter Style="StaticResource SidebarTabForegroundStyle" Name="content" ContentSource="Header" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="tab" Property="Background" Value="StaticResource SidebarTabBackgroundBrushSelected" />
<Setter TargetName="tab" Property="BorderBrush" Value="StaticResource SidebarTabBorderBrushSelected" />
<Setter TargetName="content" Property="Style" Value="StaticResource SidebarTabForegroundStyleSelected" />
</Trigger>
<Trigger Property="IsSelected" Value="False">
<Setter TargetName="tab" Property="Background" Value="StaticResource SidebarTabBackgroundBrush" />
<Setter TargetName="tab" Property="BorderBrush" Value="StaticResource SidebarTabBorderBrush" />
<Setter TargetName="content" Property="Style" Value="StaticResource SidebarTabForegroundStyle" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
关于我可以完成我正在寻找的事情的任何想法?我尝试了一些 ZIndex 技巧,但似乎不起作用。
【问题讨论】:
【参考方案1】:为了完成我所寻找的,我使用了here 找到的解决方案,并对其进行了修改以满足我的需要。这是我最终得到的结果:
<Style x:Key="SidebarTabControl" TargetType="TabControl">
<Setter Property="Background" Value="#FFC6D3DE" />
<Setter Property="Padding" Value="0,20,0,20" />
<Setter Property="TabStripPlacement" Value="Left" />
<Setter Property="IsSynchronizedWithCurrentItem" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="x:Type TabControl">
<Grid KeyboardNavigation.TabNavigation="Local">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200" MinWidth="150" MaxWidth="400" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<!-- Background of the sidebar and our clipping bounds -->
<Border Grid.Column="0"
CornerRadius="10,0,0,10"
Background="TemplateBinding Background"
Name="mask" />
<!-- Border necessary so that the top tab does not get clipped prematurely -->
<Border Grid.Column="0" Background="Transparent">
<!-- Add opacity mask to clip contents as they're scrolled -->
<Border.OpacityMask>
<VisualBrush Visual="Binding ElementName=mask"/>
</Border.OpacityMask>
<ScrollViewer
VerticalScrollBarVisibility="Visible"
HorizontalScrollBarVisibility="Disabled">
<Border Padding="TemplateBinding Padding">
<TabPanel
IsItemsHost="True"
KeyboardNavigation.TabIndex="1"
Background="Transparent">
</TabPanel>
</Border>
</ScrollViewer>
</Border>
<ContentPresenter
Grid.Column="1"
Margin="0"
ContentSource="SelectedContent" />
<GridSplitter Grid.Column="0"
HorizontalAlignment="Right"
VerticalAlignment="Stretch"
Background="StaticResource SplitterBrush"
ShowsPreview="True"
Width="1" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
编辑:我找到了第一个 TabItem 剪切问题的解决方案。将 ScrollView 嵌套在第二个边框内并将 OpacityMask 应用于此边框,而不是 ScrollView 解决了问题。此外,我必须将 Background="Transparent" 显式设置为应用 OpacityMask 的边框,以使剪辑不会过早发生。
【讨论】:
【参考方案2】:您可以在圆形边框上设置Clip
,其几何形状与边框的轮廓相匹配。
<Border>
<Border.Clip>
<RectangleGeometry Rect="..." RadiusX="..." RadiusY="..."/>
</Border.Clip>
</Border>
请注意,您可能已经发现,Border
上的 ClipToBounds
不起作用,因为角和圆边之间的区域在 @987654325 的范围内@,所以不会被剪裁。
【讨论】:
感谢您的帮助。虽然我没有使用您的解决方案(因为大小不固定),但它确实为我指明了寻找答案的正确方向。我已经在下面发布了解决方案。 为什么不能直接将 RectangleGeometry 的属性绑定到 Border 上的对应属性上呢? 您必须原谅我对这个问题的无知,因为我对 XAML 和 WPF 还很陌生,但是我会绑定到哪些属性?我假设 RadiusX 和 RadiusY 不需要绑定,但我在边框上找不到将 Rect 绑定到的属性。 我尝试了以下方法,但没有成功:我有一个类似的问题,只是假设边框会自然地剪辑它的内容。如果内容无论如何都要戳穿,那么实现圆角半径有什么好处。
我最终创建了一个继承 Border 的自定义控件,并在其中设置了边框的剪辑。长话短说,我覆盖了 ArrangeOverride 以获取边框的最终大小,并创建了一个 RectangleGeometry 用作剪辑。
public class NodeBorder : Border
static NodeBorder()
DefaultStyleKeyProperty.OverrideMetadata(typeof(NodeBorder), new FrameworkPropertyMetadata(typeof(NodeBorder)));
protected override Size ArrangeOverride(Size finalSize)
Rect rect= new Rect(0, 0,finalSize.Width,finalSize.Height);
RectangleGeometry geometry = new RectangleGeometry(rect,this.CornerRadius.TopLeft,this.CornerRadius.TopRight);
this.Clip = geometry;
return base.ArrangeOverride(finalSize);
当您在 VS 中创建自定义控件时,它将为 Generic.xaml 中的控件创建样式。删除样式即可,不需要。
然后在您的 xaml 中,您可以简单地使用 yournamespace:NodeBorder 而不是 Border。
您可以获得所有的边框优点,但内置剪辑。没有额外的标记,它会完美地适合您的边框布局。
编辑: 我突然想到,在边框内剪裁可能很有用。
上面可以修改为包含依赖属性 ContentClip...
public static readonly DependencyProperty ContentClipProperty =
DependencyProperty.Register("ContentClip", typeof(Geometry), typeof(NodeBorder), new PropertyMetadata(null));
protected override Size ArrangeOverride(Size finalSize)
Rect rect= new Rect(0, 0,finalSize.Width-(this.BorderThickness.Left+ this.BorderThickness.Right),finalSize.Height-(this.BorderThickness.Top+ this.BorderThickness.Bottom));
RectangleGeometry geometry = new RectangleGeometry(rect,this.CornerRadius.TopLeft-1,this.CornerRadius.TopRight-1);
this.ContentClip = geometry;
return base.ArrangeOverride(finalSize);
... 并这样使用:
<Controls:NodeBorder
x:Name="xxx"
Canvas.Top="300"
Canvas.Left="10"
CornerRadius="20"
BorderBrush="Red"
BorderThickness="2"
Panel.ZIndex="10"
>
<Rectangle Width="60" Height="60"
Fill="Blue"
Clip="Binding ElementName=xxx, Path=ContentClip"
/>
</Controls:NodeBorder>
【讨论】:
以上是关于使用 CornerRadius 在边框内滚动内容的主要内容,如果未能解决你的问题,请参考以下文章
WPF 边框对象边框 CornerRadius 与边框背景 CornerRadius 不同