如何创建 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>

您可以将&lt;Grid/&gt; 替换为任何布局容器...

【讨论】:

对于任何厚度对象(BorderThickness 或 CornerRadius),如果所有 4 个都相同,您可以指定单个数字,例如 CornerRadius="8"。 &lt;Border BorderBrush="Black" BorderThickness="1" CornerRadius="8"&gt; 是一个合适的替代品,更简洁一点 @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 圆角容器?的主要内容,如果未能解决你的问题,请参考以下文章

wpf窗体设置阴影后,窗体圆角就会有阴影颜色,圆角就失效了,如何解决啊?求解

如何在 WPF 中创建/制作圆角按钮?

WPF 创建无边框的圆角窗口

创建带圆角的 ImageView [重复]

仅底部带有圆角的 WPF 弹出窗口

wpf 设置图片圆角