我如何在wpf中绘制一个带有方角的边框? [英] How can I draw a border with squared corners in wpf?
问题描述
我希望边框可以很大,但包含固定大小的角落,就像CornerRadius一样。而不是圆角,我希望它们渐变,例如:
/ --------- \
| |
| |
\ _________ /
我在这方面做了两次尝试:
- 我第一次尝试操作边框类。这是行不通的,因为拉伸形状会破坏几何图形和比例。 第二次尝试是多出了一些问题。从字面上看。我创建了一个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/presentationxmlns:x =http://schemas.microsoft.com/winfx/2006 / XAML>
<网格>
< Grid.Resources>
< Style x:Key =MyPolyTargetType =Polygon>
< Setter Property =Points>
< Setter.Value>
< PointCollection>
< Point X =0.10Y =0.01/>
< Point X =0.50Y =0.01/>
< Point X =0.60Y =0.10/>
< Point X =0.60Y =0.50/>
< Point X =0.50Y =0.60/>
< Point X =0.10Y =0.60/>
< Point X =0.01Y =0.50/>
< Point X =0.01Y =0.10/>
< / PointCollection>
< / Setter>
< / style>
< /Grid.Resources>
BorderBrush =Black
BorderThickness =3
CornerRadius =
$ BorderBrush =Black
Height =100 5\" />
< Grid Width =400
Height =300>
< Polygon
Stroke =Purple
StrokeThickness =2
Style ={StaticResource MyPoly}Stretch =Fill>
< Polygon.Fill>
< SolidColorBrush Color =BlueOpacity =0.4/>
< Polygon.LayoutTransform>
< ScaleTransform ScaleX =1ScaleY =1/>
< / Polygon>
< / Grid>
< / Grid>
< / Page>
第二次尝试
< Page xmlns =http://schemas.microsoft.com/winfx/2006/xaml/presentationxmlns:x =http://schemas.microsoft.com/winfx/2006/xaml SnapsToDevicePixels = 真 >
<网格>
< Grid.Resources>
< /Grid.Resources>
< Grid Width =200Height =350SnapsToDevicePixels =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 =0Grid.Row =1Margin =0BorderBrush =RedBorderThickness =2,0,0,0Padding =0SnapsToDevicePixels =真/>
< Border BorderThickness =1BorderBrush =Black>
< Line SnapsToDevicePixels =TrueStretch =FillStroke =RedStrokeThickness =2X1 =0X2 =1Y1 =1Y2 =0>
< / Line>
< / Border>
< Border Grid.Column =1Grid.Row =0BorderBrush =RedBorderThickness =0,2,0,0SnapsToDevicePixels =True/>
< Border Grid.Column =2Grid.Row =1BorderBrush =RedBorderThickness =0,0,2,0SnapsToDevicePixels =True/>
< Border Grid.Column =1Grid.Row =2BorderBrush =RedBorderThickness =0,0,0,2SnapsToDevicePixels =True/>
< / Grid>
< / Grid>
< / Page>
线设置为缩放到网格大小。将线条属性设置为 X1 =0X2 =1Y1 =1Y2 =0
并使用 Stretch =填充
将直线展开为边缘。然而,它看起来像这样:
(烦人的是,我不能发布图片,我需要去回答某人的问题以获得一些代表。去这个链接看这条线,或者将上面的XAML粘贴到Kaxaml中。)
http://img375.imageshack.us/img375/1996/border1.png
我在主持Line的Grid元素周围绘制了洋红色边框,使问题更加明显。
如何将线条扩大到真正填充空白处(例如,通过扩大可绘制区域或者,有没有更好的方法?
另外,转换会扭曲线条,使其更加粗糙。我试图扩大规模,但没有一致性。最后,这种方法仍然存在缺陷,因为我希望能够在角色中设置角尺寸。未来,所以将行/列的边缘宽度设置为10似乎是一个绊脚石。绑定到一个属性可能会解决这个问题,我从来没有在Style中做过。
感谢阅读,Tom
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));
保护覆盖无效OnRender(DrawingContext drawingContext)
{
// TODO,使笔厚度和角宽(当前为10)变为依赖项属性。
//另外,处理边界不适合给定空间而不重叠的情况。
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));
}
私人笔_pen =新笔(Brushes.Transparent,2);
}
使用像这样:
< BorderTest:FunkyBorder BorderBrush =Red>
< TextBlock Text =Hello/>
< / BorderTest:FunkyBorder>
You know, like Battlestar paper! I have given this a few goes but now I'm stumped. I haven't yet gone down the geometery route, so I'll explain this as best as I can.
I'd like the border to be sizable, but contain fixed-size corners, just like CornerRadius does. Instead of rounded corners, I'd like them to be tapered, like:
/---------\
| |
| |
\_________/
I've done two attempts at this:
- My first attempt attempts to manipulate a border class. This just doesn't work, as stretching the shape ruins the geometry and scale.
- The second attempt was a bit more out the box. Literally. I created a 3x3 grid and filled it with 4 borders, each with a thickness of 2,0,0,0 - 0,2,0,0 - 0,0,2,0 and 0,0,0,2 respectively. The final step, is the join the borders up with a Line. Here where my question lies....
First attempt
<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>
Second attempt
<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>
The Line is set to scale to the grid size. Setting the Line properties to X1="0" X2="1" Y1="1" Y2="0"
and using Stretch="Fill"
expands the Line to the edges. However, it ends up looking like this:
(Annoyingly, I can't post images, I need to go answer someone elses questions to earn some rep. So instead please go to this link to see the line, or paste the above XAML into Kaxaml.) http://img375.imageshack.us/img375/1996/border1.png
I drew a magenta border around the Grid element hosting the Line, to make the problem more obvious.
How can I expand the line to really fill the gap (for example by inflating the drawable area within the grid), or, is there a better way?
Also, transformations distort the line, making it thicker. I tried scaling up but there wasn't a consistency to this. Endcaps on the line look just as bad (Triangle for example).
Finally, this method is still flawed, because I want to be able to set the corner size in the future, so having the edge width for the row/column set to 10 seems like a stumbling point. Binding to a property might solve that, I've never done that in a Style though.
Thanks for reading, Tom
The WPF border is inheriting from class Decorator. It is pretty easy to write your own Decorator. Below one draws a border around a child with "tucked in" corners.
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);
}
Use like this:
<BorderTest:FunkyBorder BorderBrush="Red">
<TextBlock Text="Hello" />
</BorderTest:FunkyBorder>
这篇关于我如何在wpf中绘制一个带有方角的边框?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!