WPF的基本图形包括以下几个:
Line:直线段,可以设置触笔(Stroke)
Rectangle:矩形,既有触笔,又有填充(Fill)
Ellicps:椭圆,长宽相等即为正圆,既有触笔又有填充
Polygon:多边形,由多条直线段围成的闭合区域,既有触笔又有填充。
Polyline:折线(不闭合),由多边收尾相接的直线段组成。
Path:路径(闭合区域),由若干直线、圆弧、贝塞尔曲线组成。
1. 直线:
使用X1、Y1设置起点,X2、Y2设置终点。Stroke属性类型是Brush(画刷),凡是Brush的派生类均可用于给这个属性赋值。 通过StrokeDashArray画出虚线,StrokeEndLineCap控制线段终点形状,LinearGradientBrush画出渐变色。
<Grid>
<Line X1="10" Y1="20" X2="260" Y2="20" Stroke="Red" StrokeThickness="10"/>
<Line X1="10" Y1="40" X2="260" Y2="40" Stroke="Orange" StrokeThickness="6"/>
<Line X1="10" Y1="60" X2="260" Y2="60" Stroke="Green" StrokeThickness="3"/>
<Line X1="10" Y1="80" X2="260" Y2="80" Stroke="Purple" StrokeThickness="2"/>
<Line X1="10" Y1="100" X2="260" Y2="100" Stroke="Black" StrokeThickness="1"/>
<Line X1="10" Y1="120" X2="260" Y2="120" StrokeDashArray="3" Stroke="Black"
StrokeThickness="1"/>
<Line X1="10" Y1="140" X2="260" Y2="140" StrokeDashArray="5" Stroke="Black"
StrokeThickness="1"/>
<Line X1="10" Y1="160" X2="260" Y2="160" Stroke="Black" StrokeEndLineCap="Flat"
StrokeThickness="6"/>
<Line X1="10" Y1="180" X2="260" Y2="180" Stroke="Black" StrokeEndLineCap="Triangle"
StrokeThickness="8"/>
<Line X1="10" Y1="200" X2="260" Y2="200" StrokeEndLineCap="Round" StrokeThickness="8">
<Line.Stroke>
<LinearGradientBrush EndPoint="0,0.5" StartPoint="1,0.5">
<GradientStop Color="Blue"/>
<GradientStop Offset="1"/>
</LinearGradientBrush>
</Line.Stroke>
</Line>
</Grid>
效果图如下:
2. 矩形:
矩形由笔触(Stroke,即边线)和填充(Fill)构成。Stroke属性设置和Line一样。Fill属性的数据类型是Brush。Brush是抽象类,只能用其派生类的事例为其赋值,常见的Brush类型有:
SolidColorBrush:实心画刷。可以直接用字符串(如Red、Blue等)直接赋值。
LinearGradientBrush:径向渐变画刷。
ImageBrush:使用图片作为填充内容。
DrawingBrush:使用矢量图(Vector)和位图(Bitmap)作为填充内容。
VisualBrush:WPF中每个控件都是由FrameworkElement类派生来的,而FrameworkElement又是由Visual类派生来。Visual意为“可视”之意,每个控件的可视化形象就可以通过Visual类的方法获得。获得这个可视化形象后,就可以用这个可视化形象进行填充。
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="160"/>
<RowDefinition Height="10"/>
<RowDefinition Height="160"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="180"/>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="180"/>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="180"/>
</Grid.ColumnDefinitions>
<!--实心填充-->
<Rectangle Grid.Column="0" Grid.Row="0" Stroke="Black" Fill="LightBlue"/>
<!--线性渐变-->
<Rectangle Grid.Column="2" Grid.Row="0">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
<GradientStop Color="#ffb6f8f1" Offset="0"/>
<GradientStop Color="#ff0082bd" Offset="0.25"/>
<GradientStop Color="#ff95deff" Offset="0.6"/>
<GradientStop Color="#ff005f72" Offset="1"/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<!--径向渐变-->
<Rectangle Grid.Column="4" Grid.Row="0">
<Rectangle.Fill>
<RadialGradientBrush>
<GradientStop Color="#ffb6f8f1" Offset="0"/>
<GradientStop Color="#ff0082bd" Offset="0.25"/>
<GradientStop Color="#ff95deff" Offset="0.6"/>
<GradientStop Color="#ff005f72" Offset="1"/>
</RadialGradientBrush>
</Rectangle.Fill>
</Rectangle>
<!--图片填充-->
<Rectangle Grid.Column="0" Grid.Row="2">
<Rectangle.Fill>
<ImageBrush ImageSource=".\logo.jpg" Viewport="0,0,0.3,0.15" TileMode="Tile"/>
</Rectangle.Fill>
</Rectangle>
<!--矢量图填充-->
<Rectangle Grid.Column="2" Grid.Row="2">
<Rectangle.Fill>
<DrawingBrush Viewport="0,0,0.2,0.2" TileMode="Tile">
<DrawingBrush.Drawing>
<GeometryDrawing Brush="LightBlue">
<GeometryDrawing.Geometry>
<EllipseGeometry RadiusX="10" RadiusY="10"/>
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingBrush.Drawing>
</DrawingBrush>
</Rectangle.Fill>
</Rectangle>
<!--无填充,用线性渐变填充边缘-->
<Rectangle Grid.Column="4" Grid.Row="2" StrokeThickness="10">
<Rectangle.Stroke>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
<GradientStop Color="White" Offset="0.3"/>
<GradientStop Color="Blue" Offset="1"/>
</LinearGradientBrush>
</Rectangle.Stroke>
</Rectangle>
</Grid>
效果图如下:
看一个VisualBrush的例子,利用一个Button控件。
<Grid Margin="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="160"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="160"/>
</Grid.ColumnDefinitions>
<StackPanel x:Name="stackPanelLeft" Background="White">
<Button x:Name="realButton" Content="OK" Height="40"/>
</StackPanel>
<Button Content=">>>" Grid.Column="1" Margin="5,0" Click="CloneVisual"/>
<StackPanel x:Name="stackPanelRight" Background="White" Grid.Column="2"/>
</Grid>
效果图如下:
3. 椭圆:
它的方法与矩形没有什么区别。Width与Height相等的椭圆即是正圆。
<Grid>
<Ellipse Stroke="Gray" Width="140" Height="140" Cursor="Hand" ToolTip="A Ball">
<Ellipse.Fill>
<RadialGradientBrush GradientOrigin="0.2,0.8" RadiusX="0.75" RadiusY="0.75">
<RadialGradientBrush.RelativeTransform>
<TransformGroup>
<RotateTransform Angle="90" CenterX="0.5" CenterY="0.5"/>
</TransformGroup>
</RadialGradientBrush.RelativeTransform>
<GradientStop Color="#ffffffff" Offset="0"/>
<GradientStop Color="#ff444444" Offset="0.66"/>
<GradientStop Color="#ff999999" Offset="1"/>
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>
</Grid>
4. 路径:
可以将直线、圆弧、贝塞尔曲线等基本元素结合进来,形成复杂的图形。路径最重要的一个属性是Data,Data的数据类型是Geometry(几何图形),使用这个属性将一些基本的线段拼接起来。
为Data属性赋值的语法有两种:一种是标签式的标准语法,另一种是专门用于绘制几何图形的“路径标记语法”。
Path的Data属性是Geometry类,Geometry是抽象类,只能使用Geometry的子类。子类包括:
Line、Rectangle等类与LineGeometry、RectangleGeometry类的区别:Line、Rectangle类是可独立存在的对象,而*Geometry类只能用于结合成其他几何图形、不能独立存在–当我们在Blend里选中一组独立的几何图形执行组合路径命令时,本质上是把原本独立的Line、Rectangle对象转换为*Geometry对象并结合成新的复合几何图形。
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="160"/>
<ColumnDefinition Width="160"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="160"/>
<RowDefinition Height="160"/>
</Grid.RowDefinitions>
<!--直线-->
<Path Stroke="Blue" StrokeThickness="2" Grid.Column="0" Grid.Row="0">
<Path.Data>
<LineGeometry StartPoint="20,20" EndPoint="140,140"/>
</Path.Data>
</Path>
<!--矩形路径-->
<Path Stroke="Orange" Fill="Yellow" Grid.Column="1" Grid.Row="0">
<Path.Data>
<RectangleGeometry Rect="20,20,120,120" RadiusX="10" RadiusY="10"/>
</Path.Data>
</Path>
<!--椭圆路径-->
<Path Stroke="Green" Fill="LawnGreen" Grid.Column="0" Grid.Row="1">
<Path.Data>
<EllipseGeometry Center="80,80" RadiusX="60" RadiusY="40" />
</Path.Data>
</Path>
<Path Stroke="Yellow" Fill="Orange" Grid.Column="1" Grid.Row="1">
<Path.Data>
<PathGeometry>
<PathGeometry.Figures>
<PathFigure StartPoint="25,140" IsClosed="True">
<PathFigure.Segments>
<LineSegment Point="20,40"/>
<LineSegment Point="40,110"/>
<LineSegment Point="50,20"/>
<LineSegment Point="80,110"/>
<LineSegment Point="110,20"/>
<LineSegment Point="120,110"/>
<LineSegment Point="140,40"/>
<LineSegment Point="135,140"/>
</PathFigure.Segments>
</PathFigure>
</PathGeometry.Figures>
</PathGeometry>
</Path.Data>
</Path>
</Grid>
效果图如下:
PathGeometry:Path的Figures属性可以容纳PathFigure对象,而PathFigure的Segment属性又可以容纳各种线段用于结合成复杂图形。
在绘制线的时候所有线都是没有起点(StartPoint)的,因为起点就是前一个线段的终点,而第一个线段的起点则是PathFigure的StartPoint。如上面代码中PathGeometry部分。
ArcSegment:
用来绘制圆弧,Point属性用来指名圆弧连接的终点;
圆弧截取自椭圆,Size属性即完整椭圆的横纵半径;
SweepDirection属性指名圆弧顺时针方向还是逆时针方向;
椭圆上两点不对称,圆弧会有大弧和小弧,IsLargeArc属性用于指名是否使用大弧连接;
RotationAngle属性用来指明圆弧母椭圆的旋转角度。
BezierSegment(三次贝塞尔曲线)由四个点决定:
(1) 起点:即前一个线段终点 或 PathFigure的StartPoint。
(2)终点:Point3属性,即曲线的终点位置。
(3)两个控制点:Point1和Point2属性。
粗略说,三次贝塞尔曲线就是由起点出发走向Point1方向,在走向Point2方向,最后到达终点的平滑曲线。
QuadraticBezierSegment(二次方贝塞尔曲线)与BezierSegment类似,由三个点决定:
(1)起点:同BezierSegment
(2)终点:Point2属性,曲线终点。
(3)一个控制点:Point1。
想绘制出复杂的图形,我们要做的仅仅是在PathFigure把Segment一段一段加上去。
5. 路径标记语法:
路径标记语法实际上就是各种线段的简记法,比如<LineSegment Point="150,5"/>
可以简写为”L 150,5”,比如H用来画水平直线,”H 180”指从当前点画一条水平直线,终点的横坐标是180(不需要考虑坐标,与当前纵坐标一致),类似还有V命令,用来画竖直直线。
例:
<Path Stroke="Red" Data="M 0,0 C 30,0 70,100 100,100 S 170,0 200,0"/>
<Path Stroke="Red" Data="M 0,200 Q 100,0 200,200 T 400,200"/>
路径标记语法是不区分大小写的,使用两个double数值表示一个点(x,y),用逗号分隔,又可以使用空格。由于路径标记语法中使用空格作为连个点之间的间隔,为了避免混淆,建议使用逗号作为点横纵坐标的分隔符。
6. 使用Path剪裁界面元素
不规则窗体或控件,WPF中仅需要使窗体或控件的Clip属性即可。
Clip属性被定义在UIElement类中,因此WPF窗体和所有控件、图形都具有这个属性。
Clip属性的数据类型是Geometry,与Path的Data属性一致,因此我们只要按需求制作好特殊图形的Path并把Path的Data属性赋值给目标窗体、控件或其他图形,对目标的裁剪就完成了。
想让一个窗体能被剪切,那么气AllowsTransparency必须设为True,这个属性设为True后,WindowStyle属性必须设为None。
<Grid VerticalAlignment="Center" HorizontalAlignment="Center">
<Path Visibility="Hidden" x:Name="clipPath"
Data="M 55,100 A 50,50 0 1 1 100,60 A 110,95 0 0 1 200,60 A 50,50 0 1 1 250,100 A 110,95 0 1 1 55,100 Z"/>
<Button VerticalAlignment="Center" HorizontalAlignment="Center" Width="80" Height="25" x:Name="btnClip" Click="btnClip_Click">Clip</Button>
</Grid>
private void btnClip_Click(object sender, RoutedEventArgs e)
{
this.Clip = this.clipPath.Data;
}
效果: