首先来一发图
绘制XY的坐标主要是利用Canvas setLeft和setBottom功能(Canvas内置坐标的功能)
1.首先WPF中的坐标系都是从左到右,从上到下的 即左上角位置(0,0)点,所以XY的Canvas要以(RenderTransformOrigin="0,0",为中心点)进行270°旋转,然后平移<TranslateTransform Y="{Binding ActualHeight,ElementName=canvasInPath}"/>
就是如上所图的XY坐标(绿色的)Line
不旋转的图如下:
<Canvas x:Name="canvasInPath" RenderTransformOrigin="0,0"> <Canvas.RenderTransform> <TransformGroup> <ScaleTransform/> <SkewTransform/> <RotateTransform Angle="270"/> <TranslateTransform Y="{Binding ActualHeight,ElementName=canvasInPath}"/> </TransformGroup> </Canvas.RenderTransform> <!--线和点--> <Canvas x:Name="canvasLinePoint"></Canvas> <!--Y--> <Line X1="0" X2="0" Y1="0" Y2="{Binding ActualWidth,ElementName=canvasInPath}" Stroke="Green" StrokeThickness="1" Width="1" ></Line> <!--X--> <Line X1="0" X2="{Binding ActualHeight,ElementName=canvasInPath}" Y1="0" Y2="0" Stroke="Green" StrokeThickness="1" Height="1" Canvas.Top="0" ></Line> </Canvas>
2.如果以XY的Canvas要以(RenderTransformOrigin="0.5,0.5",为中心点)旋转,如果Canvas是正方形,那么只需要旋转270可以了,如果是长方形那么就会出现如下图情况:
3.因为Canvas是旋转的,X和Y的网格线就是蓝色的线,就不在旋转的Canvas中进行画线了(注:在旋转后的Canvas再放置控件都要旋转才能正常)
跟Canvas同一个级别放置两个X和Y网格线的Canvas
Line和TextBlock如何画,看上面的测试代码,然后转换成Code,动态绘制出来。
4.如果按照Canvas 100X100的坐标系绘制出来的图像特别密集下图:
所以我对此做了一个原始坐标和实际绘制坐标进行相应的扩大倍数计算,
/// <summary> /// 宽度 /// </summary> public double XWidth { get { return _xWidth; } set { _xWidth = value; this.Width = value; //预留100的line长度 scaleNumX = (value - xyShorten) / scaleStandard / (xTotal/ scaleStandard); }
当前宽度-预留线的长度/基础倍数/(标尺总值/基础倍数),假如当前宽度是x=700,预留100宽度,基础倍数100,x标尺总刻度是200
那么计算出的scaleNumX=(700-100)/100/(200/100)=3
同理计算出 scaleNumY=(500-100)/100/(100/100)=4 (y=500 预留100 基础倍数100 y标尺总刻度是100)
原始坐标 (20,90)=>真实绘制坐标(60,360) x*scaleNumX,y*scaleNumY 下图:
完整的xaml代码如下:
<UserControl x:Class="CoordinateXY.UserControlXY" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:CoordinateXY" mc:Ignorable="d" x:Name="ucontrol" d:DesignHeight="600" d:DesignWidth="600"> <Grid Background="Wheat"> <Canvas x:Name="canvasInPath" RenderTransformOrigin="0,0"> <Canvas.RenderTransform> <TransformGroup> <ScaleTransform/> <SkewTransform/> <RotateTransform Angle="270"/> <TranslateTransform Y="{Binding ActualHeight,ElementName=canvasInPath}"/> </TransformGroup> </Canvas.RenderTransform> <!--线和点--> <Canvas x:Name="canvasLinePoint"></Canvas> <!--Y--> <Line X1="0" X2="0" Y1="0" Y2="{Binding ActualWidth,ElementName=canvasInPath}" Stroke="Green" StrokeThickness="1" Width="1" ></Line> <!--X--> <Line X1="0" X2="{Binding ActualHeight,ElementName=canvasInPath}" Y1="0" Y2="0" Stroke="Green" StrokeThickness="1" Height="1" Canvas.Top="0" ></Line> </Canvas> <!--X坐标尺度--> <Canvas x:Name="canvasXRuler" Visibility="Visible" Panel.ZIndex="-1"> <!--以下为测试代码--> <Line X1="0" X2="0" Y1="0" Y2="260" Stroke="Blue" StrokeThickness="1" Canvas.Bottom="-8" Canvas.Left="40"></Line> <TextBlock Text="12" RenderTransformOrigin="0,0" Canvas.Bottom="-25" Canvas.Left="32"> </TextBlock> <Line X1="0" X2="0" Y1="0" Y2="260" Stroke="Blue" StrokeThickness="1" Canvas.Bottom="-8" Canvas.Left="50"></Line> <TextBlock Text="45" RenderTransformOrigin="0,0" Canvas.Bottom="-25" Canvas.Left="42"> </TextBlock> <Ellipse Width="2" Height="2" Canvas.Left="49" Canvas.Top="49" Stroke="Red" StrokeThickness="1"></Ellipse> </Canvas> <!--Y坐标尺度--> <Canvas x:Name="canvasYRuler" Panel.ZIndex="-1"> <!--以下为测试代码--> <Line X1="0" X2="260" Y1="0" Y2="0" Stroke="Blue" StrokeThickness="1" Canvas.Bottom="10" Canvas.Left="-8"></Line> <TextBlock Text="Y2" RenderTransformOrigin="0,0" Canvas.Bottom="2" Canvas.Left="-25"> </TextBlock> <Line X1="0" X2="260" Y1="0" Y2="0" Stroke="Blue" StrokeThickness="1" Canvas.Bottom="20" Canvas.Left="-8"></Line> <TextBlock Text="Y1" RenderTransformOrigin="0,0" Canvas.Bottom="12" Canvas.Left="-25"> </TextBlock> </Canvas> </Grid> </UserControl>
完整的Code代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace CoordinateXY { /// <summary> /// UserControlXY.xaml 的交互逻辑 /// </summary> public partial class UserControlXY : UserControl { public UserControlXY() { InitializeComponent(); this.Loaded += UserControlXY_Loaded; } private void UserControlXY_Loaded(object sender, RoutedEventArgs e) { InitXRuler(); InitYRuler(); } #region 变量 /// <summary> /// 放大倍数 防止坐标尺子重叠 /// </summary> private static double scaleNumX = 0; /// <summary> /// 放大倍数 防止坐标尺子重叠 /// </summary> private static double scaleNumY = 0; /// <summary> /// 按照宽度和高度计算放大倍数 /// </summary> private double scaleStandard = 50; /// <summary> /// x坐标尺度 /// </summary> private double xTotal = 150; /// <summary> /// Y坐标尺度 /// </summary> private double yTotal = 300; /// <summary> /// 刻度间隔 10刻度显示一个网格线 /// </summary> private double scaleInterval = 10; /// <summary> /// 网格刻度线延长出来的长度值 /// 修改此长度看效果图 /// </summary> private int xyLine = 0; /// <summary> /// xy坐标线长比网格绘制长度长多少 /// </summary> private int xyShorten = 50; /// <summary> /// 文本距离xy坐标线的位置 /// </summary> private int txtDis = 20; /// <summary> /// 宽度 /// </summary> private double _xWidth; /// <summary> /// 高度 /// </summary> private double _yHeight; /// <summary> /// 高度 /// </summary> public double YHeight { get { return _yHeight; } set { _yHeight = value; this.Height = value; //预留100的line长度 scaleNumY = (value - xyShorten) / scaleStandard / (yTotal / scaleStandard); } } /// <summary> /// 宽度 /// </summary> public double XWidth { get { return _xWidth; } set { _xWidth = value; this.Width = value; //预留100的line长度 scaleNumX = (value - xyShorten) / scaleStandard / (xTotal / scaleStandard); } } #endregion #region 方法 /// <summary> /// 初始化X坐标尺 /// </summary> private void InitXRuler() { canvasXRuler.Children.Clear(); var xtotal = xTotal + 1; for (int i = 1; i < xtotal; i++) { if (i % scaleInterval != 0 && i + 1 != xtotal) { continue; } Line xLine = new Line(); xLine.X1 = 1; xLine.X2 = 0; xLine.Y1 = 0; xLine.Y2 = this.Height - xyShorten + xyLine;//柱状线图形高度; xLine.Stroke = new SolidColorBrush(Color.FromRgb(0, 0, 255));//蓝色 xLine.StrokeThickness = 1; xLine.IsHitTestVisible = false; Canvas.SetLeft(xLine, i * scaleNumX); Canvas.SetBottom(xLine, -xyLine);//延迟8长度刻度 TextBlock txtBlock = new TextBlock(); txtBlock.Text = (i).ToString();//文本内容 var typeface = new Typeface(txtBlock.FontFamily, txtBlock.FontStyle, txtBlock.FontWeight, txtBlock.FontStretch); var width = Commons.Helper.TrimmingHelper.GetControlWidth(txtBlock.Text, typeface, txtBlock.FontSize); Canvas.SetLeft(txtBlock, i * scaleNumX - width / 2);//计算文本宽度 使text内容center居中 Canvas.SetBottom(txtBlock, -txtDis);//刻度下方文本 canvasXRuler.Children.Add(xLine); canvasXRuler.Children.Add(txtBlock); } } /// <summary> /// 初始化Y坐标尺 /// </summary> private void InitYRuler() { canvasYRuler.Children.Clear(); var ytotal = yTotal + 1; for (int i = 1; i < ytotal; i++) { if (i % scaleInterval != 0 && i + 1 != ytotal) { continue; } Line yLine = new Line(); yLine.X1 = 1; yLine.X2 = this.Width - xyShorten + xyLine;//柱状线图形长度; yLine.Y1 = 0; yLine.Y2 = 0; yLine.Stroke = new SolidColorBrush(Color.FromRgb(0, 0, 255));//蓝色 yLine.StrokeThickness = 1; yLine.IsHitTestVisible = false; Canvas.SetLeft(yLine, -xyLine);//刻度值 Canvas.SetBottom(yLine, i * scaleNumY); TextBlock txtBlock = new TextBlock(); txtBlock.Text = (i).ToString();//文本内容 Canvas.SetRight(txtBlock, this.Width + 8); Canvas.SetBottom(txtBlock, i * scaleNumY - 8);//高度平移8文本内容上下对齐线 canvasXRuler.Children.Add(yLine); canvasXRuler.Children.Add(txtBlock); } } private static UserControlXY uControlXY; /// <summary> /// 创建点的位置 /// </summary> /// <param name="point"></param> void InCanvasPoint(Point point) { var temp = CreatePointEllipse(); //temp.ToolTip = point.X / scaleNumX + "," + point.Y / scaleNumY; temp.ToolTip = point.Y / scaleNumX + "," + point.X / scaleNumY + " " + "(" + point.Y + "," + point.X + ")"; uControlXY.canvasLinePoint.Children.Add(temp); Panel.SetZIndex(temp, 100); Canvas.SetLeft(temp, point.X - temp.Height / 2); Canvas.SetTop(temp, point.Y - temp.Width / 2); } /// <summary> /// 创建Point /// </summary> void CreatePoint(List<Point> itemList) { if (itemList != null && itemList.Count > 0) { for (int i = 0; i < itemList.Count; i++) { var startPoint = itemList[i]; var tmpPoint = ConvertPoint(startPoint); InCanvasPoint(tmpPoint); if (i + 1 == itemList.Count) { break; } var endPoint = itemList[i + 1]; var tmpEndPoint = ConvertPoint(endPoint); CreateLine(tmpPoint, tmpEndPoint); } } } /// <summary> /// 创建连接的直线 /// </summary> /// <param name="startPoint"></param> /// <param name="endPoint"></param> void CreateLine(Point startPoint, Point endPoint) { PathGeometry pg = new PathGeometry();//组合绘制的线段 Path pa = new Path();//绘制轨迹曲线的容器,用于显示 pa.Stroke = new SolidColorBrush(Color.FromRgb(0, 0, 0)); pa.StrokeThickness = 1; PathFigure pf = new PathFigure(); pf.StartPoint = startPoint; LineSegment line = new LineSegment(); line.Point = endPoint; pf.Segments.Add(line); pg.Figures.Add(pf); pa.Data = pg; uControlXY.canvasLinePoint.Children.Add(pa); } /// <summary> /// 创建弧线 /// </summary> void CreateArcLine(Tuple<Point, Point, double> data) { if (data == null) { return; } Point startPoint = ConvertPoint(data.Item1); Point endPoint = ConvertPoint(data.Item2); CreateLine(startPoint, endPoint); PathGeometry pg = new PathGeometry();//组合绘制的线段 Path pa = new Path();//绘制轨迹曲线的容器,用于显示 pa.ToolTip = data.Item1 + " " + data.Item2; //pa.Fill = new SolidColorBrush(Color.FromRgb(255, 0, 0)); pa.Stroke = new SolidColorBrush(Color.FromRgb(255, 0, 0)); pa.StrokeThickness = 1; PathFigure pf = new PathFigure(); pf.StartPoint = startPoint; ArcSegment line = new ArcSegment(); line.SweepDirection = SweepDirection.Clockwise;//顺时针弧 line.Point = endPoint; //半径 正弦定理a/sinA=2r r=a/2sinA 其中a指的是两个城市点之间的距离 角A指a边的对角 double sinA = Math.Sin(Math.PI * data.Item3 / 180.0); //计算距离 勾股定理 double x = startPoint.X - endPoint.X; double y = startPoint.Y - endPoint.Y; double aa = x * x + y * y; double l = Math.Sqrt(aa); double r = l / (sinA * 2); line.Size = new Size(r, r); pf.Segments.Add(line); pg.Figures.Add(pf); pa.Data = pg; uControlXY.canvasLinePoint.Children.Add(pa); } /// <summary> /// 把坐标转换为绘画坐标 /// </summary> /// <param name="point"></param> /// <returns></returns> Point ConvertPoint(Point point) { var tmpPoint = new Point(); tmpPoint.X = point.Y * scaleNumY; tmpPoint.Y = point.X * scaleNumX; return tmpPoint; } /// <summary> /// 创建圆点 /// </summary> /// <returns></returns> Ellipse CreatePointEllipse() { Ellipse ell = new Ellipse(); ell.Stroke = new SolidColorBrush(Color.FromRgb(255, 0, 0)); ell.Fill = new SolidColorBrush(Color.FromRgb(255, 0, 0)); ell.Height = 8; ell.Width = 8; return ell; } public void Refresh(List<Point> _itemsSource) { canvasLinePoint.Children.Clear(); CreatePoint(_itemsSource); InitXRuler(); InitYRuler(); } public void ClearLine() { this.canvasLinePoint.Children.Clear(); } #endregion #region Customer DependencyObject /// <summary> /// 求两点之间的弧线 /// item1 开始坐标 item2 结束坐标 item3 弧度值 /// </summary> public Tuple<Point, Point, double> PointArc { get { return (Tuple<Point, Point, double>)GetValue(PointArcProperty); } set { SetValue(PointArcProperty, value); } } public List<Point> ItemsSource { get { return (List<Point>)GetValue(ItemsSourceProperty); } set { SetValue(ItemsSourceProperty, value); } } // Using a DependencyProperty as the backing store for ItemsSource. This enables animation, styling, binding, etc... public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(List<Point>), typeof(UserControlXY), new PropertyMetadata(null, new PropertyChangedCallback(OnItemsSourceChangedCallback))); public static void OnItemsSourceChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (e.NewValue != null) { uControlXY = d as UserControlXY; uControlXY.CreatePoint(e.NewValue as List<Point>); } } // Using a DependencyProperty as the backing store for PointArc. This enables animation, styling, binding, etc... public static readonly DependencyProperty PointArcProperty = DependencyProperty.Register("PointArc", typeof(Tuple<Point, Point, double>), typeof(UserControlXY), new PropertyMetadata(null, new PropertyChangedCallback(OnPointArcChangedCallback))); public static void OnPointArcChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (e.NewValue != null) { uControlXY = d as UserControlXY; uControlXY.CreateArcLine(e.NewValue as Tuple<Point, Point, double>); } } #endregion } }
帮助类
/*********************************************************************** * Copyright(c) 2016-2050 ligl * CLR 版本: 4.0.30319.42000 * 文 件 名:TrimmingHelper * 创 建 人:ligl * 创建日期:2016/7/14 21:05:16 * 修 改 人:ligl * 修改日期: * 备注描述: ************************************************************************/ using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Text; using System.Windows.Media; namespace Commons.Helper { /// <summary> /// 计算长度是否超出文本宽度的帮助类 /// </summary> public class TrimmingHelper { /// <summary> /// /// </summary> /// <param name="source">原始文本</param> /// <param name="suffix">省略文本符号</param> /// <param name="endNoTrimSource">追加省略号后面的文本,source+endNoTrimSource总体长度计算省略号</param> /// <param name="width">文本长度</param> /// <param name="face">字体类</param> /// <param name="fontsize">字体大小</param> /// <param name="ShowTip">True标示截取了文本</param> /// <returns></returns> public static string Trim(string source, string suffix, string endNoTrimSource, double width, Typeface face, double fontsize, ref bool ShowTip) { if (face != null) { //real display max width. double realWidth = width; //try to get GlyphTypeface. GlyphTypeface glyphTypeface; face.TryGetGlyphTypeface(out glyphTypeface); if (glyphTypeface != null) { //calculate end string 's display width. if (!string.IsNullOrEmpty(endNoTrimSource)) { double notrimWidth = 0; foreach (char c in endNoTrimSource) { ushort w; glyphTypeface.CharacterToGlyphMap.TryGetValue(c, out w); notrimWidth += glyphTypeface.AdvanceWidths[w] * fontsize; } realWidth = width - notrimWidth; } //calculate source 's screen width double sourceWidth = 0; if (!string.IsNullOrEmpty(source)) { foreach (char c in source) { ushort w; glyphTypeface.CharacterToGlyphMap.TryGetValue(c, out w); sourceWidth += glyphTypeface.AdvanceWidths[w] * fontsize; } } //don't need to trim. if (sourceWidth <= realWidth) return source + endNoTrimSource; //calculate suffix's display width double suffixWidth = 0; if (!string.IsNullOrEmpty(suffix)) { foreach (char c in suffix) { ushort w; glyphTypeface.CharacterToGlyphMap.TryGetValue(c, out w); suffixWidth += glyphTypeface.AdvanceWidths[w] * fontsize; } } realWidth = realWidth - suffixWidth; if (realWidth > 0) { sourceWidth = 0; string trimStr = string.Empty; foreach (char c in source) { ushort w; glyphTypeface.CharacterToGlyphMap.TryGetValue(c, out w); double cWidth = glyphTypeface.AdvanceWidths[w] * fontsize; if ((sourceWidth + cWidth) > realWidth) { ShowTip = true; return trimStr + suffix + endNoTrimSource; } trimStr += c; sourceWidth += cWidth; } } else { ShowTip = true; if (width > suffixWidth) return suffix; else return "..."; } } } ShowTip = false; return source + endNoTrimSource; } /// <summary> /// 获取文本内容宽度的方法 /// </summary> /// <param name="source"></param> /// <param name="face"></param> /// <param name="fontsize"></param> /// <returns></returns> public static double GetControlWidth(string source, Typeface face, double fontsize) { double realWidth = 0; if (face != null) { //try to get GlyphTypeface. GlyphTypeface glyphTypeface; face.TryGetGlyphTypeface(out glyphTypeface); if (glyphTypeface != null) { //calculate source 's screen width if (!string.IsNullOrEmpty(source)) { foreach (char c in source) { ushort w; glyphTypeface.CharacterToGlyphMap.TryGetValue(c, out w); realWidth += glyphTypeface.AdvanceWidths[w] * fontsize; } } } } return realWidth; } } }
使用方式:
Xaml
<UserControl x:Class="CoordinateXY.UserControlShow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:CoordinateXY" mc:Ignorable="d"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="30" /> <RowDefinition Height="30" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <Grid> <DockPanel VerticalAlignment="Center"> <TextBox x:Name="txtboxWH" Text="600,600" Width="60"></TextBox> <Button Content="设置宽度和高度" Width="120" Margin="10 0 0 0" VerticalAlignment="Center" HorizontalAlignment="Left" Click="BtnRefresh_Click"></Button> <TextBlock Text="开始坐标:" Margin="5 0" VerticalAlignment="Center"></TextBlock> <TextBox x:Name="txtboxArcSpoint" Text="10,10" Width="60" Margin="5 0"></TextBox> <TextBlock Text="结束坐标:" Margin="5 0" VerticalAlignment="Center"></TextBlock> <TextBox x:Name="txtboxArcEpoint" Text="90,90" Width="60" Margin="5 0"></TextBox> <TextBlock Text="弧度值:" Margin="5 0" VerticalAlignment="Center"></TextBlock> <TextBox x:Name="txtboxAngle" Text="80" Width="60" Margin="5 0"></TextBox> <Button Content="设置弧线坐标" Width="120" Margin="5 0 0 0" VerticalAlignment="Center" HorizontalAlignment="Left" Click="BtnArc_Click"></Button> <Button Content="Clear所有线" Width="120" Margin="5 0 0 0" VerticalAlignment="Center" HorizontalAlignment="Left" Click="BtnClear_Click"></Button> </DockPanel> </Grid> <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="X Y Demos" FontSize="20" Grid.Row="1"/> <Grid Grid.Row="2" Background="DarkOrange"> <Viewbox> <local:UserControlXY x:Name="uControlXY" XWidth="600" YHeight="600" ItemsSource="{Binding XyList,Mode=TwoWay}" PointArc="{Binding ArcData}" Margin="20"></local:UserControlXY> </Viewbox> </Grid> </Grid> </UserControl>
Code
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.ComponentModel; namespace CoordinateXY { /// <summary> /// UserControlShow.xaml 的交互逻辑 /// </summary> public partial class UserControlShow : UserControl { ViewMode vModel = new ViewMode(); public UserControlShow() { InitializeComponent(); this.DataContext = vModel; } private void BtnRefresh_Click(object sender, RoutedEventArgs e) { //uControlXY.Width = uControlXY.Width * 1.1; //uControlXY.Height = uControlXY.Height * 1.1; var txt = txtboxWH.Text.Trim(); string[] whs = txt.Split(','); if (whs.Length != 2) { return; } double w; double h; double.TryParse(whs[0], out w); double.TryParse(whs[1], out h); if (w != 0 && h != 0) { this.uControlXY.XWidth = w; this.uControlXY.YHeight = h; this.uControlXY.Refresh(vModel.XyList); } } private void BtnArc_Click(object sender, RoutedEventArgs e) { var spoints = txtboxArcSpoint.Text.Trim().Split(','); if (spoints.Length != 2) { return; } Point startPoint = new Point(); double sX; double sY; double.TryParse(spoints[0], out sX); double.TryParse(spoints[1], out sY); startPoint.X = sX; startPoint.Y = sY; var epoints = txtboxArcEpoint.Text.Trim().Split(','); if (epoints.Length != 2) { return; } Point endPoint = new Point(); double eX; double eY; double.TryParse(epoints[0], out eX); double.TryParse(epoints[1], out eY); endPoint.X = eX; endPoint.Y = eY; var angletxt = txtboxAngle.Text.Trim(); double angle; double.TryParse(angletxt, out angle); vModel.ArcData = new Tuple<Point, Point, double>(startPoint, endPoint, angle); } private void BtnClear_Click(object sender, RoutedEventArgs e) { uControlXY.ClearLine(); } } public class ViewMode : INotifyPropertyChanged { public ViewMode() { _xyList = new List<Point>(); XyList.Add(new Point(10, 10)); XyList.Add(new Point(40, 50)); XyList.Add(new Point(30, 40)); XyList.Add(new Point(90, 10)); XyList.Add(new Point(20, 90)); XyList.Add(new Point(45.5, 73.2)); XyList.Add(new Point(140, 235)); _arcData = new Tuple<Point, Point, double>(new Point(50, 50), new Point(80, 90), 60); } private Tuple<Point, Point, double> _arcData; private List<Point> _xyList; public event PropertyChangedEventHandler PropertyChanged; public void OnPropertyChanged(PropertyChangedEventArgs e) { if (PropertyChanged != null) { PropertyChanged(this, e); } } public List<Point> XyList { get { return _xyList; } set { _xyList = value; OnPropertyChanged(new PropertyChangedEventArgs("XyList")); } } /// <summary> /// 弧线构成数据 /// </summary> public Tuple<Point, Point, double> ArcData { get { return _arcData; } set { _arcData = value; OnPropertyChanged(new PropertyChangedEventArgs("ArcData")); } } } }