走进WPF系列学习之二 如何用WPF绘制与配置2D图形界面
2012-01-12 17:23 哒不溜 阅读(2282) 评论(6) 编辑 收藏 举报今天在公司比较闲所以就连续学了两节WPF,下面是要跟大家探讨一下关于如何用WPF绘制与配置2D图像界面,说来好笑,刚刚公司的同事遇到一个有关WPF的问题——怎样在WPF的设计页面化一个红色箭头。刚刚看到这个问题,我思考了一下,也动手做了一下,不过能力有限还是自己没有解决,于是开始Google,Baidu。在网上有很多解决方案,但是用的方法都是差不多的:写一个画箭头的class,然后在需要用到箭头的地方实例化箭头Class,并给相应的坐标赋值,最后将对象Add到Canvas控件中。
用WPF画箭头 |
自定义的画箭头Class代码:
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>
很少吧。这里很有趣,大家可以自由发挥,我实在没什么美术天分,所以画不出来好东西,在这里也不给大家画啦。
我把源码分享给大家吧(源码下载)
我个人QQ:1791786556
讨论QQ群:
WPF学习交流:699150554
WPF/UI 界面开发:527847154
WPF控件编程:699191787