web-dev-qa-db-fra.com

Comment créer un conteneur WPF Rounded Corner?

Nous sommes en train de créer une application XBAP dont les coins arrondis doivent être arrondis à différents endroits sur une seule page et nous aimerions disposer d’un conteneur WPF Rounded Corner pour y placer un tas d’autres éléments. Quelqu'un at-il des suggestions ou un exemple de code sur la meilleure façon d’y parvenir? Avec des styles sur ou avec la création d'un contrôle personnalisé?

104
FarrEver

Vous n'avez pas besoin d'un contrôle personnalisé, il suffit de placer votre conteneur dans un élément border:

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

Vous pouvez remplacer le <Grid/> avec l'un des conteneurs de mise en page ...

243
kobusb

Je sais que ce n'est pas une réponse à la question initiale ... mais vous voulez souvent couper le contenu intérieur de la bordure d'angle arrondi que vous venez de créer.

Chris Cavanagh a mis au point un excellente façon de faire exactement cela.

J'ai essayé plusieurs approches différentes à cet égard ... et je pense que celle-ci est géniale.

Voici le xaml ci-dessous:

<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>
50
cplotts

Je devais le faire moi-même, alors j'ai pensé poster une autre réponse ici.

Voici un autre moyen de créer une bordure d’angle arrondie et de découper son contenu interne. C'est la méthode la plus simple en utilisant la propriété Clip. C'est bien si vous voulez éviter un VisualBrush.

Le 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>

Le code pour le convertisseur:

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

Implémentation basée sur le code VB.Net de la solution de contrôle des frontières de kobusb. Je l'ai utilisé pour remplir un ListBox de contrôles Button. Les contrôles Button sont créés à partir d'extensions MEF. Chaque extension utilise l'attribut ExportMetaData de MEF pour une description de l'extension. Les extensions sont des objets de cartographie VisiFire. L'utilisateur appuie sur un bouton, sélectionné dans la liste des boutons, pour exécuter le graphique souhaité.

        ' 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
2
BSalita

Si vous essayez de placer un bouton dans une bordure rectangulaire arrondie, vous devriez vérifier exemple de msdn . J'ai trouvé cela en recherchant des images du problème sur Google (au lieu de texte). Leur rectangle extérieur volumineux est (heureusement) facile à enlever.

Notez que vous devrez redéfinir le comportement du bouton (depuis que vous avez modifié le ControlTemplate). En d'autres termes, vous devez définir le comportement du bouton lorsque vous cliquez dessus à l'aide d'une balise Trigger (Property = "IsPressed" Value = "true") dans la balise ControlTemplate.Triggers. J'espère que cela épargnera à quelqu'un d'autre le temps que j'ai perdu :)

1
Daniel