如何创建 WPF 圆角容器?
Posted
技术标签:
【中文标题】如何创建 WPF 圆角容器?【英文标题】:How do I create a WPF Rounded Corner container? 【发布时间】:2010-09-12 08:39:08 【问题描述】:我们正在创建一个 XBAP 应用程序,我们需要在单个页面的不同位置有圆角,并且我们希望有一个 WPF 圆角容器来放置一堆其他元素。有没有人对我们如何最好地实现这一点有一些建议或示例代码?是使用样式还是创建自定义控件?
【问题讨论】:
警告:如果您在圆角矩形边框内放置一行文本,像我这样的老人会看着它并想,“Macintosh 80 年代按钮!” 你不知道我多么怀念 80 年代的 Macintosh!我认为这个问题应该明确说明是否需要剪裁角落,因为所选答案不会剪裁边框。 【参考方案1】:您不需要自定义控件,只需将容器放在边框元素中即可:
<Border BorderBrush="#FF000000" BorderThickness="1" CornerRadius="8">
<Grid/>
</Border>
您可以将<Grid/>
替换为任何布局容器...
【讨论】:
对于任何厚度对象(BorderThickness 或 CornerRadius),如果所有 4 个都相同,您可以指定单个数字,例如 CornerRadius="8"。<Border BorderBrush="Black" BorderThickness="1" CornerRadius="8">
是一个合适的替代品,更简洁一点
@Patrik Deoghare,别误会,kobusb 很棒……但我认为 WPF 团队在构建它并使其易于利用方面非常棒。 (尽管有人可能会争辩说,没有内置此功能的现代 UI 平台......真的不是现代 UI 平台。)
顺便说一下,Border 的实现非常有启发性......如果你想深入挖掘的话。例如,它如何使用 StreamGeometry ...
好的,我通过增加边框厚度来实现它,但是这个解决方案不会剪裁容器内的孩子的角落。它仅通过限制子项的高度和宽度来防止角与边界半径重叠。 Chris Cavanagh 的解决方案处理了这种情况。遗憾的是,我希望这个解决方案能奏效,因为它看起来更高效、更优雅。【参考方案2】:
我知道这不是对最初问题的回答……但您经常想要剪裁刚刚创建的圆角边框的内部内容。
Chris Cavanagh 提出了一个 excellent way 来做这件事。
我尝试了几种不同的方法来解决这个问题......我认为这个很不错。
这是下面的 xaml:
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Background="Black"
>
<!-- Rounded yellow border -->
<Border
HorizontalAlignment="Center"
VerticalAlignment="Center"
BorderBrush="Yellow"
BorderThickness="3"
CornerRadius="10"
Padding="2"
>
<Grid>
<!-- Rounded mask (stretches to fill Grid) -->
<Border
Name="mask"
Background="White"
CornerRadius="7"
/>
<!-- Main content container -->
<StackPanel>
<!-- Use a VisualBrush of 'mask' as the opacity mask -->
<StackPanel.OpacityMask>
<VisualBrush Visual="Binding ElementName=mask"/>
</StackPanel.OpacityMask>
<!-- Any content -->
<Image Source="http://chriscavanagh.files.wordpress.com/2006/12/chriss-blog-banner.jpg"/>
<Rectangle
Height="50"
Fill="Red"/>
<Rectangle
Height="50"
Fill="White"/>
<Rectangle
Height="50"
Fill="Blue"/>
</StackPanel>
</Grid>
</Border>
</Page>
【讨论】:
Blacklight Controls (blacklight.codeplex.com) 还有一个漂亮的小控件,称为 ClippingBorder,它还允许您将内容剪辑到圆角。 ClippingBorder 的一个优点是它不使用 VisualBrush(这是成本最高(就性能而言)的画笔之一)。 但是,我只是看了一下 ClippingBorder 的实现......它在其默认 ControlTemplate 中使用了 4 个 ContentControl(每个角一个)......所以我不确定它是否比上面的 VisualBrush 方法性能更高或更低。我推测可能性能较差。 如果需要剪辑,这应该是选择的答案。 这是唯一真正的方法!令人惊讶的是,这不是边框内元素的默认行为。 @eranotzap 很高兴您喜欢这个答案。唯一的缺点是您使用的是 VisualBrush,这是一个性能更重的项目。我在下面的另一个答案向您展示了如何避免这种 VisualBrush ...【参考方案3】:我只需要自己做这个,所以我想我会在这里发布另一个答案。
这是另一种创建圆角边框并剪裁其内部内容的方法。这是使用 Clip 属性的直接方法。如果您想避免使用 VisualBrush,那就太好了。
xaml:
<Border
Width="200"
Height="25"
CornerRadius="11"
Background="#FF919194"
>
<Border.Clip>
<RectangleGeometry
RadiusX="Binding CornerRadius.TopLeft, RelativeSource=RelativeSource AncestorType=x:Type Border"
RadiusY="Binding RadiusX, RelativeSource=RelativeSource Self"
>
<RectangleGeometry.Rect>
<MultiBinding
Converter="StaticResource widthAndHeightToRectConverter"
>
<Binding
Path="ActualWidth"
RelativeSource="RelativeSource AncestorType=x:Type Border"
/>
<Binding
Path="ActualHeight"
RelativeSource="RelativeSource AncestorType=x:Type Border"
/>
</MultiBinding>
</RectangleGeometry.Rect>
</RectangleGeometry>
</Border.Clip>
<Rectangle
Width="100"
Height="100"
Fill="Blue"
HorizontalAlignment="Left"
VerticalAlignment="Center"
/>
</Border>
转换器的代码:
public class WidthAndHeightToRectConverter : IMultiValueConverter
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
double width = (double)values[0];
double height = (double)values[1];
return new Rect(0, 0, width, height);
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
throw new NotImplementedException();
【讨论】:
非常酷的解决方案。我正在为在不同状态下需要外发光和内发光的按钮创建一个控件模板,这有助于解决问题。【参考方案4】:基于 VB.Net 代码的 kobusb 边界控制解决方案的实现。我用它来填充 Button 控件的 ListBox。 Button 控件是从 MEF 扩展中创建的。每个扩展都使用 MEF 的 ExportMetaData 属性作为扩展的描述。扩展是 VisiFire 图表对象。用户按下从按钮列表中选择的按钮来执行所需的图表。
' Create a ListBox of Buttons, one button for each MEF charting component.
For Each c As Lazy(Of ICharts, IDictionary(Of String, Object)) In ext.ChartDescriptions
Dim brdr As New Border
brdr.BorderBrush = Brushes.Black
brdr.BorderThickness = New Thickness(2, 2, 2, 2)
brdr.CornerRadius = New CornerRadius(8, 8, 8, 8)
Dim btn As New Button
AddHandler btn.Click, AddressOf GenericButtonClick
brdr.Child = btn
brdr.Background = btn.Background
btn.Margin = brdr.BorderThickness
btn.Width = ChartsLBx.ActualWidth - 22
btn.BorderThickness = New Thickness(0, 0, 0, 0)
btn.Height = 22
btn.Content = c.Metadata("Description")
btn.Tag = c
btn.ToolTip = "Push button to see " & c.Metadata("Description").ToString & " chart"
Dim lbi As New ListBoxItem
lbi.Content = brdr
ChartsLBx.Items.Add(lbi)
Next
Public Event Click As RoutedEventHandler
Private Sub GenericButtonClick(sender As Object, e As RoutedEventArgs)
Dim btn As Button = DirectCast(sender, Button)
Dim c As Lazy(Of ICharts, IDictionary(Of String, Object)) = DirectCast(btn.Tag, Lazy(Of ICharts, IDictionary(Of String, Object)))
Dim w As Window = DirectCast(c.Value, Window)
Dim cc As ICharts = DirectCast(c.Value, ICharts)
c.Value.CreateChart()
w.Show()
End Sub
<System.ComponentModel.Composition.Export(GetType(ICharts))> _
<System.ComponentModel.Composition.ExportMetadata("Description", "Data vs. Time")> _
Public Class DataTimeChart
Implements ICharts
Public Sub CreateChart() Implements ICharts.CreateChart
End Sub
End Class
Public Interface ICharts
Sub CreateChart()
End Interface
Public Class Extensibility
Public Sub New()
Dim catalog As New AggregateCatalog()
catalog.Catalogs.Add(New AssemblyCatalog(GetType(Extensibility).Assembly))
'Create the CompositionContainer with the parts in the catalog
ChartContainer = New CompositionContainer(catalog)
Try
ChartContainer.ComposeParts(Me)
Catch ex As Exception
Console.WriteLine(ex.ToString)
End Try
End Sub
' must use Lazy otherwise instantiation of Window will hold open app. Otherwise must specify Shutdown Mode of "Shutdown on Main Window".
<ImportMany()> _
Public Property ChartDescriptions As IEnumerable(Of Lazy(Of ICharts, IDictionary(Of String, Object)))
End Class
【讨论】:
【参考方案5】:如果您想将按钮放在圆角矩形边框中,您应该查看msdn's example。我通过谷歌搜索问题的图像(而不是文本)发现了这一点。它们笨重的外部矩形(谢天谢地)很容易移除。
请注意,您必须重新定义按钮的行为(因为您已更改 ControlTemplate)。也就是说,您需要使用 ControlTemplate.Triggers 标记中的触发器标记 (Property="IsPressed" Value="true") 定义单击按钮时的行为。希望这可以节省其他人我失去的时间:)
【讨论】:
以上是关于如何创建 WPF 圆角容器?的主要内容,如果未能解决你的问题,请参考以下文章