Bagaimana cara membuat wadah WPF Rounded Corner?

114

Kami membuat aplikasi XBAP yang kami butuhkan untuk memiliki sudut membulat di berbagai lokasi dalam satu halaman dan kami ingin memiliki wadah WPF Rounded Corner untuk menempatkan banyak elemen lain di dalamnya. Apakah ada yang punya beberapa saran atau contoh kode tentang bagaimana kita bisa mencapai hal ini? Apakah dengan gaya di a atau dengan membuat kontrol kustom?

FarrEver
sumber
1
Peringatan: jika Anda meletakkan satu baris teks di dalam batas persegi panjang, orang tua seperti saya akan melihatnya dan berpikir, "tombol tekan Macintosh tahun 80-an!"
mjfgates
Anda tidak tahu betapa saya merindukan Macintosh tahun 80-an! Saya pikir pertanyaan ini harus secara eksplisit menyatakan apakah kliping sudut diinginkan atau tidak karena jawaban yang dipilih tidak memotong perbatasan.
ATL_DEV

Jawaban:

266

Anda tidak memerlukan kontrol khusus, cukup letakkan penampung Anda di elemen perbatasan:

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

Anda dapat mengganti <Grid/>dengan salah satu wadah tata letak ...

kobusb
sumber
30
Untuk objek ketebalan apa pun (BorderThickness atau CornerRadius) Anda dapat menentukan satu nomor jika keempatnya sama, seperti CornerRadius = "8".
Santiago Palladino
3
<Border BorderBrush="Black" BorderThickness="1" CornerRadius="8">adalah pengganti yang cocok untuk ini, sedikit lebih baik
Kieren Johnstone
@Patrik Deoghare, jangan salah, kobusb luar biasa ... tapi saya pikir tim WPF sangat luar biasa karena membangun ini dan membuatnya sangat mudah untuk dimanfaatkan. (Meskipun orang dapat berargumen bahwa platform UI modern yang tidak memiliki ini ... sebenarnya bukanlah platform UI modern.)
cplotts
1
Ngomong-ngomong, penerapan Border sangat mencerahkan ... jika Anda ingin menggali di balik selimut. Misalnya, cara menggunakan StreamGeometry ...
cplotts
8
Oke, saya membuatnya berfungsi dengan meningkatkan ketebalan bingkai, tetapi solusi ini tidak memotong sudut anak-anak di dalam wadah. Ini hanya mencegah sudut tumpang tindih dengan radius perbatasan dengan menahan tinggi dan lebar anak-anak. Solusi Chris Cavanagh menangani kasus ini. Sayangnya, saya berharap solusi ini berhasil karena tampaknya lebih efisien dan elegan.
ATL_DEV
54

Saya tahu bahwa ini bukan jawaban untuk pertanyaan awal ... tetapi Anda sering ingin memotong konten bagian dalam dari batas sudut melengkung yang baru saja Anda buat.

Chris Cavanagh telah menemukan cara terbaik untuk melakukan hal ini.

Saya telah mencoba beberapa pendekatan berbeda untuk ini ... dan saya pikir yang satu ini menarik.

Ini xaml di bawah ini:

<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>
cplotts
sumber
1
Kontrol Blacklight ( blacklight.codeplex.com ) juga memiliki kontrol kecil yang bagus yang disebut ClippingBorder yang juga memungkinkan Anda untuk memotong konten ke sudut bulat Anda. Satu hal yang menyenangkan tentang ClippingBorder adalah ia tidak menggunakan VisualBrush (yang merupakan salah satu kuas dengan biaya tertinggi (dalam hal kinerja)).
cplotts
1
Namun, saya baru saja melihat ke dalam implementasi ClippingBorder ... dan menggunakan 4 ContentControl (s) dalam ControlTemplate defaultnya (satu untuk masing-masing sudut) ... jadi saya tidak yakin apakah itu lebih atau kurang lebih baik daripada pendekatan VisualBrush di atas. Saya akan berspekulasi mungkin kurang berkinerja.
cplotts
Ini harus menjadi jawaban yang dipilih jika pemotongan diperlukan.
ATL_DEV
1
Inilah satu-satunya cara nyata untuk melakukan ini! Sungguh menakjubkan ini bukan perilaku default untuk elemen di dalam perbatasan.
eran otzap
@eranotzap Senang Anda menyukai jawaban ini. Satu-satunya downside adalah bahwa Anda menggunakan VisualBrush yang merupakan barang berkinerja lebih berat. Jawaban saya yang lain di bawah ini menunjukkan kepada Anda bagaimana menghindari VisualBrush ini ...
cplotts
14

Saya hanya harus melakukan ini sendiri, jadi saya pikir saya akan memposting jawaban lain di sini.

Berikut adalah cara lain untuk membuat batas sudut membulat dan memotong konten dalamnya . Ini adalah cara mudah dengan menggunakan properti Clip. Sangat menyenangkan jika Anda ingin menghindari 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>

Kode untuk konverter:

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();
    }
}
cplotts
sumber
Solusi yang sangat keren. Saya membuat template kontrol untuk tombol yang membutuhkan cahaya luar dan cahaya dalam di berbagai status, dan ini membantu menyelesaikan masalah.
Quanta
2

VB.Net implementasi berbasis kode dari solusi kontrol Perbatasan kobusb. Saya menggunakannya untuk mengisi ListBox of Button kontrol. Kontrol Tombol dibuat dari ekstensi MEF. Setiap ekstensi menggunakan atribut ExportMetaData MEF untuk Deskripsi ekstensi. Ekstensi adalah objek bagan VisiFire. Pengguna menekan sebuah tombol, yang dipilih dari daftar tombol, untuk menjalankan grafik yang diinginkan.

        ' 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
BSalita
sumber
1

Jika Anda mencoba untuk meletakkan tombol di perbatasan persegi panjang bulat, Anda harus melihat contoh msdn . Saya menemukan ini dengan mencari gambar masalah di Google (bukan teks). Persegi panjang luarnya yang besar (untungnya) mudah dilepas.

Perhatikan bahwa Anda harus mendefinisikan ulang perilaku tombol (karena Anda telah mengubah ControlTemplate). Artinya, Anda perlu menentukan perilaku tombol saat diklik menggunakan tag Pemicu (Property = "IsPressed" Value = "true") di tag ControlTemplate.Triggers. Semoga ini menyelamatkan orang lain waktu saya tersesat :)

Daniel
sumber