ฉันจะสร้างคอนเทนเนอร์ WPF Rounded Corner ได้อย่างไร


114

เรากำลังสร้างแอปพลิเคชัน XBAP ที่เราต้องมีมุมโค้งมนในตำแหน่งต่างๆในหน้าเดียวและเราต้องการมีคอนเทนเนอร์ WPF Rounded Corner เพื่อวางองค์ประกอบอื่น ๆ ไว้ภายใน ใครมีคำแนะนำหรือโค้ดตัวอย่างเกี่ยวกับวิธีที่เราจะทำสิ่งนี้ให้สำเร็จได้ดีที่สุด? ไม่ว่าจะด้วยสไตล์ในหรือด้วยการสร้างคอนโทรลแบบกำหนดเอง?


1
ข้อแม้: ถ้าคุณใส่ข้อความบรรทัดเดียวภายในเส้นขอบสี่เหลี่ยมผืนผ้าโค้งมนคนแก่อย่างฉันจะมองและคิดว่า "ปุ่มกด Macintosh ยุค 80!"
mjfgates

คุณไม่รู้หรอกว่าฉันพลาด Macintosh ยุค 80 แค่ไหน! ฉันคิดว่าคำถามนี้ควรระบุอย่างชัดเจนว่าต้องการตัดมุมหรือไม่เพราะคำตอบที่เลือกไม่ได้ตัดขอบ
ATL_DEV

คำตอบ:


266

คุณไม่จำเป็นต้องมีการควบคุมแบบกำหนดเองเพียงใส่คอนเทนเนอร์ของคุณในองค์ประกอบขอบ:

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

คุณสามารถแทนที่<Grid/>ด้วยคอนเทนเนอร์เค้าโครงใดก็ได้ ...


30
สำหรับวัตถุที่มีความหนา (BorderThickness หรือ CornerRadius) คุณสามารถระบุตัวเลขเดียวหากทั้ง 4 เหมือนกันเช่น CornerRadius = "8"
Santiago Palladino

3
<Border BorderBrush="Black" BorderThickness="1" CornerRadius="8">เป็นตัวทดแทนที่เหมาะสมสำหรับสิ่งนี้ช่วยให้เพียงพอมากขึ้น
Kieren Johnstone

@ Patrik Deoghare ฉันไม่ผิด kobusb ยอดเยี่ยมมาก ... แต่ฉันคิดว่าทีม WPF นั้นยอดเยี่ยมมากสำหรับการสร้างสิ่งนี้และทำให้ง่ายต่อการใช้ประโยชน์ (แม้ว่าใครจะโต้แย้งได้ว่าแพลตฟอร์ม UI สมัยใหม่ที่ไม่มีในตัว ... ไม่ใช่แพลตฟอร์ม UI ที่ทันสมัยจริงๆ)
cplotts

1
อย่างไรก็ตามการใช้งาน Border นั้นให้ความกระจ่างอย่างยิ่ง ... หากคุณรู้สึกอยากขุดใต้ฝาครอบ ตัวอย่างเช่นวิธีใช้ StreamGeometry ...
cplotts

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 Controls ( blacklight.codeplex.com ) ยังมีตัวควบคุมเล็ก ๆ น้อย ๆ ที่เรียกว่า ClippingBorder ซึ่งช่วยให้คุณสามารถตัดเนื้อหาไปยังมุมโค้งมนของคุณได้ สิ่งที่ดีอย่างหนึ่งเกี่ยวกับ ClippingBorder คือไม่ใช้แปรง VisualBrush (ซึ่งเป็นหนึ่งในแปรงที่มีต้นทุนสูงที่สุด (ในแง่ของประสิทธิภาพ)
cplotts

1
อย่างไรก็ตามฉันเพิ่งดูภายในการใช้งาน ClippingBorder ... และใช้ ContentControl 4 รายการใน ControlTemplate เริ่มต้น (หนึ่งอันสำหรับแต่ละมุม) ... ดังนั้นฉันไม่แน่ใจว่าจะมากหรือน้อย ประสิทธิภาพมากกว่าวิธี VisualBrush ด้านบน ฉันคิดว่าอาจจะมีประสิทธิภาพน้อยกว่า
cplotts

นี่ควรเป็นคำตอบที่เลือกหากจำเป็นต้องมีการตัด
ATL_DEV

1
นี่เป็นวิธีเดียวที่แท้จริงในการทำสิ่งนี้! มันน่าทึ่งมากนี่ไม่ใช่พฤติกรรมเริ่มต้นสำหรับองค์ประกอบภายในเส้นขอบ
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();
    }
}

ทางออกที่ยอดเยี่ยมมาก ฉันกำลังสร้างเทมเพลตการควบคุมสำหรับปุ่มที่ต้องการทั้งการเรืองแสงด้านนอกและการเรืองแสงด้านในในสถานะต่างๆซึ่งจะช่วยแก้ปัญหาได้
Quanta

2

การใช้รหัส VB.Net ตามโซลูชันการควบคุมชายแดนของ kobusb ฉันใช้มันเพื่อเติมข้อมูลในกล่องรายการของปุ่มควบคุม ปุ่มควบคุมถูกสร้างขึ้นจากส่วนขยาย MEF ส่วนขยายแต่ละรายการใช้แอตทริบิวต์ ExportMetaData ของ MEF สำหรับคำอธิบายของส่วนขยาย ส่วนขยายเป็นอ็อบเจ็กต์การสร้างแผนภูมิ 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ตัวอย่างของฉันพบสิ่งนี้โดย googling เพื่อหารูปภาพของปัญหา (แทนที่จะเป็นข้อความ) สี่เหลี่ยมผืนผ้าด้านนอกขนาดใหญ่ของพวกเขานั้นง่ายต่อการถอด (ขอบคุณ)

โปรดทราบว่าคุณจะต้องกำหนดลักษณะการทำงานของปุ่มใหม่ (เนื่องจากคุณได้เปลี่ยน ControlTemplate) นั่นคือคุณจะต้องกำหนดลักษณะการทำงานของปุ่มเมื่อคลิกโดยใช้แท็ก Trigger (Property = "IsPressed" Value = "true") ในแท็ก ControlTemplate.Triggers หวังว่านี่จะช่วยคนอื่นเวลาที่ฉันเสียไป :)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.