如何创建WPF圆角容器?


114

我们正在创建一个XBAP应用程序,我们需要在单个页面中的各个位置具有圆角,并且我们希望拥有一个WPF圆角容器以在其中放置许多其他元素。是否有人对我们如何最好地做到这一点有任何建议或示例代码?使用上的样式还是创建自定义控件?


1
注意事项:如果将一行文本放在圆角矩形边框内,像我这样的老年人会看着它,然后想到:“ Macintosh 80年代按钮!”
mjfgates

您不知道我多么想念80年代的Macintosh!我认为这个问题应该明确说明是否需要剪裁角,因为所选答案不会剪裁边界。
ATL_DEV

Answers:


266

您不需要自定义控件,只需将容器放在border元素中:

<Border BorderBrush="#FF000000" BorderThickness="1" CornerRadius="8">
   <Grid/>
</Border>

您可以将其替换为<Grid/>任何布局容器...


30
对于任何厚度对象(BorderThickness或CornerRadius),如果所有4个相同,则可以指定一个数字,例如CornerRadius =“ 8”。
圣地亚哥帕拉迪诺

3
<Border BorderBrush="Black" BorderThickness="1" CornerRadius="8">是对此的合适替代,更简洁
Kieren Johnstone

@Patrik Deoghare,请问我没错,kobusb很棒……但是我认为WPF团队在构建它并使其易于利用方面实在太棒了。(尽管有人可能会争辩说,没有内置的现代UI平台……实际上不是现代UI平台。)
cplotts 2012年

1
顺便说一句,Border的实现非常有启发性……如果您想在幕后挖洞。例如,它如何使用StreamGeometry ...
cplotts 2012年

8
好的,我可以通过增加边框的厚度来使它起作用,但是这种解决方案不会夹住容器内孩子的角。它仅通过限制子项的高度和宽度来防止拐角与边界半径重叠。克里斯·卡瓦纳(Chris Cavanagh)的解决方案可以解决这个问题。可悲的是,我希望该解决方案能够工作,因为它看起来更高效,更优雅。
ATL_DEV

54

我知道这不是最初问题的答案...但是您经常想裁剪刚创建的圆角边框的内部内容。

克里斯·卡瓦纳(Chris Cavanagh)提出了一种出色的方法来做到这一点。

我已经尝试了几种不同的方法来解决这个问题……而且我认为这很困难。

这是下面的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>

1
Blacklight控件(blacklight.codeplex.com)还具有一个名为ClippingBorder的漂亮小控件,该控件还允许您将内容剪切到圆角。ClippingBorder的一个优点是它不使用VisualBrush(这是成本最高(在性能方面)的画笔之一)。
cplotts 2010年

1
但是,我只是在内部查看了ClippingBorder的实现...,它在其默认ControlTemplate中使用了4个ContentControl(每个角各一个)...所以我不确定它是多还是少性能优于上面的VisualBrush方法。我会推测可能表现不佳。
cplotts

如果需要裁剪,这应该是选择的答案。
ATL_DEV

1
这是唯一的真正方法!令人惊讶的是,这不是border内元素的默认行为。
eran otzap

@eranotzap很高兴您喜欢这个答案。唯一的缺点是您正在使用VisualBrush,这是性能更重的项。我下面的其他答案显示了如何避免使用VisualBrush ...
cplotts

14

我只需要自己做,所以我想在这里再发表一个答案。

这是创建圆角边框并裁剪其内部内容的另一种方法。这是使用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();
    }
}

很酷的解决方案。我正在为一个需要在不同状态下同时需要外部辉光和内部辉光的按钮创建控件模板,这有助于解决该问题。
2012年

2

基于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

1

如果您尝试将按钮置于圆角矩形边框中,则应查看msdn的example。我通过搜索问题的图像(而不是文本)发现了这一点。笨重的外部矩形很容易(很容易)删除。

注意,您将不得不重新定义按钮的行为(因为您已经更改了ControlTemplate)。也就是说,您需要使用ControlTemplate.Triggers标记中的Trigger标记(Property =“ IsPressed” Value =“ true”)定义单击按钮时的行为。希望这可以节省我失去的时间:)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.