Подтвердить что ты не робот

Как сделать содержимое округлой границы также круглым?

У меня есть элемент границы с закругленными углами, содержащими сетку 3x3. Уголки сетки торчат из границы. Как я могу это исправить? Я пробовал использовать ClipToBounds, но ничего не получал. Спасибо за вашу помощь.

4b9b3361

Ответ 1

Вот основные моменты этой темы, упомянутой Jobi

  • Ни один из декораторов (например, границы) или панелей компоновки (например, Stackpanel) не приходит с этим поведением из коробки.
  • ClipToBounds предназначен для макета. ClipToBounds не препятствует рисованию элемента за пределами его границ; это просто мешает детским макетам "проливать". Кроме того, ClipToBounds = True не требуется для большинства элементов, потому что их реализация не позволяет их макет контента разливать в любом случае. Наиболее заметным исключением является Canvas.
  • Наконец, Border рассматривает закругленные углы как чертежи внутри границ его макета.

Вот реализация класса, который наследует от Border и реализует правильную функциональность:

     /// <Remarks>
    ///     As a side effect ClippingBorder will surpress any databinding or animation of 
    ///         its childs UIElement.Clip property until the child is removed from ClippingBorder
    /// </Remarks>
    public class ClippingBorder : Border {
        protected override void OnRender(DrawingContext dc) {
            OnApplyChildClip();            
            base.OnRender(dc);
        }

        public override UIElement Child 
        {
            get
            {
                return base.Child;
            }
            set
            {
                if (this.Child != value)
                {
                    if(this.Child != null)
                    {
                        // Restore original clipping
                        this.Child.SetValue(UIElement.ClipProperty, _oldClip);
                    }

                    if(value != null)
                    {
                        _oldClip = value.ReadLocalValue(UIElement.ClipProperty);
                    }
                    else 
                    {
                        // If we dont set it to null we could leak a Geometry object
                        _oldClip = null;
                    }

                    base.Child = value;
                }
            }
        }

        protected virtual void OnApplyChildClip()
        {
            UIElement child = this.Child;
            if(child != null)
            {
                _clipRect.RadiusX = _clipRect.RadiusY = Math.Max(0.0, this.CornerRadius.TopLeft - (this.BorderThickness.Left * 0.5));
                _clipRect.Rect = new Rect(Child.RenderSize);
                child.Clip = _clipRect;
            }
        }

        private RectangleGeometry _clipRect = new RectangleGeometry();
        private object _oldClip;
    }

Ответ 2

Чистый XAML:

<Border CornerRadius="30" Background="Green">
    <Border.OpacityMask>
        <VisualBrush>
            <VisualBrush.Visual>
                <Border 
                    Background="Black"
                    SnapsToDevicePixels="True"
                    CornerRadius="{Binding CornerRadius, RelativeSource={RelativeSource AncestorType=Border}}"
                    Width="{Binding ActualWidth, RelativeSource={RelativeSource AncestorType=Border}}"
                    Height="{Binding ActualHeight, RelativeSource={RelativeSource AncestorType=Border}}"
                    />
            </VisualBrush.Visual>
        </VisualBrush>
    </Border.OpacityMask>
    <TextBlock Text="asdas das d asd a sd a sda" />
</Border>

Update: Нашел лучший способ добиться того же результата. Вы также можете заменить Border на любой другой элемент.

<Grid>
    <Grid.OpacityMask>
        <VisualBrush Visual="{Binding ElementName=Border1}" />
    </Grid.OpacityMask>
    <Border x:Name="Border1" CornerRadius="30" Background="Green" />
    <TextBlock Text="asdas das d asd a sd a sda" />
</Grid>

Пример

Ответ 3

Итак, я просто наткнулся на это решение, а затем зашел в ссылку форума msdn, которую Jobi предоставил и потратил 20 минут на мой собственный элемент ClippingBorder.

Тогда я понял, что тип свойства CornerRadius не является двойным, а System.Windows.CornerRaduis, который принимает 4 удвоения, по одному для каждого угла.

Итак, я собираюсь перечислить еще одно альтернативное решение, которое, скорее всего, удовлетворит требования большинства людей, которые наткнутся на этот пост в будущем...

Скажем, у вас есть XAML, который выглядит так:

<Border CornerRadius="10">
    <Grid>
        ... your UI ...
    </Grid>
</Border>

И проблема в том, что фон для элемента Grid пропускает и просматривает закругленные углы. Убедитесь, что ваш <Grid> имеет прозрачный фон вместо того, чтобы назначать одну и ту же кисть для свойства "Background" элемента <Border>. Больше нет кровотечений за углами и нет необходимости в целую кучу кода CustomControl.

Правда, теоретически, клиентская область по-прежнему имеет потенциал прохода за край угла, но вы контролируете этот контент, чтобы вы, как разработчик, могли либо иметь достаточное дополнение, либо убедиться, что форма элемента управления рядом с краем уместно (в моем случае, мои кнопки круглые, поэтому очень удобно в углу без проблем).

Ответ 4

Как упоминал Мика ClipToBounds не будет работать с Border.ConerRadius.

Существует UIElement.Clip свойство, которое Border наследования.

Если вы знаете точный размер границы, то вот решение:

<Border Background="Blue" CornerRadius="3" Height="100" Width="100">
      <Border.Clip>
        <RectangleGeometry RadiusX="3" RadiusY="3" Rect="0,0,100,100"/>
      </Border.Clip>
      <Grid Background="Green"/>
</Border>

Если размер неизвестен или динамичен, тогда можно использовать Converter для Border.Clip. См. Решение здесь.

Ответ 5

Используя решение @Andrew Mikhailov, вы можете определить простой класс, который делает ненужным определение VisualBrush для каждого затронутого элемента вручную:

public class ClippedBorder : Border
{
    public ClippedBorder() : base()
    {
        var e = new Border()
        {
            Background = Brushes.Black,
            SnapsToDevicePixels = true,
        };
        e.SetBinding(Border.CornerRadiusProperty, new Binding()
        {
            Mode = BindingMode.OneWay,
            Path = new PropertyPath("CornerRadius"),
            Source = this
        });
        e.SetBinding(Border.HeightProperty, new Binding()
        {
            Mode = BindingMode.OneWay,
            Path = new PropertyPath("ActualHeight"),
            Source = this
        });
        e.SetBinding(Border.WidthProperty, new Binding()
        {
            Mode = BindingMode.OneWay,
            Path = new PropertyPath("ActualWidth"),
            Source = this
        });

        OpacityMask = new VisualBrush(e);
    }
}

Чтобы проверить это, просто скомпилируйте следующие два примера:

<!-- You should see a blue rectangle with rounded corners/no red! -->
<Controls:ClippedBorder
    Background="Red"
    CornerRadius="10"
    Height="425"
    HorizontalAlignment="Center"
    VerticalAlignment="Center"
    Width="425">
    <Border Background="Blue">
    </Border>
</Controls:ClippedBorder>

<!-- You should see a blue rectangle with NO rounded corners/still no red! -->
<Border
    Background="Red"
    CornerRadius="10"
    Height="425"
    HorizontalAlignment="Center"
    VerticalAlignment="Center"
    Width="425">
    <Border Background="Blue">
    </Border>
</Border>

Ответ 6

Сделать сетку меньшей или увеличить границу. Чтобы элемент границы полностью содержал сетку.

Альтернативно, посмотрите, можно ли сделать фон сетки прозрачным, чтобы "торчащий" не был заметен.

Обновление: Ой, не заметил, что это вопрос WPF. Я не знаком с этим. Это был общий совет HTML/CSS. Может быть, это помогает...