使用 WPF 和 C# 绘制图形

news2025/1/13 14:01:45

绘图困难

在 WPF 和 C# 中绘制图形

此示例展示了如何在 WPF 和 C# 中绘制图形。绘制图形总是很棘手,因为您通常需要在至少两个不同的坐标系中工作。首先,您要为图形使用世界坐标。例如,您可能希望 X 值的范围为 2000 年至 2020 年,Y 值的范围为 10,000 美元至 100,000 美元之间的销售额值。

第二个坐标系是以屏幕上的像素为单位测量的 设备坐标系。

显然,在绘制图形本身之类的东西时,您需要使用世界坐标系。最棘手的部分发生在您需要在世界坐标中定位某些东西但在设备坐标中绘制时。例如,假设您要绘制带有 5 个像素长的刻度标记的 X 轴和 Y 轴。您使用世界坐标来确定刻度标记应放置在何处,但随后您需要计算设备坐标中刻度标记的长度(以像素为单位)。

类似地,假设您想在图表上绘制一些文本来标记某些内容。您将文本定位在世界坐标中,但您可能希望在设备坐标中绘制文本。否则很难将文本居中并对齐。

我要提到的最后一个怪异问题是让图形的线条具有一致的粗细。假设您在某个规范化的空间中绘制图形,然后缩放它以适合设备区域。例如,您在世界坐标空间 2000 <= x <= 2020、$10,000 <= y <= $100,000 中绘制图形,然后使用 LayoutTransform使图形适合Canvas控件。当变换拉伸图形时。它还会拉伸您为图形绘制的线条。除非垂直和水平比例因子相同,否则线条在垂直和水平方向上的拉伸量会不同。文本也会被拉伸,从而产生一些非常烦人的结果。

无论如何,要真正将所有东西都准确地放置在您想要的位置,您需要能够在世界坐标和设备坐标中自由工作。这篇文章是一系列简短文章的开篇,这些文章探讨了您可以用来在 WPF 和 C# 中绘制图形的技术。

一个简单的图表

此示例仅使用设备坐标绘制了一个简单的图形。换句话说,所有位置都以像素为单位测量,左上角为 (0, 0)。以下帖子将展示如何在更方便的世界坐标中工作。

以下代码显示了构建该程序的 XAML。

<Window x:Class="howto_wpf_graph.Window1"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Title="howto_wpf_graph"
  Height="250" Width="335" Loaded="Window_Loaded">
  <Grid Background="LightGreen">
    <Canvas Name="canGraph" Background="White"
      Width="300" Height="200"
      VerticalAlignment="Center" HorizontalAlignment="Center"/>
  </Grid>
</Window>

该窗口的主要子窗口是Grid,其中包含一个名为canGraph的Canvas

在 WPF 中,您通常不会直接在绘图表面上绘图。如果确实需要,您可以这样做,但通常使用LineEllipseRectangle和其他形状控件进行绘制。如果愿意,您可以将这些对象包含在 XAML 代码中,但如果您要绘制非平凡图形,则需要使用代码来完成。

当此示例启动时,以下事件处理程序将构建图形。(请注意窗口的 XAML 声明中的Loaded="Window_Loaded"部分。这告诉程序Window_Loaded方法是窗口的Loaded事件的事件处理程序。)

// Draw a simple graph.
private void Window_Loaded(object sender, RoutedEventArgs e)
{
    const double margin = 10;
    double xmin = margin;
    double xmax = canGraph.Width - margin;
    double ymin = margin;
    double ymax = canGraph.Height - margin;
    const double step = 10;

    // Make the X axis.
    GeometryGroup xaxis_geom = new GeometryGroup();
    xaxis_geom.Children.Add(new LineGeometry(
        new Point(0, ymax), new Point(canGraph.Width, ymax)));
    for (double x = xmin + step;
        x <= canGraph.Width - step; x += step)
    {
        xaxis_geom.Children.Add(new LineGeometry(
            new Point(x, ymax - margin / 2),
            new Point(x, ymax + margin / 2)));
    }

    Path xaxis_path = new Path();
    xaxis_path.StrokeThickness = 1;
    xaxis_path.Stroke = Brushes.Black;
    xaxis_path.Data = xaxis_geom;
    
    canGraph.Children.Add(xaxis_path);

    // Make the Y ayis.
    GeometryGroup yaxis_geom = new GeometryGroup();
    yaxis_geom.Children.Add(new LineGeometry(
        new Point(xmin, 0), new Point(xmin, canGraph.Height)));
    for (double y = step; y <= canGraph.Height - step; y += step)
    {
        yaxis_geom.Children.Add(new LineGeometry(
            new Point(xmin - margin / 2, y),
            new Point(xmin + margin / 2, y)));
    }

    Path yaxis_path = new Path();
    yaxis_path.StrokeThickness = 1;
    yaxis_path.Stroke = Brushes.Black;
    yaxis_path.Data = yaxis_geom;

    canGraph.Children.Add(yaxis_path);

    // Make some data sets.
    Brush[] brushes = { Brushes.Red, Brushes.Green, Brushes.Blue };
    Random rand = new Random();
    for (int data_set = 0; data_set < 3; data_set++)
    {
        int last_y = rand.Next((int)ymin, (int)ymax);

        PointCollection points = new PointCollection();
        for (double x = xmin; x <= xmax; x += step)
        {
            last_y = rand.Next(last_y - 10, last_y + 10);
            if (last_y < ymin) last_y = (int)ymin;
            if (last_y > ymax) last_y = (int)ymax;
            points.Add(new Point(x, last_y));
        }

        Polyline polyline = new Polyline();
        polyline.StrokeThickness = 1;
        polyline.Stroke = brushes[data_set];
        polyline.Points = points;

        canGraph.Children.Add(polyline);
    }
}

代码首先为图定义一些边界。

接下来,程序创建一个GeometryGroup对象来表示 X 轴。GeometryGroup可以容纳其他几何对象,例如线条。代码创建一个Line表示轴的基线并将其添加到组中。然后,它使用循环创建一组Line对象来表示刻度标记并将它们添加到组中。

在创建完所有轴的Line对象并将其添加到GeometryGroup后,程序将创建一个Path对象并设置其StrokeThicknessStroke属性。然后它将路径的Data属性设置为等于GeometryGroup

最后,代码将路径添加到canGraph对象的Children集合中。

然后代码重复这些步骤来创建 Y 轴。

接下来,代码生成一些图形数据。对于每个数据集,代码都会创建一个PointCollection对象。它会生成一堆随机点并将它们添加到集合中。完成数据生成后,程序会创建一个Polyline,设置其绘制属性,并将其Points属性设置为点集合。最后,代码将Polyline添加到canGraph对象的Children集合中。

这就是程序需要做的全部工作。当窗口出现时,LinePolyline对象会根据需要自行绘制。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2275991.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

年度技术突破奖|中兴微电子引领汽车芯片新变革

随着以中央计算区域控制为代表的新一代整车电子架构逐步成为行业主流&#xff0c;车企在电动化与智能化之后&#xff0c;正迎来以架构创新为核心的新一轮技术竞争。中央计算SoC&#xff0c;作为支撑智驾和智舱高算力需求的核心组件&#xff0c;已成为汽车电子市场的重要新增量。…

【JVM-2.3】深入解析JVisualVM:Java性能监控与调优利器

在Java应用的开发和运维过程中&#xff0c;性能监控与调优是不可或缺的环节。无论是排查内存泄漏、分析CPU瓶颈&#xff0c;还是优化线程使用&#xff0c;开发者都需要借助一些强大的工具来辅助诊断。JVisualVM 正是这样一款由Oracle提供的免费工具&#xff0c;它集成了多种性能…

filestream安装使用全套+filebeat的模块用法

1 filestream介绍 官方宣布&#xff1a;输入类型为log在filebeat7.16版本已经弃用了 Filestream 是 Filebeat 中的一种 输入类型&#xff08;Input&#xff09;&#xff0c;用于处理日志文件的读取。它是为了取代 Filebeat 中传统的 log 输入&#xff08;Input&#xff09;设…

超燃预告!Origin百图绘制系列即将登场

Hello&#xff0c;大家好 这里是练习时长两年半的菜狗~ 持续更新各种竞赛&#xff0c;科研&#xff0c;保研&#xff0c;学习干货ing 回想刚开始打比赛那会&#xff0c;啥都不懂&#xff0c;就从用 Excel 画图起步&#xff0c;绘制的图形实在太难看。后来运用 Matlab&#xf…

八、系统托盘与配置面板

没有人会把你变得越来越好&#xff0c;时间和经历只是陪衬。 支撑你变得越来越好的&#xff0c;是你自己坚强的意志、修养、品行、以及不断的反思和经验。 人生最好的贵人&#xff0c;就是努力向上的自己。 一、系统托盘 1、资源文件夹 新建资源文件夹&#xff0c;我们需要把…

uniapp 之 uni-forms校验提示【提交的字段[‘xxx‘]在数据库中并不存在】解决方案

目录 场景问题代码结果问题剖析解决方案 场景 uni-forms官方组件地址 使用uniapp官方提供的组件&#xff0c;某个表单需求&#xff0c;单位性质字段如果是高校&#xff0c;那么工作单位则是高校的下拉选择格式&#xff0c;单位性质如果是其他的类型&#xff0c;工作单位则是手动…

Java面试核心知识4

公平锁与非公平锁 公平锁&#xff08;Fair&#xff09; 加锁前检查是否有排队等待的线程&#xff0c;优先排队等待的线程&#xff0c;先来先得 非公平锁&#xff08;Nonfair&#xff09; 加锁时不考虑排队等待问题&#xff0c;直接尝试获取锁&#xff0c;获取不到自动到队尾…

基于 SSH 的任务调度系统

文末附有完整项目代码 在当今科技飞速发展的时代&#xff0c;任务调度系统的重要性日益凸显。本文将详细介绍一个基于 SSH&#xff08;SpringStruts2Hibernate&#xff09;的任务调度系统的设计与实现。 一、系统概述 本系统旨在改变传统人工任务调度方式&#xff0c;通过计算…

我的128天创作之路:回顾与展望

大家好呀&#xff01;今天来和你们分享一下我的创作历程&#x1f601;。 一、机缘 最开始创作呢&#xff0c;是因为在学习 C 的 STL 时&#xff0c;像 string、list、vector 这些模板可把我折腾得够呛&#xff0c;但也让我学到了超多东西&#xff01;我就想&#xff0c;要是把我…

性能测试工具Jmeter中的FTP脚本开发

FTP文件传输协议是TCP/IP协议组织中的常用协议之一&#xff0c;主要用在internet上双向传输文件。FTP协议具有客户端和服务器端两个部分组成部分&#xff0c;具有上传与下载两种功能。Jmeter也提供了FTP请求的测试支持&#xff0c;实现了上传和下载功能测试。 对于上图的FTP请求…

【C++】string的关系运算与比较分析

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 &#x1f4af;前言&#x1f4af;基础知识&#xff1a;C 中的 string 关系运算器1. 关系运算器概述2. 字符串比较的本质 &#x1f4af;代码解析与扩展代码例一&#xff1a;相等比较代码解析输出 代码例二&a…

mysql本地安装和pycharm链接数据库操作

MySQL本地安装和相关操作 Python相关&#xff1a;基础、函数、数据类型、面向、模块。 前端开发&#xff1a;HTML、CSS、JavaScript、jQuery。【静态页面】 Java前端&#xff1b; Python前端&#xff1b; Go前端 -> 【动态页面】直观&#xff1a; 静态&#xff0c;写死了…

深度学习|表示学习|一个神经元可以干什么|02

如是我闻&#xff1a; 如果我们只有一个神经元&#xff08;即一个单一的线性或非线性函数&#xff09;&#xff0c;仍然可以完成一些简单的任务。以下是一个神经元可以实现的功能和应用&#xff1a; 1. 实现简单的线性分类 输入&#xff1a;一组特征向量 x x x 输出&#xff…

HTB:Paper[WriteUP]

目录 连接至HTB服务器并启动靶机 信息收集 使用rustscan对靶机TCP端口进行开放扫描 将靶机TCP开放端口号提取并保存 使用nmap对靶机TCP开放端口进行脚本、服务扫描 使用nmap对靶机TCP开放端口进行漏洞、系统扫描 使用nmap对靶机常用UDP端口进行开放扫描 对靶机进行子域…

做一个 简单的Django 《股票自选助手》显示 用akshare 库(A股数据获取)

图&#xff1a; 股票自选助手 这是一个基于 Django 开发的 A 股自选股票信息查看系统。系统使用 akshare 库获取实时股票数据&#xff0c;支持添加、删除和更新股票信息。 功能特点 支持添加自选股票实时显示股票价格和涨跌幅一键更新所有股票数据支持删除不需要的股票使用中…

Unity + Firebase + GoogleSignIn 导入问题

我目前使用 Unity版本&#xff1a;2021.3.33f1 JDK版本为&#xff1a;1.8 Gradle 版本为&#xff1a;6.1.1 Firebase 版本: 9.6.0 Google Sign In 版本为&#xff1a; 1.0.1 问题1 &#xff1a;手机点击登录报错 apk转化成zip&#xff0c;解压&#xff0c;看到/lib/armeabi-v…

Django学习笔记之数据库(一)

文章目录 安装一、数据库配置二、基本操作步骤1.增加2.查看3.排序4.更新5.删除数据 三、一对多&#xff0c;多对多&#xff0c;一对一1.一对多1.一对一1.多对多 四、查询操作五、聚合操作六、F和Q操作 安装 首先就是安装Mysql和Navicat。 一、数据库配置 其实整个就是连接前端…

SpringBoot日常:集成Kafka

文章目录 1、pom.xml文件2、application.yml3、生产者配置类4、消费者配置类5、消息订阅6、生产者发送消息7、测试发送消息 本章内容主要介绍如何在springboot项目对kafka进行整合&#xff0c;最终能达到的效果就是能够在项目中通过配置相关的kafka配置&#xff0c;就能进行消息…

RK3568 Android 13 内置搜狗输入法小计

问&#xff1a;为什么写&#xff1f; 答&#xff1a;网上搜出来的都试过了&#xff0c;不行&#xff01;下面直接上代码和注意事项&#xff01; 首先到这个目录&#xff08;/RK3568/Rockchip_Android13_SDK_Release/device/rockchip/rk356x/tl3568_evm/preinstall&#xff09…

【opencv】第8章 图像轮廓与图像分割修复

8.1 查找并绘制轮廓 一个轮廓一般对应一系列的点&#xff0c;也就是图像中的一条曲线。其表示方法可能 根据不同的情况而有所不同。在OpenCV 中&#xff0c;可以用findContours()函数从二值图 像中查找轮廓 8.1.1 寻找轮廓&#xff1a; findContours() 函数 findContours) 函…