Nasıl WPF Yuvarlak Köşe kabı oluşturabilirim?


114

Tek bir sayfada çeşitli konumlarda yuvarlatılmış köşelere sahip olmamız gereken bir XBAP uygulaması oluşturuyoruz ve içine bir grup diğer öğeyi yerleştirmek için bir WPF Rounded Corner konteynırına sahip olmak istiyoruz. Bunu en iyi şekilde nasıl başarabileceğimize dair bir önerisi veya örnek kodu olan var mı? Ya stillerle mi yoksa özel bir kontrol oluşturarak mı?


1
Uyarı: Yuvarlatılmış dikdörtgen bir kenarlığın içine tek bir metin satırı koyarsanız, benim gibi yaşlı insanlar ona bakacak ve "Macintosh'un 80'lerin düğmesi!"
mjfgates

80'lerin Macintosh'unu ne kadar özlediğimi bilemezsin! Bence bu soru, köşelerin kırpılmasının istenip istenmediğini açıkça belirtmelidir çünkü seçilen cevap kenarlığı kırpmaz.
ATL_DEV

Yanıtlar:


266

Özel bir kontrole ihtiyacınız yok, sadece kabınızı bir sınır elemanına koyun:

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

<Grid/>Düzen kaplarından herhangi biriyle değiştirebilirsiniz ...


30
Herhangi bir kalınlık nesnesi için (BorderThickness veya CornerRadius), CornerRadius = "8" gibi 4'ü de aynıysa tek bir sayı belirtebilirsiniz.
Santiago Palladino

3
<Border BorderBrush="Black" BorderThickness="1" CornerRadius="8">bunun için uygun bir alternatif, biraz daha başarılı
Kieren Johnstone

@Patrik Deoghare, beni yanlış yapma, kobusb harika ... ama WPF ekibinin bunu inşa etmek ve bundan yararlanmayı çok kolaylaştırmak için harika olduğunu düşünüyorum. (Buna sahip olmayan modern bir UI platformunun ... gerçekten modern bir UI platformu olmadığı iddia edilebilir.)
cplotts

1
Bu arada, eğer kapakların altını kazmak isterseniz, Border uygulaması son derece aydınlatıcıdır. Örneğin, StreamGeometry'yi nasıl kullanıyor ...
cplotts

8
Tamam, bordür kalınlığını artırarak çalışmasını sağladım ama bu çözüm konteynerin içindeki çocukların köşelerini kırmıyor. Sadece çocukların boylarını ve genişliklerini kısıtlayarak köşelerin kenar yarıçapı ile örtüşmesini önler. Chris Cavanagh'ın çözümü bu davayı ele alıyor. Ne yazık ki, bu çözümün daha verimli ve şık göründüğü için işe yaramasını umuyordum.
ATL_DEV

54

Bunun ilk sorunun cevabı olmadığını biliyorum ... ama genellikle yeni oluşturduğunuz yuvarlak köşe kenarlığının iç içeriğini kırpmak istersiniz.

Chris Cavanagh tam da bunu yapmanın mükemmel bir yolunu buldu .

Buna birkaç farklı yaklaşım denedim ... ve bence bu harika.

İşte aşağıdaki 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 Kontrolleri ( blacklight.codeplex.com ) ayrıca, içeriği yuvarlatılmış köşelerinize kırpmanıza izin veren ClippingBorder adlı şık ve küçük bir kontrole sahiptir. ClippingBorder ile ilgili güzel bir şey, bir VisualBrush (performans açısından en yüksek maliyetli fırçalardan biridir) kullanmamasıdır.
cplotts

1
Bununla birlikte, ClippingBorder uygulamasının içine bir göz attım ... ve varsayılan ControlTemplate'inde 4 ContentControl (s) kullanıyor (köşelerin her biri için bir tane) ... bu yüzden az ya da çok olup olmadığından emin değilim Yukarıdaki VisualBrush yaklaşımından daha yüksek performanslı. Belki daha az performans gösteren spekülasyon yapardım.
cplotts

Kırpma gerekliyse seçilen cevap bu olmalıdır.
ATL_DEV

1
Bunu yapmanın tek gerçek yolu bu! Bunun bir sınır içindeki öğeler için varsayılan davranış olmaması şaşırtıcı.
eran otzap

@eranotzap Bu yanıtı beğendiğinize sevindim. Tek dezavantajı, daha yüksek performanslı bir ürün olan bir VisualBrush kullanıyor olmanızdır. Aşağıdaki diğer cevabım bu VisualBrush'tan nasıl kaçınılacağını gösteriyor ...
cplotts

14

Bunu kendim yapmak zorundaydım, bu yüzden buraya başka bir cevap göndereceğimi düşündüm.

İşte yuvarlatılmış köşe kenarlığı oluşturmanın ve iç içeriğini kırpmanın başka bir yolu . Bu, Clip özelliğini kullanmanın basit yoludur. Bir VisualBrush'tan kaçınmak istiyorsanız güzel.

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>

Dönüştürücünün kodu:

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

Çok güzel çözüm. Farklı durumlarda hem dış ışıma hem de iç ışıma ihtiyacı olan bir düğme için bir kontrol şablonu oluşturuyorum ve bu, sorunun çözülmesine yardımcı oldu.
Quanta

2

Kobusb'un Sınır kontrol çözümünün VB.Net kod tabanlı uygulaması. ListBox of Button kontrollerini doldurmak için kullandım. Düğme kontrolleri MEF uzantılarından oluşturulur. Her uzantı, uzantının bir Açıklaması için MEF'in ExportMetaData özniteliğini kullanır. Uzantılar, VisiFire grafik nesneleridir. Kullanıcı, istenen grafiği yürütmek için düğmeler listesinden seçilen bir düğmeye basar.

        ' 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

Yuvarlatılmış dikdörtgen bir kenarlığın içine bir düğme koymaya çalışıyorsanız, msdn'nin örneğine bakmalısınız . Bunu, sorunun görüntülerini (metin yerine) araştırarak buldum. Hacimli dış dikdörtgenlerinin çıkarılması (neyse ki) kolaydır.

Düğmenin davranışını yeniden tanımlamanız gerekeceğini unutmayın (Kontrol Şablonunu değiştirdiğiniz için). Yani, ControlTemplate.Triggers etiketinde bir Tetikleyici etiketi (Property = "IsPressed" Value = "true") kullanarak tıklandığında düğmenin davranışını tanımlamanız gerekir. Umarım bu, kaybettiğim zamandan başkasını kurtarır :)

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.