利用Canvas进行绘制XY坐标系

首先来一发图

绘制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();
        }

        /// <summary>
        /// 放大倍数 防止坐标尺子重叠
        /// </summary>
        private static double scaleNumX = 5;

        /// <summary>
        /// 放大倍数 防止坐标尺子重叠
        /// </summary>
        private static double scaleNumY = 5;

        /// <summary>
        /// 按照宽度和高度计算放大倍数
        /// </summary>
        private double scaleStandard = 40;

        /// <summary>
        /// x坐标尺度
        /// </summary>
        private double xTotal = 200;

        /// <summary>
        /// Y坐标尺度
        /// </summary>
        private double yTotal = 126;

        /// <summary>
        ///  刻度间隔 10刻度显示一个网格线
        /// </summary>
        private double scaleInterval = 10;

        /// <summary>
        /// 网格刻度线延长出来的长度值
        /// 修改此长度看效果图
        /// </summary>
        private int xyLine = 0;

        /// <summary>
        /// xy坐标线长比网格绘制长度长多少
        /// </summary>
        private int xyShorten = 40;

        /// <summary>
        /// 文本距离xy坐标线的位置
        /// </summary>
        private int txtDis = 20;

        /// <summary>
        /// 宽度
        /// </summary>
        private double _xWidth;

        /// <summary>
        /// 高度
        /// </summary>
        private double _yHeight;

        /// <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();//文本内容
                Canvas.SetLeft(txtBlock, i * scaleNumX - 8);//两位数的文本平移8 让文本居中显示
                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.SetLeft(txtBlock, -txtDis - 2);
                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>
        static 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>
        static 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 = new Point();
                    tmpPoint.X = startPoint.Y * scaleNumY;
                    tmpPoint.Y = startPoint.X * scaleNumX;
                    InCanvasPoint(tmpPoint);
                    if (i + 1 == itemList.Count)
                    {
                        break;
                    }
                    var endPoint = itemList[i + 1];
                    var tmpEndPoint = new Point();
                    tmpEndPoint.X = endPoint.Y * scaleNumY;
                    tmpEndPoint.Y = endPoint.X * scaleNumX;
                    CreateLine(tmpPoint, tmpEndPoint);
                }
            }
        }

        /// <summary>
        /// 创建连接的直线
        /// </summary>
        /// <param name="startPoint"></param>
        /// <param name="endPoint"></param>
        static 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>
        /// <returns></returns>
        static 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 List<Point> ItemsSource
        {
            get { return (List<Point>)GetValue(ItemsSourceProperty); }
            set { SetValue(ItemsSourceProperty, value); }
        }

        /// <summary>
        /// 宽度
        /// </summary>
        public double XWidth
        {
            get
            {
                return _xWidth;
            }

            set
            {
                _xWidth = value;
                this.Width = value;
                //预留100的line长度
                scaleNumX = (value - xyShorten) / scaleStandard / (xTotal/ scaleStandard);
            }
        }

        /// <summary>
        /// 求两点之间的弧线
        /// item1 开始坐标 item2 结束坐标 item3 弧度值
        /// </summary>
        public Tuple<Point, Point, double> PointArc
        {
            get { return (Tuple<Point, Point, double>)GetValue(PointArcProperty); }
            set { SetValue(PointArcProperty, value); }
        }

        /// <summary>
        /// 高度
        /// </summary>
        public double YHeight
        {
            get
            {
                return _yHeight;
            }

            set
            {
                _yHeight = value;
                this.Height = value;
                //预留100的line长度
                scaleNumY = (value - xyShorten) / scaleStandard / (yTotal / scaleStandard);
            }
        }

        public void Refresh(List<Point> _itemsSource)
        {
            canvasLinePoint.Children.Clear();
            CreatePoint(_itemsSource);
            InitXRuler();
            InitYRuler();

        }

        // 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;
                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));

    }
}

使用方式:

<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="34" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Grid>
            <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="X Y Demos"  FontSize="20"/>
            <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>
            </DockPanel>

        </Grid>
        <Grid Grid.Row="2" Background="DarkOrange">
            <Viewbox>
                <local:UserControlXY  x:Name="uControlXY"   XWidth="600"  YHeight="600" ItemsSource="{Binding XyList,Mode=TwoWay}" Margin="20"></local:UserControlXY>
            </Viewbox>

        </Grid>
    </Grid>
</UserControl>

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);

            }
        }

    }

    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));

        }

        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"));
            }
        }

    }
}

下载源代码

此代码textblock文本显示的地方存在一定的问题,就是文本字数不确定性,x坐标Center对齐有问题,y坐标文本会向右(仔细看y=100那个标尺,如果y=1000就会跑到绿色线里面去了),求大神们给个思路。

时间: 2024-09-28 16:04:06

利用Canvas进行绘制XY坐标系的相关文章

利用canvas绘制图形

绘制图有很多种方法,可以借助flash实现,也可以使用SVG和VML来绘图.本章将要学习一种新的绘图方法--使用Canvas元素,它是基于HTML5原生的绘图功能.使用Canvas元素,可以绘制图形,也可以实现动画.它方便了使用Javascript脚本的前端开发人员,寥寥的竖行代码,就可以在Canvas元素中实现各种图形及动画.本章将介绍如何使用Canvas元素来绘制一些简单的图形.本章主要知识点如下:·认识Canvas元素·使用Canvas绘图·Canvas与JavaScript之间的互动·利

10分钟,利用canvas画一个小的loading界面(顺便讨论下绘制效率问题)

首先利用定义下canvas得样式 <canvas width="1024" height="720" id="canvas" style="border: 1px solid #808080;display: block;margin: 100px auto;>你的游览器不支持canvas</canvas> 这里主要要说的就是宽高,不要在style里面定义,不然会被拉伸.(对于这点,建议大家看下W3c文档,不是很

C#WPF 如何绘制几何图形 图示教程 绘制sin曲线 正弦 绘制2D坐标系 有图有代码

C#WPF 如何绘制几何图形? 怎么绘制坐标系? 这离不开Path(System.Windows.Shapes)和StreamGeometry(System.Windows.Media)类. 一.建立WPF工程 二.添加代码 MainWindow.xaml 中代码 <Window x:Class="WPFDrawingTraning.MainWindow"         xmlns="<a target=_blank href="http://sche

安卓自己定义View进阶-Canvas之绘制基本形状

Canvas之绘制基本形状 作者微博: @GcsSloop [本系列相关文章] 在上一篇自己定义View分类与流程中我们了解自己定义View相关的基本知识,只是,这些东西依然还是理论,并不能拿来(zhuang)用(B), 这一次我们就了解一些能(zhaung)用(B)的东西. 在本篇文章中,我们先了解Canvas的基本用法,最后用一个小演示样例来结束本次教程. 一.Canvas简单介绍 Canvas我们能够称之为画布,能够在上面绘制各种东西,是安卓平台2D图形绘制的基础,非常强大. **一般来说

Android利用canvas画各种图形

canvas通俗的说就是一张画布,我们可以使用画笔paint,在其上面画任意的图形. 原理: 可以把canvas视为Surface的替身或者接口,图形便是绘制在Surface上的.Canvas封装了所有的绘制调用.通过Canvas, 绘制到Surface上的内容首先存储到一个内存区域(也就是对应的Bitmapz中),该Bitmap最终会呈现到窗口上. 使用: 1.Canvas可以直接new Canvas(): 2.在View中重写OnDraw()方法,里面有一个Canvas,今天讨论的内容. 方

利用canvas制作一个时钟

先上张效果图. 利用canvas来画出一个时钟,想想都是一件神奇又美妙的事情.话不多说,先上代码吧. <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-sca

10分钟,利用canvas画一个小的loading界面

首先利用定义下canvas得样式 <canvas width="1024" height="720" id="canvas" style="border: 1px solid #808080;display: block;margin: 100px auto;>你的游览器不支持canvas</canvas> 这里主要要说的就是宽高,不要在style里面定义,不然会被拉伸.(对于这点,建议大家看下W3c文档,不是很

canvas之绘制圆形

当利用canvas进行图形绘制时,对于那么些未关闭的图形,默认会将其关闭,一下实现了如何绘制未关闭图形,以及如何给区域内填充色彩等常用的方法. 以下是腻子,复制粘贴至一格式为HTML的文档即可用: <canvas id="canvas"style="border:5px solid #ccc;display: block;margin:0 auto;"></canvas> 1 <script type="text/javasc

HTML5在canvas中绘制复杂形状附效果截图

HTML5在canvas中绘制复杂形状附效果截图 一.绘制复杂形状或路径 在简单的矩形不能满足需求的情况下,绘图环境提供了如下方法来绘制复杂的形状或路径. beginPath() : 开始绘制一个新路径. closePath() : 通过绘制一条当前点到路径起点的线段来闭合形状. fill() , stroke() : 填充形状或绘制空心形状. moveTo() : 将当前点移动到点(x,y). lineTo() : 从当前点绘制一条直线到点(x,y). arc(x,y,r,sAngle,eAn