11.绘图

WPF基本的图形包括:

  • Line,直线段
  • Rectangle,矩形
  • Ellipse,椭圆
  • Polygon,多边形,由多条直线段围成的闭合区域
  • Polylin,折线,不闭合
  • Path,路径,闭合区域,基本图形中功能最强大的一个,可由直线、圆弧、贝塞尔曲线组成

绘图不一定要在Canvas中完成,可以再任何一种布局中完成。

基本图形

Line

X1,Y1作为起点,X2,Y2作为终点。Stroke(边线)属性的数据类型是Brush,用来填充。

<Window x:Class="WpfApplication1.Window45"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window45" Height="293" Width="437">
<Grid>
<Line X1="10" Y1="20" X2="260" Y2="20" Stroke="Red" StrokeThickness="10"></Line>
<Line X1="10" Y1="40" X2="260" Y2="40" Stroke="Orange" StrokeThickness="6"></Line>
<Line X1="10" Y1="60" X2="260" Y2="60" Stroke="Green" StrokeThickness="3"></Line>
<Line X1="10" Y1="80" X2="260" Y2="80" Stroke="Purple" StrokeThickness="2"></Line>
<Line X1="10" Y1="100" X2="260" Y2="100" Stroke="Black" StrokeThickness="1"></Line>
<Line X1="10" Y1="120" X2="260" Y2="120" StrokeDashArray="3" Stroke="Black" StrokeThickness="1"></Line>
<Line X1="10" Y1="140" X2="260" Y2="140" StrokeDashArray="5" Stroke="Black" StrokeThickness="1"></Line>
<Line X1="10" X2="260" Y1="160" Y2="160" Stroke="Black" StrokeThickness="6" StrokeEndLineCap="Flat"></Line>
<Line X1="10" X2="260" Y1="180" Y2="180" Stroke="Black" StrokeThickness="8" StrokeEndLineCap="Triangle"></Line>
<Line X1="10" X2="260" Y1="200" Y2="200" StrokeEndLineCap="Round" StrokeThickness="10">
<Line.Stroke>
<LinearGradientBrush EndPoint="0,0.5" StartPoint="1,0.5">
<GradientStop Color="Blue"></GradientStop>
<GradientStop Offset="1" Color="Red"></GradientStop>
</LinearGradientBrush>
</Line.Stroke>
</Line>
</Grid>
</Window>

11.WPF绘图_wpf

Rectangle

矩形由Stroke(边线)和Fill(填充)构成。数据类型均为Brush,常见的Brush子类有:

  • SolidColorBrush:实心画刷,常用Red、Blue等字符串赋值
  • LinearGradientBrush:线性渐变画刷
  • RadialGradentBrush:径向渐变画刷
  • ImageBrush:图片作为内容
  • DrawingBrush:矢量图或者位图填充
  • VisualBrush:每个控件的可视化形象可以通过Visual类的方法获得,并用该形象进行填充,这就是VisualBrush。如:控件被拖拽过程中显示一个幻影,这个幻影就可以使用VisualBrush来填充。
<Window x:Class="WpfApplication1.Window46"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window46" Height="390" Width="600">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="180" />
<ColumnDefinition Width="10" />
<ColumnDefinition Width="180" />
<ColumnDefinition Width="10" />
<ColumnDefinition Width="180*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="160" />
<RowDefinition Height="10" />
<RowDefinition Height="160" />
</Grid.RowDefinitions>
<!--实心填充-->
<Rectangle Grid.Row="0" Grid.Column="0" Stroke="Black" Fill="LightBlue"></Rectangle>
<!--线性渐变-->
<Rectangle Grid.Row="0" Grid.Column="2">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
<GradientStop Color="#FFB6F8F1" Offset="0"></GradientStop>
<GradientStop Color="#FF0082BD" Offset="0.25"></GradientStop>
<GradientStop Color="#FF95DEFF" Offset="0.6"></GradientStop>
<GradientStop Color="#FF004F72" Offset="1"></GradientStop>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<!--径向渐变-->
<Rectangle Grid.Row="0" Grid.Column="4">
<Rectangle.Fill>
<RadialGradientBrush>
<GradientStop Color="#FFB6F8F1" Offset="0"></GradientStop>
<GradientStop Color="#FF0082BD" Offset="0.25"></GradientStop>
<GradientStop Color="#FF95DEFF" Offset="0.75"></GradientStop>
<GradientStop Color="#FF004F72" Offset="1.5"></GradientStop>
</RadialGradientBrush>
</Rectangle.Fill>
</Rectangle>
<!--图片填充-->
<Rectangle Grid.Row="2" Grid.Column="0">
<Rectangle.Fill>
<ImageBrush ImageSource="./01077_1.png" Viewport="0,0,0.3,0.3" TileMode="Tile">

</ImageBrush>
</Rectangle.Fill>
</Rectangle>
<!--矢量图-->
<Rectangle Grid.Row="2" Grid.Column="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"></EllipseGeometry>
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingBrush.Drawing>
</DrawingBrush>
</Rectangle.Fill>
</Rectangle>
<!--无填充,使用线性渐变填充边框-->
<Rectangle Grid.Row="2" Grid.Column="5" StrokeThickness="10">
<Rectangle.Stroke>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
<GradientStop Color="White" Offset="0.3"></GradientStop>
<GradientStop Color="Blue" Offset="1"></GradientStop>
</LinearGradientBrush>
</Rectangle.Stroke>
</Rectangle>
</Grid>
</Window>

11.WPF绘图_.net_02

使用visualBrush的案例

<Grid Margin="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="160" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="160" />
</Grid.ColumnDefinitions>
<StackPanel Background="White" x:Name="spleft">
<Button Height="40" Content="OK" x:Name="btnReal" Click="btnReal_Click"></Button>
</StackPanel>
<Button Grid.Column="1" Content=">>" Margin="5,0"></Button>
<StackPanel Grid.Column="2" Background="White" x:Name="spRight">
</StackPanel>
</Grid>
double o = 1;//不透明度指数
private void btnReal_Click(object sender, RoutedEventArgs e)
{
VisualBrush vb = new VisualBrush(this.btnReal);
Rectangle rtg = new Rectangle();
rtg.Width = btnReal.Width;
rtg.Height = btnReal.Height;
rtg.Fill = vb;
rtg.Opacity = o;
o -= 0.2;
this.spRight.Children.Add(rtg);
}

11.WPF绘图_ui_03

Ellipse

<Grid>
<Ellipse Height="140" Name="ellipse1" Stroke="Gray" Width="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"></RotateTransform>
</TransformGroup>
</RadialGradientBrush.RelativeTransform>
<GradientStop Color="#FFFFFFFF" Offset="0" />
<GradientStop Color="#FF444444" Offset="0.66" />
<GradientStop Color="#FF999999" Offset="1" />
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>
</Grid>

11.WPF绘图_wpf_04

Path

路径可以替代其他几种图形,并且可以将直线、圆弧等基本元素结合形成复杂的图形。Path最重要的属性是Data,其数据类型为Geometry抽象类。Geometry子类包括:

  • LineGeometry:直线几何
  • RectangleGeometry:矩形几何
  • EllipseGeometry:椭圆几何
  • PathGeometry:路径几何
  • StreamGeometry:PathGeometry的轻量替代类,不支持Binding、动画等
  • CombinedGeometry:多个基本图形的组合,形成一个几何图形
  • GeometryGroup:多个基本图形的组合,形成一个几何图形组

与Line、Rectangle等的区别:Line等可以独立存在,而Geometry只能结合其他几何,不能独立存在。

<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="160" />
<RowDefinition Height="160" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="160" />
<ColumnDefinition Width="160" />
</Grid.ColumnDefinitions>
<!--直线-->
<Path Stroke="Blue" StrokeThickness="2" Grid.Row="0" Grid.Column="0">
<Path.Data>
<LineGeometry StartPoint="0,0" EndPoint="160,160"></LineGeometry>
</Path.Data>
</Path>
<!--矩形路径-->
<Path Stroke="Orange" Fill="Yellow" Grid.Row="0" Grid.Column="1">
<Path.Data>
<RectangleGeometry Rect="20,20,120,120" RadiusX="10" RadiusY="10"></RectangleGeometry>
</Path.Data>
</Path>
<!--椭圆路径-->
<Path Stroke="Green" Fill="LawnGreen" Grid.Column="0" Grid.Row="1">
<Path.Data>
<EllipseGeometry Center="80,80" RadiusX="60" RadiusY="40"></EllipseGeometry>
</Path.Data>
</Path>
<!--自定义路径-->
<Path Stroke="Yellow" Fill="Orange" Grid.Row="1" Grid.Column="1">
<Path.Data>
<PathGeometry>
<!--Path.Figures属性可以容纳Figuress对象,PathFigure的Segments属性可以容纳各种线段用于形复杂的图形-->
<PathGeometry.Figures>
<PathFigure StartPoint="25,140" IsClosed="True">
<PathFigure.Segments>
<LineSegment Point="20,40"></LineSegment>
<LineSegment Point="40,110"></LineSegment>
<LineSegment Point="50,20"></LineSegment>
<LineSegment Point="80,110"></LineSegment>
<LineSegment Point="110,20"></LineSegment>
<LineSegment Point="120,110"></LineSegment>
<LineSegment Point="140,40"></LineSegment>
<LineSegment Point="135,140"></LineSegment>
</PathFigure.Segments>
</PathFigure>
</PathGeometry.Figures>
</PathGeometry>
</Path.Data>
</Path>
</Grid>

PathFigure的Segments属性所包含的线段:

  • LineSegment:直线
  • ArcSegment:圆弧
  • BezierSement:三次方贝塞尔曲线
  • QuadraticBezierSement:二次方贝塞尔曲线
  • PolyLineSegment:多直线段
  • PolyBezierSegment:多三次方贝塞尔曲线
  • PolyQuadraticBezierSement:多二次方贝塞尔曲线

注意:绘制上面的线段是没有起点的,起点就是上一个线段的终点,第一个线段的起点是PathFigure的StartPoint。

路径标记法

Path非常强大,但是上面的标签式的语法过于繁琐。WPF提供路径标记法来替代标签式语法。、

路径标记法就是各种线段的简记法,如​​<LineSegment Point="15,15">​​​可以改为​​L, 15,15​​​。除此之外,路径标记法还增加了一些实用的命令,如​​H 80​​,代表从当前点画一条水平直线,横坐标终点是80。

使用路径标记法的步骤分为三步​​移动至起点->绘图->闭合图形​​,常用命令如下:

命令

用途

语法

示例

标签式语法

分类

M

移动到起点

M 起始点

M 10,10

移动命令

L

绘制直线

L 终点

L 150,30

绘图命令

H

绘制水平直线

H 终点横坐标

H 180

绘图命令

V

绘制数值线

V 终点纵坐标

V 180

绘图命令

A

绘制圆弧

A 母椭圆尺寸 旋转角度是否大弧 顺/逆 终点

A 180,80 45 1 1 150,150

绘图命令

C

三次方贝塞尔曲线

C 控制点1 控制点2 终点

C 250,0 50,200 300,200

绘图命令

Q

二次方贝塞尔

Q 控制点1 终点

Q 150,100 300,200

绘图命令

S

平滑三次贝塞尔

S 控制点2 终点

S 100,200 200,300

绘图命令

T

平滑二次方贝塞尔

T 终点

T 400,200

绘图命令

Z

闭合图形

Z

M 0,0 L 40,80 L80,40 Z

关闭命令

裁剪界面

不规则窗体和控件仅需使用Clip属性就可以做到。

<Grid VerticalAlignment="Center" HorizontalAlignment="Center">
<Path Stroke="Orange" Fill="Yellow" x:Name="clipPath0" Visibility="Hidden" 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">
</Path>
<Button Content="Clip" Width="80" Height="25" Click="Button_Click" HorizontalAlignment="Center" VerticalAlignment="Center"></Button>
</Grid>
private void Button_Click(object sender, RoutedEventArgs e)
{
this.Clip = clipPath0.Data;
}

11.WPF绘图_.net_05

图形的效果与滤镜

简单易用的BitmapEffect

WPF早期的效果属性,是使用CPU来进行运算,会导致程序卡顿,不建议使用

BitmapEffect派生类:

  • BevelBitmapEffect:斜角效果
  • BitmapEffectGroup:符合效果
  • BlurBitmapEffect:模糊效果
  • DropShadowBitmapEffect:投影效果
  • EmbossBitmapEffect:浮雕效果
  • OuterGlowBitmapEffect:外发光效果
<Button Content="Click Me" Grid.Column="0" Grid.Row="0" Margin="20">
<Button.BitmapEffect>
<BlurBitmapEffect Radius="3"/>
</Button.BitmapEffect>
</Button>

11.WPF绘图_c#_06

Effect

采用GPU对UI进行渲染,功能强大。

Effect具有如下派生类

  • BlurEffect:模糊效果
  • DropShadowEffect:投影效果
  • ShaderEffect:着色器(抽象类)

图形的变形

WPF中的变形与UI元素是分开的,可以单独设置一个旋转45度的变形,然后把该变形赋值给不同的UI元素。

控制变形的属性有两个:

  • RenderTransform:呈现变形
  • LayoutTransform:布局变形

这两个属性的值类型为Transform抽象类型,常见的派生类有:

  • MatrixTransform:矩阵变形
  • RotateTransform:旋转变形
  • ScaleTransform:缩放变形
  • SkewTransform:拉伸变形
  • TranslateTransform:偏移变形
  • TransformGroup:变形组

呈现变形(Render Transform)

让UI元素呈现出来的属性与本来的属性不一样,如按钮本来在左上角,但是让他呈现在右下角,且旋转45度。

<Window x:Class="WpfApplication1.Window59"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window59" Height="334" Width="485">
<Grid Margin="10" Background="AliceBlue">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto">
</ColumnDefinition>
<ColumnDefinition Width="*">
</ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto">
</RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<Button Width="80" Height="80" Content="OK" Background="Red" Foreground="White">
<Button.RenderTransform>
<!--复合变形-->
<TransformGroup>
<!--旋转变形-->
<RotateTransform CenterX="40" CenterY="40" Angle="45"></RotateTransform>
<!--偏移变形-->
<TranslateTransform X="300" Y="150"></TranslateTransform>
</TransformGroup>
</Button.RenderTransform>
</Button>
</Grid>
</Window>

11.WPF绘图_Line_07

在Grid中划分了2行2列,第一行第一列根据Button自动调整,然后利用变形天正button的位置和角度,但是Button本身的位置属性却没有改变,这样仅仅改变button的显示效果会极大提高效率。所以在做动画的时候一定要用Render Transform。

布局变形

与呈现变形不同,布局变形会影响整个窗体的布局导致重新测算,所以会影响程序的性能。布局变形一般用在静态变形上。

实现文字纵向排列

  • 使用呈现变形
<Grid x:Name="titleBar" Background="LightBlue">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock FontSize="24" Text="Hello Transformer" VerticalAlignment="Bottom" HorizontalAlignment="Center">
<TextBlock.LayoutTransform>
<RotateTransform Angle="-90"></RotateTransform>
</TextBlock.LayoutTransform>
</TextBlock>
</Grid>

11.WPF绘图_.net_08

看上去实现了纵向排列,但事实上TextBox本身属性没有变,所以Grid的第一列宽度任然没有变。

  • 使用布局变形

将上面的

<TextBlock.LayoutTransform>
<RotateTransform Angle="-90"></RotateTransform>
</TextBlock.LayoutTransform>

改为

<TextBlock.LayoutTransform>
<RotateTransform Angle="-90"></RotateTransform>
</TextBlock.LayoutTransform>

此时,Grid的第一列宽度改变。

11.WPF绘图_ui_09