我该如何在WPF中绘制带有直角边框?

4

你知道,就像战斗星的纸一样!我已经尝试过几次了,但现在我卡住了。我还没有走几何路线,所以我会尽我所能解释清楚。

我想要边框可以调整大小,但包含固定大小的角落,就像CornerRadius一样。我不想要圆角,而是想让它们变细,就像这样:

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

我已经尝试了两种方法:
  1. 我的第一次尝试是尝试操作边框类。但是,拉伸形状会破坏几何和比例,因此这种方法行不通。
  2. 第二次尝试更加创新。我创建了一个3x3的网格,并用4个边框填充它,每个边框的厚度分别为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"会将线条扩展到边缘。然而,它最终看起来像这样:http://img375.imageshack.us/img375/1996/border1.png。我在Grid元素周围画了一个品红色的边框,以使问题更加明显。
我应该如何扩展线条以真正填补间隙(例如通过膨胀网格内的可绘制区域),或者是否有更好的方法?
此外,变换会扭曲线条,使其变粗。我尝试过缩放,但没有一致性。线条的端点看起来也很糟糕(例如三角形)。
最后,这种方法仍然存在缺陷,因为我希望能够在未来设置角落大小,所以将行/列的边缘宽度设置为10似乎是一个障碍点。绑定到属性可能会解决这个问题,但我从未在样式中这样做过。
谢谢阅读,汤姆
2个回答

6

WPF边框继承自Decorator类。编写自己的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>

嗨,Wallstreet,感谢您的回复。这看起来非常全面。明天早上我回到编程模式时会尝试一下,并告诉您我的进展情况。我希望采用“全部xaml”路线,以便可以更换边框样式,但现在先不考虑这个,这可能就可以解决问题了! - Tom
将其实现为类的优点在于易于重用。如果您只想要一些显示相同边框类型的Xaml,则可以在Xaml中定义8行。将它们放置在一个具有一个单元格的网格中,并使用来自该网格的ActualHeight和ActualWidth。这就是我确定代码后面坐标的方法。生成的可视树将是相同的。 - Wallstreet Programmer
好的,我刚刚试了一下,这绝对是正确的方法。 在纯XAML中执行它比实际有用更麻烦,因为要做偏移量和实际高度等事情需要使用IValueConverter。 我略微修改了您的代码以包括CornerWidthOffset DP,并使用三角形笔尖来减少缝隙效果,如我的原始图像所示。(在我的当前示例中,0.299的效果非常好,填充了孔洞。可以像这样替换边框:http://pavanpodila.spaces.live.com/Blog/cns!9C9E888164859398!339.entry?sa=601481398 。看起来运行得很好。再次感谢!) - Tom
到目前为止,我所做的所有源代码都在这里http://www.codescratch.net/files/cornerborder.zip。希望其他人也能发现这个有用 :) - Tom

0
为了避免末尾出现不好的断点,你可以使用多边形或折线:
    <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"
        />

我选择的宽度是任意的...


谢谢Merlyn,我刚刚尝试了一下,但是无法使其正常运行。我明天会再试一次。该行完全超出了其边界,而方块部分看起来很奇怪,但还是感谢您的回复。 - Tom

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接