原文:使用WPF创建画图箭头

今天要给leader line画个箭头,所以就google一下,找到下面的文章,写的不错,可以实现我的需求,所以就摘录下来。

我把源代码中的arraw.cs加入到我的工程,修改了namespace,然后写了个方法进行调用:

        private void DrawLeaderLineArrow(Point startPt, Point endPt)
        {
            Arrow arrow = new Arrow();
            arrow.X1 = startPt.X;
            arrow.Y1 = startPt.Y;
            arrow.X2 = endPt.X;
            arrow.Y2 = endPt.Y;
            arrow.HeadWidth = 15;
            arrow.HeadHeight = 5;
            arrow.Stroke = Brushes.Black;
            arrow.StrokeThickness = 1;
            _canvas.Children.Add(arrow);
        }

 

WPF Arrow and Custom Shapes

 

Introduction

WPF is the best UI Framework ever. It provides us with a large arsenal of vector graphic types such as Line, Ellipse, Path and others. Sometimes we need shapes which are not provided in the WPF arsenal (such an Arrow), and with all due respect to the Path shape, which can be used to create any type of 2D shape, we do not want to recalculate each point every time. This is a good reason and opportunity to create a custom shape.

 

Background

WPF provides two kinds of vector types: Shapes and Geometries.

Shape is any type that derives from the Shape base class. It provides Fill, Stroke and other properties for coloring, and is actually a FrameworkElement. Thus we can place shapes inside Panels, we can register shapes routed events and do anything related to FrameworkElement. (MSDN)

Geometry is any type that derives from the Geometry base type. It provides properties for describing any type of 2D geometry. A geometry is actually a Freezable type, thus can be frozen. A frozen object provides better performance by not notifying changes, and can be safely accessed by other threads. Geometry is not Visual, hence should be painted by other types such as Path. (MSDN)

 

Using the Code

Now that we have a little background, and we know what the differences between a Geometry and Shape are, we can create our shape based on one of these two types. Correct?

Well, surprisingly we can't base our custom shape on the Geometry type, since its one and only default constructor is marked as internal. Shame on you Microsoft.

Don't worry! We still have an option to base our custom shape on the Shape base class.

Now, let’s say that we want to create an Arrow shape. An arrow is actually a kind of line, so let’s derive our custom type from the WPF Line type which has X1, Y1, X2 and Y2 properties.

Ooopps... Line is sealed! (Shame on you twice).

Never mind, let's derive directly from the Shape base class, and add X1, Y1, X2, Y2 and two additional properties for defining the arrow's head width and height.

 

Our code should end up with something like this:

CollapseCopy Code
    public sealed class Arrow : Shape
{
public static readonly DependencyProperty X1Property = ...;
public static readonly DependencyProperty Y1Property = ...;
public static readonly DependencyProperty HeadHeightProperty = ...;
... [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 HeadHeight
{
get { return (double)base.GetValue(HeadHeightProperty); }
set { base.SetValue(HeadHeightProperty, value); }
}
... 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;
}
} /// <summary>
/// Draws an Arrow
/// </summary>
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);
}
}

As you can see, it is very easy to implement a custom shape, thanks to the great work in the Shape base class. All we have to do is derive our custom shape type from Shape, and override the DefiningGeometry property. This property should return a Geometry of any type.

使用WPF创建画图箭头的更多相关文章

  1. 利用WPF创建含多种交互特性的无边框窗体

    咳咳,标题一口气读下来确实有点累,让我先解释一下.另外文章底部有演示程序的下载. 本文介绍利用WPF创建一个含有以下特性的窗口: 有窗口阴影,比如QQ窗口外围只有几像素的阴影: 支持透明且无边框,为了 ...

  2. WPF 创建无边框的圆角窗口

    原文:WPF 创建无边框的圆角窗口 如题所述,在WPF中要创建一个没有边框且为圆角的窗体,有如下几步工作要进行: 第一步:去掉窗体默认样式的边框 首先将窗体的背景设为透明,将允许透明的属性设置为Tru ...

  3. WPF 创建自定义窗体

    在前面的一篇博客"WPF 自定义Metro Style窗体",展示了如何创建一个类似于Metro Style的Window,并在程序中使用.但是这个窗体不能够自由的改变大小.今天的 ...

  4. wpf 创建动画三种方式

    动画类型 : 故事版,CompositionTarget,DispachTime 那么到此,三种动态创建动画的方法都已经详细介绍过了,大家可能会有种感觉,比较钟情于第一种WPF/Silverlight ...

  5. 使用WPF创建无边框窗体

    一.无边框窗口添加窗口阴影 实际上在WPF中添加无边框窗口的窗口阴影十分简单. 首先,设置WindowStyle="None"以及AllowsTransparency=" ...

  6. 使用 WPF 创建单实例应用程序

    一个简单的例子就是大家在使用很多应用程序,例如在使用Microsoft Word 时会遇到一种情况,不管你打开多少个文档,系统一次只能加载一个winword.exe 实例.当打开新文档时,文档在新窗口 ...

  7. WPF InkCanvas 画图 基础使用教程

    大家好,由于很多原因,我有很长一段时间没有在 CSDN 上分享我的学习成果了,如今终于可以回归分享之路了. 之前在做一个项目的时候,想在一个区域里绘制自己的图形,于是上网搜索资料,无意中找到了 Ink ...

  8. AutoCAD 凸度(bulge)的概念及使用WPF函数画图

    前言  凸度(bulge)是AutoCAD 中一个非常重要的概念,凸度控制着两点之间弧度大小,弧度的方向.各种复杂的图像有可能就是成百上千的弧线组成的.从AutoCAD中导出的数据也有该值,一般的形式 ...

  9. WPF 创建用户控件并引用

    项目源码地址:https://github.com/lizhiqiang0204/WpfControlLibrary.git 首先创建新项目->WPF用户控件库项目 在UserControl1. ...

随机推荐

  1. js进阶 12-8 如何知道鼠标和键盘当前操作的是哪个键

    js进阶 12-8 如何知道鼠标和键盘当前操作的是哪个键 一.总结 一句话总结:event.which属性. 1.如何获取事件发生的时间? timeStamp属性 event.timeStamp 属性 ...

  2. 机器学习算法笔记1_2:分类和逻辑回归(Classification and Logistic regression)

    形式: 採用sigmoid函数: g(z)=11+e−z 其导数为g′(z)=(1−g(z))g(z) 如果: 即: 若有m个样本,则似然函数形式是: 对数形式: 採用梯度上升法求其最大值 求导: 更 ...

  3. 【66.47%】【codeforces 556B】Case of Fake Numbers

    time limit per test2 seconds memory limit per test256 megabytes inputstandard input outputstandard o ...

  4. 修改QList中的item(使用下标([index])才可以获得可修改的item的引用)

    QList算是最常用的集合了,今儿偶然间需要修改QList中的值,结果郁闷了.QList中提供了replace函数来替换item,但不是修改.而at().value()操作均返回的是const的ite ...

  5. Jupyter Notebook 常用快捷键

    Jupyter Notebook 提供了比 IPython 美观的多得多的可视化形式.(比如对于 pandas 下的 DataFrame 的展示,df.head(5)) Jupyter Noteboo ...

  6. Android OkHttp网络连接封装工具类

    package com.lidong.demo.utils; import android.os.Handler; import android.os.Looper; import com.googl ...

  7. iOS --- UIColor中使用16进制选取颜色

    iOS中的UIColor能够使用16进制来选取颜色. 预先定义例如以下: #define UIColorFromHex(s) [UIColor colorWithRed:(((s & 0xFF ...

  8. <Linux> Xen虚拟机镜像的安装

    了解系统安装在哪个磁盘上:fdisk -l 建立存放虚拟机镜像的目录:mkdir /mnt/vmx 更改文件系统格式: mkfs -t ext4 /dev/sda或者/dev/sdb(系统不在的那个硬 ...

  9. 博客搬家啦! -----> http://ronghaopger.github.io/

    新地方: http://ronghaopger.github.io/ 以后这里就不更新了,感谢博客园!

  10. js中动态创建json,动态为json添加属性、属性值的实例

    如下所示: ? 1 2 3 4 5 6 7 var param = {};  for(var i=0;i<fields.length;i++){  var field = fields[i]; ...