Как я могу нарисовать границу с прямоугольными углами в wpf? - PullRequest
2 голосов
/ 09 сентября 2010

Вы знаете, как бумага Battlestar! Я дал это несколько раз, но теперь я в тупике. Я еще не пошел по маршруту геометрии, поэтому объясню это как можно лучше.

Я бы хотел, чтобы граница была значительной, но содержала углы фиксированного размера, как это делает CornerRadius. Вместо закругленных углов я бы хотел, чтобы они были сужены, например:

/---------\
|         |
|         |
\_________/

Я сделал две попытки:

  1. Моя первая попытка попытаться манипулировать пограничным классом. Это просто не работает, так как растяжение формы разрушает геометрию и масштаб.
  2. Вторая попытка была немного более нестандартной. В прямом смысле. Я создал сетку 3х3 и заполнил ее четырьмя границами, каждая толщиной 2,0,0,0 - 0,2,0,0 - 0,0,2,0 и 0,0,0,2 соответственно. Последний шаг - это соединение границ с линией. Вот где лежит мой вопрос ....

Первая попытка

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
   <Grid>
      <Grid.Resources>
         <Style x:Key="MyPoly" TargetType="Polygon">
            <Setter Property="Points">
               <Setter.Value>
                  <PointCollection>
                     <Point X="0.10" Y="0.01"/>
                     <Point X="0.50" Y="0.01"/>
                     <Point X="0.60" Y="0.10"/>
                     <Point X="0.60" Y="0.50"/>
                     <Point X="0.50" Y="0.60"/>
                     <Point X="0.10" Y="0.60"/>
                     <Point X="0.01" Y="0.50"/>
                     <Point X="0.01" Y="0.10"/>
                  </PointCollection>
               </Setter.Value>
            </Setter>
         </Style>
      </Grid.Resources>
      <Border
         Width="100"
         Height="100"
         BorderBrush="Black"
         BorderThickness="3"
         CornerRadius="5"/>
      <Grid Width="400"
            Height="300">
         <Polygon
            Stroke="Purple"
            StrokeThickness="2"
            Style="{StaticResource MyPoly}" Stretch="Fill">
            <Polygon.Fill>
               <SolidColorBrush Color="Blue" Opacity="0.4"/>
            </Polygon.Fill>
            <Polygon.LayoutTransform>
               <ScaleTransform ScaleX="1" ScaleY="1"/>
            </Polygon.LayoutTransform>
         </Polygon>
      </Grid>
   </Grid>
</Page>

Вторая попытка

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" SnapsToDevicePixels="True">
    <Grid>
        <Grid.Resources>
        </Grid.Resources>
        <Grid Width="200" Height="350" SnapsToDevicePixels="True">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="10"/>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="10"/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="10"/>
                <RowDefinition Height="*"/>
                <RowDefinition Height="10"/>
            </Grid.RowDefinitions>
            <Border Grid.Column="0" Grid.Row="1" Margin="0" BorderBrush="Red" BorderThickness="2,0,0,0" Padding="0" SnapsToDevicePixels="True"/>
            <Border BorderThickness="1" BorderBrush="Black">
                <Line SnapsToDevicePixels="True" Stretch="Fill" Stroke="Red" StrokeThickness="2" X1="0" X2="1" Y1="1" Y2="0">
                </Line>
            </Border>
            <Border Grid.Column="1" Grid.Row="0" BorderBrush="Red" BorderThickness="0,2,0,0" SnapsToDevicePixels="True"/>
            <Border Grid.Column="2" Grid.Row="1" BorderBrush="Red" BorderThickness="0,0,2,0" SnapsToDevicePixels="True"/>
            <Border Grid.Column="1" Grid.Row="2" BorderBrush="Red" BorderThickness="0,0,0,2" SnapsToDevicePixels="True"/>
        </Grid>
    </Grid>
</Page>

Линия настроена в соответствии с размером сетки. Если задать для свойств линии значение X1="0" X2="1" Y1="1" Y2="0", а с помощью Stretch="Fill" линия будет расширена до краев. Тем не менее, это выглядит так:

(Досадно, я не могу публиковать изображения, мне нужно идти, чтобы ответить на вопросы кого-то другого, чтобы заработать несколько повторений. Вместо этого перейдите по этой ссылке, чтобы увидеть строку, или вставьте вышеуказанный XAML в Kaxaml.) http://img375.imageshack.us/img375/1996/border1.png

Я нарисовал пурпурную границу вокруг элемента Grid, в котором находится Линия, чтобы сделать проблему более очевидной.

Как я могу расширить линию до на самом деле заполнить пробел (например, надувая область рисования в сетке), или, есть лучший способ?

Кроме того, преобразования искажают линию, делая ее толще. Я пытался увеличить, но не было последовательности в этом. Концевые заглушки на линии выглядят так же плохо (например, треугольник).

Наконец, этот метод все еще имеет недостатки, потому что я хочу иметь возможность устанавливать размер угла в будущем, поэтому ширина края для строки / столбца, равная 10, кажется камнем преткновения. Привязка к свойству может решить эту проблему, хотя я никогда не делал этого в стиле.

Спасибо за чтение, Том

Ответы [ 2 ]

4 голосов
/ 09 сентября 2010

Граница WPF наследуется от класса Decorator.Это довольно легко написать свой собственный декоратор.Ниже вы рисуете рамку вокруг ребенка с «заправленными» углами.

class FunkyBorder : Decorator
{
    public Brush BorderBrush
    {
        get { return (Brush)GetValue(BorderBrushProperty); }
        set { SetValue(BorderBrushProperty, value); }
    }

    public static readonly DependencyProperty BorderBrushProperty =
        DependencyProperty.Register("BorderBrush", 
                                    typeof(Brush), 
                                    typeof(FunkyBorder), 
                                    new UIPropertyMetadata(Brushes.Transparent));

    protected override void OnRender(DrawingContext drawingContext)
    {
        // TODO, make pen thickness and corner width (currently 10) into dependency properties.
        // Also, handle case when border don't fit into given space without overlapping.

        if (_pen.Brush != BorderBrush)
        {
            _pen.Brush = BorderBrush;
        }

        drawingContext.DrawLine(_pen, new Point(0, 10), new Point(10, 0));
        drawingContext.DrawLine(_pen, new Point(10, 0), new Point(ActualWidth - 10, 0));
        drawingContext.DrawLine(_pen, new Point(ActualWidth - 10, 0), new Point(ActualWidth, 10));
        drawingContext.DrawLine(_pen, new Point(0, 10), new Point(0, ActualHeight - 10));
        drawingContext.DrawLine(_pen, new Point(ActualWidth, 10), new Point(ActualWidth, ActualHeight - 10));
        drawingContext.DrawLine(_pen, new Point(0, ActualHeight - 10), new Point(10, ActualHeight));
        drawingContext.DrawLine(_pen, new Point(10, ActualHeight), new Point(ActualWidth - 10, ActualHeight));
        drawingContext.DrawLine(_pen, new Point(ActualWidth - 10, ActualHeight), new Point(ActualWidth, ActualHeight - 10));
    }

    private Pen _pen = new Pen(Brushes.Transparent, 2);
}

Используйте вот так:

   <BorderTest:FunkyBorder BorderBrush="Red">
        <TextBlock Text="Hello" />
    </BorderTest:FunkyBorder>
0 голосов
/ 10 сентября 2010

Чтобы избежать неприятных разрывов в конце, вы можете использовать Polygon или PolyLine:

    <Polygon
        Stroke="Red"
        StrokeThickness="2"
        Points="
            0,1 1,0
            1,0 20,0
            20,0 21,1
            21,1 21,20
            21,20 20,21
            20,21 1,21
            1,21 0,20
            0,1 1,0
        "
        Stretch="Fill"
        />

Ширина, которую я выбрал, произвольна ...

...