走进WPF系列学习之二 如何用WPF绘制与配置2D图形界面

今天在公司比较闲所以就连续学了两节WPF,下面是要跟大家探讨一下关于如何用WPF绘制与配置2D图像界面,说来好笑,刚刚公司的同事遇到一个有关WPF的问题——怎样在WPF的设计页面化一个红色箭头。刚刚看到这个问题,我思考了一下,也动手做了一下,不过能力有限还是自己没有解决,于是开始Google,Baidu。在网上有很多解决方案,但是用的方法都是差不多的:写一个画箭头的class,然后在需要用到箭头的地方实例化箭头Class,并给相应的坐标赋值,最后将对象Add到Canvas控件中。

用WPF画箭头

自定义的画箭头Class代码:

View Code
namespace ZhangWei.WPF.Shapes
{
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Media;
using System.Windows.Shapes;


/// <summary>
/// TODO: Update summary.
/// </summary>
public sealed class Arrow : Shape
{
#region Dependency Properties

public static readonly DependencyProperty X1Property = DependencyProperty.Register("X1", typeof(double), typeof(Arrow), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure));
public static readonly DependencyProperty Y1Property = DependencyProperty.Register("Y1", typeof(double), typeof(Arrow), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure));
public static readonly DependencyProperty X2Property = DependencyProperty.Register("X2", typeof(double), typeof(Arrow), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure));
public static readonly DependencyProperty Y2Property = DependencyProperty.Register("Y2", typeof(double), typeof(Arrow), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure));
public static readonly DependencyProperty HeadWidthProperty = DependencyProperty.Register("HeadWidth", typeof(double), typeof(Arrow), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure));
public static readonly DependencyProperty HeadHeightProperty = DependencyProperty.Register("HeadHeight", typeof(double), typeof(Arrow), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure));

#endregion

#region CLR Properties

[TypeConverter(typeof(LengthConverter))]
public double X1
{
get { return (double)base.GetValue(X1Property); }
set { base.SetValue(X1Property, value); }
}

[TypeConverter(typeof(LengthConverter))]
public double Y1
{
get { return (double)base.GetValue(Y1Property); }
set { base.SetValue(Y1Property, value); }
}

[TypeConverter(typeof(LengthConverter))]
public double X2
{
get { return (double)base.GetValue(X2Property); }
set { base.SetValue(X2Property, value); }
}

[TypeConverter(typeof(LengthConverter))]
public double Y2
{
get { return (double)base.GetValue(Y2Property); }
set { base.SetValue(Y2Property, value); }
}

[TypeConverter(typeof(LengthConverter))]
public double HeadWidth
{
get { return (double)base.GetValue(HeadWidthProperty); }
set { base.SetValue(HeadWidthProperty, value); }
}

[TypeConverter(typeof(LengthConverter))]
public double HeadHeight
{
get { return (double)base.GetValue(HeadHeightProperty); }
set { base.SetValue(HeadHeightProperty, value); }
}

#endregion

#region Overrides

protected override Geometry DefiningGeometry
{
get
{
// Create a StreamGeometry for describing the shape
StreamGeometry geometry = new StreamGeometry();
geometry.FillRule = FillRule.EvenOdd;

using (StreamGeometryContext context = geometry.Open())
{
InternalDrawArrowGeometry(context);
}

// Freeze the geometry for performance benefits
geometry.Freeze();

return geometry;
}
}

#endregion

#region Privates

private void InternalDrawArrowGeometry(StreamGeometryContext context)
{
double theta = Math.Atan2(Y1 - Y2, X1 - X2);
double sint = Math.Sin(theta);
double cost = Math.Cos(theta);

Point pt1 = new Point(X1, this.Y1);
Point pt2 = new Point(X2, this.Y2);

Point pt3 = new Point(
X2 + (HeadWidth * cost - HeadHeight * sint),
Y2 + (HeadWidth * sint + HeadHeight * cost));

Point pt4 = new Point(
X2 + (HeadWidth * cost + HeadHeight * sint),
Y2 - (HeadHeight * cost - HeadWidth * sint));

context.BeginFigure(pt1, true, false);
context.LineTo(pt2, true, true);
context.LineTo(pt3, true, true);
context.LineTo(pt2, true, true);
context.LineTo(pt4, true, true);
}

#endregion


}
}

给一个调用的例子:

首先在页面中放一个Canvas控件用来在其中显示箭头,

<Window x:Class="Tomers.WPF.Shapes.MainWindow"
xmlns
="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml"
Title
="MainWindow" Height="350" Width="525">
<Grid>
<Canvas Height="104" Margin="42,45,313,0" Name="canvas1" VerticalAlignment="Top" >
</Canvas>
</Grid>
</Window>

下面就可以在后台代码中让箭头出现在Canvas中,代码:

using System.Windows;
using System.Windows.Media;

namespace Tomers.WPF.Shapes
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Arrow arrow = new Arrow();
arrow.X1 = 2;
arrow.Y1 = 10;
arrow.X2 = 60;
arrow.Y2 = 10;
arrow.HeadWidth = 10;
arrow.HeadHeight = 2;
arrow.Stroke = Brushes.Red;
arrow.StrokeThickness = 2;
canvas1.Children.Add(arrow);
}
}
}

在代码中

arrow.X1 = 2;
arrow.Y1 = 10;
arrow.X2 = 60;
arrow.Y2 = 10;

用来指定箭头在画布Canvas中的位置,其中X1,Y1表示箭头的开始坐标,X2,Y2表示箭头的结束坐标。当配置完成之后将对象Add到Canvase的子节点中。

源代码下载

当我很兴奋的告诉同事问题已经被我拿下来啦,但是当她看到那个箭头Class时,直接否定了我。原因代码太多了接受不了。说要用XAML代码来完成,不用C#。于是让我寻求第二种解决方法的动力就来了。正在我焦头乱额时,突然让我看到了曙光,那就是——WPF绘制与配置2D图像界面。

其实也不是什么高深的东东,主要用到了Line和Path,这两个东西简直太神奇了,我先前不知道第二种方法的原因是:我以为只能用ToolBox里面提供的控件,但是在Toolbox里面都没有可以画直线是东东。让我找到了Line顾名思义是用来画直线的,Path可以用来画曲线,有了这两个东西,画箭头俺还不是小菜吗。呵呵。。。。。。

只需要短短的几行XAML就搞定了上面大量代码搞定的事咯: 

<Window x:Class="Tomers.WPF.Shapes.MainWindow"
xmlns
="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml"
Title
="MainWindow" Height="350" Width="525">
<Grid>
<Canvas Height="152" Name="canvas2" Width="200" Canvas.Left="43" Canvas.Top="90">
<Line X1="30" Y1="100" X2="100" Y2="100" Stroke="Red" StrokeThickness="3" Width="226" Height="140" />
<Path Stroke="Red" StrokeThickness="3" Data="M85,105 100,100 85,95" />
</Canvas>
</Grid>
</Window>

一句C#代码都不要太爽啦。

下面就更深一步的来研究一下其他的用来画图的东东,来看看下面这个图吧:

很简单,这些都是小Case,你们可以自由发挥,用最简单最基本的东西可以画出很多好看的图形。实现上面图像的XAML代码是:

<Window x:Class="Demo2.MainWindow"
xmlns
="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml"
Title
="MainWindow" Height="350" Width="525">
<Grid Name="grid1">
<Line X1="100" Y1="150" X2="200" Y2="200" Stroke="Red" StrokeThickness="2"/>
<Ellipse Height="237" HorizontalAlignment="Left" Margin="299,12,0,0" Name="ellipse1" Stroke="Black" VerticalAlignment="Top" Width="143" Fill="Yellow" StrokeThickness="6" />
<Button Content="Button" Height="77" HorizontalAlignment="Left" Margin="42,222,0,0" Name="button1" VerticalAlignment="Top" Width="203" Click="button1_Click" />
<Button Content="Button" Height="60" HorizontalAlignment="Left" Margin="278,239,0,0" Name="button2" VerticalAlignment="Top" Width="75" Click="button2_Click" />
<Canvas Height="139" HorizontalAlignment="Left" Margin="10,10,0,0" Name="canvas1" VerticalAlignment="Top" Width="252" >
<Ellipse Canvas.Left="10" Canvas.Top="10" Height="25" Name="ellipse2" Stroke="Black" Width="50" />
<Path Stroke="Blue" StrokeThickness="2" Data="M2,2 Q30,40 4,50"/>
<Polygon Stroke="Red" StrokeThickness="4" Points="40,100 80,120 60,160">
<Polygon.Fill>
<SolidColorBrush Color="Yellow"/>
</Polygon.Fill>
</Polygon>
</Canvas>
</Grid>
</Window>

很少吧。这里很有趣,大家可以自由发挥,我实在没什么美术天分,所以画不出来好东西,在这里也不给大家画啦。

我把源码分享给大家吧(源码下载

原文地址:https://www.cnblogs.com/zwzw/p/good.html