WPF Rounded Cornerコンテナーを作成するにはどうすればよいですか?


114

単一のページのさまざまな場所に角を丸くする必要があるXBAPアプリケーションを作成しています。WPFの丸みを帯びたコーナーコンテナーに他の要素の束を配置したいと考えています。私たちがこれを最もうまく達成する方法について誰かが提案やサンプルコードを持っていますか?のスタイルまたはカスタムコントロールの作成のどちらか?


1
警告:丸みを帯びた四角形の枠内に1行のテキストを入れると、私のような古い人たちはそれを見て、「Macintosh '80年代のプッシュボタン!」
mjfgates 2008

私が80年代のMacintoshをどれほどひどく寂しく思っているか、あなたにはわかりません!この質問では、選択した回答では境界線が切り取られないため、コーナーの切り取りが必要かどうかを明示的に述べるべきだと思います。
ATL_DEV 2013

回答:


266

カスタムコントロールは必要ありません。コンテナーを境界要素に配置するだけです。

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

<Grid/>を任意のレイアウトコンテナーに置き換えることができます...


30
任意の厚さオブジェクト(BorderThicknessまたはCornerRadius)では、CornerRadius = "8"のように、4つすべてが同じである場合、単一の数値を指定できます。
サンティアゴパラディーノ

3
<Border BorderBrush="Black" BorderThickness="1" CornerRadius="8">これに適した代替品であり、もう少し簡潔です
ジョンストーン

@Patrik Deoghare、誤解しないでください、kobusbは素晴らしいです...しかし、私はWPFチームがこれを組み込み、それを簡単に利用できるようにするのがすごいと思っていたと思います。(これが組み込まれていない最新のUIプラットフォームは、実際には最新のUIプラットフォームではないと主張することもできます。)
cplotts

1
ちなみに、境界線の実装は非常に啓発的です...カバーの下を掘り下げたくなった場合。たとえば、StreamGeometryの使用方法...
cplotts

8
OK、ボーダーの太さを増やすことで機能しましたが、このソリューションではコンテナー内の子のコーナーがクリップされません。子の高さと幅を制限することで、コーナーが境界線の半径と重ならないようにするだけです。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コントロール(blacklight.codeplex.com)にはClippingBorderと呼ばれる気の利いた小さなコントロールがあり、コンテンツを丸みを帯びた角にクリップすることもできます。ClippingBorderの良い点の1つは、VisualBrushを使用しないことです(これは(パフォーマンスの観点から)コストが最も高いブラシの1つです)。
cplotts

1
ただし、私はClippingBorderの実装の内部を調べただけで、デフォルトのControlTemplate(各コーナーに1つ)で4つのContentControlを使用しているので、多かれ少なかれわかりません。上記のVisualBrushアプローチよりも優れています。私はおそらくパフォーマンスが低いと推測します。
cplotts、2011

これは、クリッピングが必要な場合に選択される答えです。
ATL_DEV 2013

1
これが唯一の本当の方法です!これがボーダー内の要素のデフォルトの動作ではないのは驚くべきことです。
eran otzap 2015年

@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 2012年

2

VB.Netコードベースのkobusbのボーダーコントロールソリューションの実装。これを使用して、ButtonコントロールのListBoxを作成しました。ボタンコントロールは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.