WPF 둥근 모서리 컨테이너는 어떻게 만듭니 까?


114

단일 페이지의 다양한 위치에 둥근 모서리가 있어야하는 XBAP 응용 프로그램을 만들고 있으며 WPF 둥근 모서리 컨테이너를 사용하여 다른 요소를 배치하려고합니다. 누구든지 우리가 이것을 가장 잘 수행 할 수있는 방법에 대한 제안이나 샘플 코드가 있습니까? 스타일을 사용하거나 사용자 지정 컨트롤을 만들었습니까?


1
주의 사항 : 둥근 사각형 테두리 안에 한 줄의 텍스트를 넣으면 저와 같은 노인들은 그것을보고 "Macintosh '80s push button!"이라고 생각할 것입니다.
mjfgates

내가 80 년대 매킨토시를 얼마나 그리워하는지 당신은 모를 겁니다! 이 질문은 선택된 답변이 테두리를 자르지 않기 때문에 모서리를 자르는 것이 바람직한 지 여부를 명시해야한다고 생각합니다.
ATL_DEV 2013

답변:


266

사용자 지정 컨트롤이 필요하지 않고 테두리 요소에 컨테이너를 배치하기 만하면됩니다.

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

<Grid/>레이아웃 컨테이너로를 대체 할 수 있습니다 .


30
모든 두께 개체 (BorderThickness 또는 CornerRadius)에 대해 CornerRadius = "8"과 같이 4 개가 모두 동일한 경우 단일 숫자를 지정할 수 있습니다.
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 2013

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의 구현 내부를 살펴 보았고 ... 기본 ControlTemplate에서 4 개의 ContentControl (s)을 사용합니다 (각 모서리에 하나씩) ... 그래서 어느 정도인지 확실하지 않습니다. 위의 VisualBrush 접근 방식보다 성능이 뛰어납니다. 성능이 떨어질 수 있다고 추측합니다.
cplotts

클리핑이 필요한 경우 선택해야합니다.
ATL_DEV 2013

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

kobusb의 Border 제어 솔루션의 VB.Net 코드 기반 구현. 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의 예제를 확인해야합니다 . (텍스트 대신) 문제의 이미지를 검색하여 이것을 발견했습니다. 그들의 부피가 큰 외부 직사각형은 (고맙게도) 제거하기 쉽습니다.

ControlTemplate을 변경 했으므로 단추의 동작을 다시 정의해야합니다. 즉, ControlTemplate.Triggers 태그에서 Trigger 태그 (Property = "IsPressed"Value = "true")를 사용하여 클릭 할 때의 버튼 동작을 정의해야합니다. 이것이 내가 잃어버린 시간을 다른 사람에게 구하기를 바랍니다. :)

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.