Як створити контейнер 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".
Сантьяго Палладіно

3
<Border BorderBrush="Black" BorderThickness="1" CornerRadius="8">є підходящою заміною для цього, трохи більш вдалого
Кірен Джонстон

@Patrik Deoghare, не помиляйся, kobusb - це дивовижно ... але я думаю, що команда WPF була страшно приголомшливою для того, щоб створити це і зробити так легко скористатися. (Хоча можна стверджувати, що сучасна платформа користувальницького інтерфейсу, яка не має цього вбудованого ... насправді не є сучасною платформою інтерфейсу користувача.)
cplotts

1
До речі, реалізація Border надзвичайно освічує ... якщо ви відчуваєте, як копаєтесь під прикриттями. Наприклад, як використовується StreamGeometry ...
cplotts

8
Гаразд, я змусив його працювати, збільшуючи товщину рамки, але це рішення не затискає куточки дітей у контейнері. Це лише заважає куточкам перекривати радіус кордону, обмежуючи висоту та ширину дітей. Рішення Кріса Кавана займається цією справою. На жаль, я сподівався, що це рішення спрацює, оскільки воно здається більш ефективним та елегантним.
ATL_DEV

54

Я знаю, що це не відповідь на початкове запитання ... але ти часто хочеш відрізати внутрішній зміст тієї закругленої кутової межі, яку ти тільки що створив.

Кріс Кавана придумав чудовий спосіб зробити саме це.

Я спробував кілька різних підходів до цього ... і я думаю, що цей один гойдається.

Ось 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

1
Однак я лише зазирнув у внутрішню реалізацію ClippingBorder ... і він використовує 4 ContentControl (ів) у своєму замовчуванні 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();
    }
}

Дуже круте рішення. Я створюю шаблон управління для кнопки, яка потребує як зовнішнього світіння, так і внутрішнього світіння в різних станах, і це допомогло вирішити проблему.
Кванта

2

Впровадження VB.Net на базі кодової реалізації рішення про прикордонний контроль. Я використовував його для заповнення елементів 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). Тобто вам потрібно буде визначити поведінку кнопки при натисканні за допомогою тегу тригера (Властивість = "IsPress" Значення = "вірно") у тезі ControlTemplate.Triggers. Сподіваюсь, це заощадить когось іншого, який я втратив :)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.