Làm cách nào để tạo vùng chứa WPF Rounded Corner?


114

Chúng tôi đang tạo một ứng dụng XBAP mà chúng tôi cần có các góc tròn ở các vị trí khác nhau trong một trang duy nhất và chúng tôi muốn có một vùng chứa WPF Rounded Corner để đặt một loạt các phần tử khác bên trong. Có ai có một số gợi ý hoặc mã mẫu về cách chúng tôi có thể thực hiện điều này tốt nhất không? Với các kiểu trên a hoặc với việc tạo điều khiển tùy chỉnh?


1
Lưu ý: nếu bạn đặt một dòng văn bản bên trong đường viền hình chữ nhật bo tròn, những người già như tôi sẽ nhìn vào nó và nghĩ, "Nút nhấn của những năm 80 của Macintosh!"
mjfgates 23/09/08

Bạn không biết tôi nhớ Macintosh của thập niên 80 đến mức nào! Tôi nghĩ câu hỏi này nên nêu rõ ràng có muốn cắt các góc hay không vì câu trả lời đã chọn không cắt viền.
ATL_DEV

Câu trả lời:


266

Bạn không cần điều khiển tùy chỉnh, chỉ cần đặt vùng chứa của bạn trong phần tử đường viền:

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

Bạn có thể thay thế <Grid/>bằng bất kỳ vùng chứa bố cục nào ...


30
Đối với bất kỳ đối tượng độ dày nào (BorderThickness hoặc CornerRadius), bạn có thể chỉ định một số duy nhất nếu cả 4 đều giống nhau, chẳng hạn như CornerRadius = "8".
Santiago Palladino

3
<Border BorderBrush="Black" BorderThickness="1" CornerRadius="8">là một sự thay thế phù hợp cho điều này, một cắn succint hơn
Kieren Johnstone

@Patrik Deoghare, tôi không nhầm đâu, kobusb thật tuyệt vời ... nhưng tôi nghĩ nhóm WPF đã rất tuyệt vời khi xây dựng điều này và giúp nó dễ dàng tận dụng. (Mặc dù người ta có thể lập luận rằng một nền tảng giao diện người dùng hiện đại mà không có này được xây dựng trong ... thực sự không phải là một nền tảng giao diện người dùng hiện đại.)
cplotts

1
Nhân tiện, việc triển khai Border cực kỳ thú vị ... nếu bạn cảm thấy muốn đào sâu dưới vỏ bọc. Ví dụ: cách nó sử dụng StreamGeometry ...
cplotts

8
Được rồi, tôi đã làm cho nó hoạt động bằng cách tăng độ dày của đường viền, nhưng giải pháp này không cắt các góc của phần con trong vùng chứa. Nó chỉ ngăn các góc chồng lên bán kính đường viền bằng cách hạn chế chiều cao và chiều rộng của con cái. Giải pháp của Chris Cavanagh xử lý trường hợp này. Đáng buồn thay, tôi đã hy vọng giải pháp này hoạt động vì nó có vẻ hiệu quả và thanh lịch hơn.
ATL_DEV

54

Tôi biết rằng đây không phải là câu trả lời cho câu hỏi ban đầu ... nhưng bạn thường muốn cắt nội dung bên trong của đường viền góc tròn mà bạn vừa tạo.

Chris Cavanagh đã đưa ra một cách tuyệt vời để thực hiện điều này.

Tôi đã thử một vài cách tiếp cận khác nhau để giải quyết vấn đề này ... và tôi nghĩ cách này rất phù hợp.

Đây là xaml bên dưới:

<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 Controls ( blacklight.codeplex.com ) cũng có một điều khiển nhỏ tiện lợi được gọi là ClippingBorder cũng cho phép bạn cắt nội dung theo các góc tròn của mình. Một điều thú vị về ClippingBorder là nó không sử dụng VisualBrush (là một trong những loại cọ có giá thành cao nhất (về mặt hiệu suất)).
cplotts

1
Tuy nhiên, tôi chỉ mới xem xét bên trong quá trình triển khai ClippingBorder ... và nó sử dụng 4 ContentControl trong ControlTemplate mặc định của nó (một cho mỗi góc) ... vì vậy tôi không chắc liệu nó nhiều hay ít hiệu quả hơn cách tiếp cận VisualBrush ở trên. Tôi sẽ suy đoán có thể ít hiệu suất hơn.
cplotts

Đây sẽ là câu trả lời được chọn nếu cần cắt bớt.
ATL_DEV

1
Đây là cách thực sự duy nhất để làm điều này! Thật ngạc nhiên khi đây không phải là hành vi mặc định cho các phần tử bên trong đường viền.
eran otzap

@eranotzap Rất vui vì bạn thích câu trả lời này. Nhược điểm duy nhất là bạn đang sử dụng VisualBrush là một mục nặng về hiệu suất hơn. Câu trả lời khác của tôi dưới đây cho thấy bạn làm thế nào để tránh VisualBrush này ...
cplotts

14

Tôi chỉ phải làm điều này bản thân mình, vì vậy tôi nghĩ tôi sẽ đăng một câu trả lời khác ở đây.

Đây là một cách khác để tạo đường viền góc tròn và cắt nội dung bên trong của nó . Đây là cách đơn giản bằng cách sử dụng thuộc tính Clip. Thật tuyệt nếu bạn muốn tránh một 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>

Mã cho bộ chuyển đổi:

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();
    }
}

Giải pháp rất mát mẻ. Tôi đang tạo một mẫu điều khiển cho một nút cần cả ánh sáng bên ngoài và ánh sáng bên trong ở các trạng thái khác nhau và điều này đã giúp giải quyết vấn đề.
Quanta

2

Việc triển khai dựa trên mã VB.Net của giải pháp kiểm soát Biên giới của kobusb. Tôi đã sử dụng nó để điền vào một ListBox của các nút điều khiển. Các nút điều khiển được tạo từ các phần mở rộng MEF. Mỗi tiện ích mở rộng sử dụng thuộc tính ExportMetaData của MEF cho Mô tả của tiện ích mở rộng. Các phần mở rộng là các đối tượng biểu đồ VisiFire. Người dùng nhấn một nút, được chọn từ danh sách các nút, để thực hiện biểu đồ mong muốn.

        ' 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

Nếu bạn đang cố gắng đặt một nút trong đường viền hình chữ nhật tròn, bạn nên xem ví dụ của msdn . Tôi tìm thấy điều này bằng cách tìm kiếm hình ảnh của sự cố (thay vì văn bản) trên googling. Hình chữ nhật bên ngoài cồng kềnh của chúng (rất may) dễ dàng tháo ra.

Lưu ý rằng bạn sẽ phải xác định lại hành vi của nút (vì bạn đã thay đổi ControlTemplate). Nghĩa là, bạn sẽ cần xác định hành vi của nút khi được nhấp bằng thẻ Trigger (Thuộc tính = "IsPressed" Value = "true") trong thẻ ControlTemplate.Triggers. Hy vọng điều này sẽ tiết kiệm cho người khác khoảng thời gian tôi đã mất :)

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.