原文:使用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. 浅浅的分析下es6箭头函数

    原文链接:http://damobing.com/?p=589 前言 箭头函数作为es6重点的语法内容之一,很多开发者对其爱不释手,当也要注意其可能存在的问题,其正确的使用场景,否则会引起不必要的bu ...

  2. 黑马程序猿——15,String,StringBuffer,基本数据类型包装对象

    ------<ahref="http://www.itheima.com" target="blank">Java培训.Android培训.iOS培 ...

  3. 原生js螺旋运动

    window.onload=function(){ var oSpiral=document.getElementById('spiral'); var oUl=oSpiral.getElements ...

  4. windows2003 IIS6下安装ISAPI_Rewrite3破解版

    摘抄的https://jingyan.baidu.com/article/ff42efa931a2c0c19e220298.html 非常感谢,我是怕百度经验有一天消失了,以防万一 iis6 ISAP ...

  5. docker 在运行 aspnetcore 镜像时提示 命令找不到

    不要讲路径映射到镜像中的应用根文件夹 例如默认的 /app

  6. 【32.22%】【codeforces 602B】Approximating a Constant Range

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

  7. Android 使用binder访问service的方式

    binder机制是贯穿整个Android系统的进程间访问机制,经常被用来访问service,我们结合代码看一下binder在访问service的情形下是怎么具体使用的. service 你可以理解成没 ...

  8. js进阶正则表达式15验证身份证号(|符号的使用:var reg=/^\d{17}[\d|X]$|^\d{15}$/)(str的方法substr)

    js进阶正则表达式15验证身份证号(|符号的使用:var reg=/^\d{17}[\d|X]$|^\d{15}$/)(str的方法substr) 一.总结 1.|符号的使用:var reg=/^\d ...

  9. JS中的JSON对象 定义和取值

    1.JSON(JavaScript Object Notation)一种简单的数据格式,比xml更轻巧.JSON是JavaScript原生格式,这意味着在JavaScript中处理JSON数据不需要任 ...

  10. 使用AJAX实现页面跳转

    $.ajax({ type:"POST", url: //你的请求程序页面随便啦 async:false,//同步:意思是当有返回值以后才会进行后面的js程序. data://请求 ...