走進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>

 

很少吧。這里很有趣,大家可以自由發揮,我實在沒什么美術天分,所以畫不出來好東西,在這里也不給大家畫啦。

我把源碼分享給大家吧(源碼下載

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM