OxyPlot 导出图片及 WPF 元素导出为图片的方法

OxyPlot 导出图片及 WPF 元素导出为图片的方法

目录

OxyPlot 导出图片及 WPF 元素导出为图片的方法

一、OxyPlot 自带导出方法

二、导出 WPF 界面元素的方法

三、通过附加属性来使用

独立观察员 2022 年 2 月 26 日

最近有个需求,就是将 OxyPlot 图形导出图片。经过尝试,本文记录三种方法:1、OxyPlot 自带导出方法;2、网上找的导出 WPF 界面元素的方法;3、基于方法 2 的附加属性调用方式。下面将逐一介绍。

一、OxyPlot 自带导出方法

同事说这个用 OxyPlot 官方提供的导出方法即可,我在 Demo 中试了一下,是可以的,代码如下:

/// <summary>
/// 曲线数据源(OxyPlot)
/// </summary>
public PlotModel PlotModel { get; set; } = new PlotModel();ExportPngCmd ??= new RelayCommand(o => true, async o =>
{var pngExporter = new PngExporter { Width = (int)PlotModel.Width, Height = (int)PlotModel.Height, };//string exportPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Export");string exportPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), "Export");if (!Directory.Exists(exportPath)){Directory.CreateDirectory(exportPath);}pngExporter.ExportToFile(PlotModel, Path.Combine(exportPath, $"{DateTime.Now:yyyyMMdd_HHmmss}.png"));await ConfirmBoxHelper.ShowMessage(DialogVm, "导出完成", 3);
});

各种导出方法可以在 OxyPlot 官方文档(https://oxyplot.readthedocs.io/en/latest/export/index.html)中查看

a4fe21e470519e6efda73abf49c59b7d.png

这里用到的是导出到 PNG 文件的方法,不过用的 NuGet 包最新版(2.1.0)中,PngExporter 中并没有 Background 属性:

2adc4ef95cfaa43feda23e0e6b0765ae.png

所以如果图表没有设置背景色的话,导出背景为透明的,可以设置上:

PlotModel.Background = OxyColor.Parse("#FFFFFF");

总的来说,这个方法简单快捷,而且对 MVVM 友好。不过也有缺点,就是如果有些元素(比如说标题、坐标轴文字)不是使用 OxyPlot 图表控件来生成的话,则导出的图片就不会包含它们了:

0dba9fd9706455a4687d6ff8294ef902.png

我在实际项目中确实遇到了这个问题,所以需要寻找其它方法,我们接着看。

二、导出 WPF 界面元素的方法

首先给出能够导出任意 WPF 界面元素(FrameworkElement)为图片的方法,来源于网络,地址在方法注释中已给出,略作修改,代码如下:

using System;
using System.IO;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;namespace WPFTemplateLib.WpfHelpers
{/// <summary>/// 导出图片帮助类/// </summary>public class ExportPicHelper{/// <summary>/// 保存为图片/// (修改自:https://blog.csdn.net/dhl11/article/details/108621634)/// </summary>/// <param name="frameworkElement"> 可视化元素,可以是 Grid、StackPanel 等类型的所有可视化元素 </param>/// <param name="filePath"> 文件路径 </param>/// <param name="errorMsg"> 错误消息 </param>/// <returns> 是否成功 </returns>public static bool SaveToImage(FrameworkElement frameworkElement, string filePath, out string errorMsg){try{errorMsg = string.Empty;FileStream fs = new FileStream(filePath, FileMode.Create);RenderTargetBitmap bmp = new RenderTargetBitmap((int)frameworkElement.ActualWidth,(int)frameworkElement.ActualHeight,1 / 96, 1 / 96, PixelFormats.Default);bmp.Render(frameworkElement);BitmapEncoder encoder = new TiffBitmapEncoder();encoder.Frames.Add(BitmapFrame.Create(bmp));encoder.Save(fs);fs.Close();return true;}catch (Exception ex){Console.WriteLine($" 保存图片异常:{ex}");errorMsg = ex.Message;return false;}}}
}

用这个方法首先要给界面元素起个名字,我这里给图表区用户控件元素起了个 “Plot” 名称:

c6497ce5ff3fb03a83801e925ca54517.png

这样在后台代码中就可以用来导出了:

private void ExportPicBtn_OnClick(object sender, RoutedEventArgs e)
{ExportPicture(Plot);
}/// <summary>
/// 导出图片
/// </summary>
/// <param name="element">xaml 里面的某个可视化元素对象 </param>
private void ExportPicture(FrameworkElement element)
{SaveFileDialog saveFileDialog = new SaveFileDialog{Filter = "PNG 文件 (*.png)|*.png|JPG 文件 (*.jpg)|*.jpg|BMP 文件 (*.bmp)|*.bmp|GIF 文件 (*.gif)|*.gif|TIF 文件 (*.tif)|*.tif"};if (saveFileDialog.ShowDialog() == true){string dir = System.IO.Path.GetDirectoryName(saveFileDialog.FileName);if (!Directory.Exists(dir)){Directory.CreateDirectory(dir);}string filePath = saveFileDialog.FileName;if (File.Exists(filePath)){File.Delete(filePath);}bool success = ExportPicHelper.SaveToImage(element, filePath, out string errorMsg);if (success){MessageBox.Show($"导出成功");}else{MessageBox.Show($" 导出失败 {errorMsg}");}}
}

可以看到想要导出的内容都导出成功了:

e236cd6e86421760011d2e1ff0b3ec12.png

优点是显而易见的,缺点就是导出逻辑要写在后台代码中,对 MVVM 模式不友好。下面来看看本人修改的使用附加属性的方案,尝试解决这个问题。

三、通过附加属性来使用

还是先给出代码:

using System;
using System.IO;
using System.Windows;
using WPFTemplateLib.WpfHelpers;namespace WPFTemplateLib.Attached
{/// <summary>/// 导出图片附加属性类/// </summary>public class ExportPicAttached : DependencyObject{#region 是否开始导出public static bool GetIsExporting(DependencyObject obj){return (bool)obj.GetValue(IsExportingProperty);}public static void SetIsExporting(DependencyObject obj, bool value){obj.SetValue(IsExportingProperty, value);}/// <summary>/// 是否正在导出(运行时设置为 true 则将附加的元素导出为图片)/// </summary>public static readonly DependencyProperty IsExportingProperty =DependencyProperty.RegisterAttached("IsExporting", typeof(bool), typeof(ExportPicAttached),new PropertyMetadata(false, OnIsExportingValueChanged));private static void OnIsExportingValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e){FrameworkElement element = d as FrameworkElement;if (element == null)return;if ((e.NewValue as bool?) == false)return;try{string exportPath = GetExportPath(d);if (string.IsNullOrEmpty(exportPath)){exportPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory),"Export");}if (!Directory.Exists(exportPath)){Directory.CreateDirectory(exportPath);}string filePath = Path.Combine(exportPath, $"{DateTime.Now:yyyyMMddHHmmss}.png");bool success = ExportPicHelper.SaveToImage(element, filePath, out string errorMsg);if (success){MessageBox.Show($"导出成功");}else{Console.WriteLine($" 导出失败:{errorMsg}");MessageBox.Show($" 导出失败 {errorMsg}");}}catch (Exception ex){Console.WriteLine($" 导出异常:{ex}");MessageBox.Show($" 导出异常:{ex.Message}");}finally{// 此处设置为 false 没什么用,还是需要业务层在设置为 true 前先设置为 false 才行。SetIsExporting(d, false);}}#endregion#region 导出文件夹public static string GetExportPath(DependencyObject obj){return (string)obj.GetValue(ExportPathProperty);}public static void SetExportPath(DependencyObject obj, string value){obj.SetValue(ExportPathProperty, value);}/// <summary>/// 导出文件夹路径/// </summary>public static readonly DependencyProperty ExportPathProperty =DependencyProperty.RegisterAttached("ExportPath", typeof(string), typeof(ExportPicAttached), new PropertyMetadata(string.Empty));#endregion}
}

ExportPicAttached 类中包含两个附加属性,一个是导出文件夹路径 ExportPath,一个是是否开始导出 IsExporting。当 IsExporting 被设置为 true 则开始导出,如果导出文件夹路径没被设定,则导出到桌面文件夹,然后就是调用方案二中出现的 ExportPicHelper.SaveToImage 方法。

使用方法就是在要导出的元素上设置上这两个附加属性,然后把值进行绑定:

f5ecb1c37da6e90583e6ce9e9e96a5e1.png

在 ViewModel 中,先设定导出路径,然后把 IsExporting 置为 true 即可开始导出:

520678f7ddd3c2ed94940d2cd7c9f7f5.png

也是能正常导出的:

24f1ba46157acec7106d27e1bc477d81.png

这个方案结合了前两个方案的优点,既能导出所有想要的内容,又对 MVVM 友好。

缺点就是导出的控制有点奇怪,需要先将 IsExporting 置为 false,不然第二次就导出不了了。尝试了在附加属性逻辑中自动置为 false,但是好像值传递不到 VM 中的相关绑定属性中,有了解解决方法的朋友们请不吝赐教。

ccc530ac81b1b633f527ac3d8ef3f73d.png

全文完,感谢阅读,祝大家天天开心。

WPF

让 WPF 的 RadioButton 支持再次点击取消选中的功能

WPF DataGrid 如何将被选中行带到视野中

WPF 触屏事件后触发鼠标事件的问题及 DataGrid 误触问题

WPF DataGrid 通过自定义表头模拟首行固定

WPF ComboBox 使用 ResourceBinding 动态绑定资源键并支持语言切换

【翻译】WPF 中附加行为的介绍 Introduction to Attached Behaviors in WPF

WPF 使用 Expression Design 画图导出及使用 Path 画图

WPF MVVM 弹框之等待框

解决 WPF 绑定集合后数据变动界面却不更新的问题(使用 ObservableCollection)

WPF 消息框 TextBox 绑定新数据时让光标和滚动条跳到最下面

真・WPF 按钮拖动和调整大小

WPF MVVM 模式下的弹窗

WPF 让一组 Button 实现 RadioButton 的当前样式效果

WPF 原生绑定和命令功能使用指南

WPF 用户控件的自定义依赖属性在 MVVM 模式下的使用备忘

在WPF的MVVM模式中使用OCX组件

第三方库使用

WPF 表格控件 ReoGrid 的简单使用

OxyPlot.WPF 公共属性一览

OxyPlot.Wpf 图表控件使用备忘

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

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

相关文章

delphi中利用Indy的TIdFtp控件实现FTP协议

2019独角兽企业重金招聘Python工程师标准>>> delphi中利用Indy的TIdFtp控件实现FTP协议版权声明&#xff1a;本文为博主原创文章&#xff0c;未经博主允许不得转载。现在很多应用都需要上传与下载大型文件&#xff0c;通过HTTP方式上传大文件有一定的局限性。幸好FT…

C++之map插入数据相同的key不能覆盖value解决办法

1、问题 C里面,如果map里面插入之前的<key, value>,如果key在map里面有的话&#xff0c;不会覆盖之前的value,一般先判断之前有没有数据&#xff0c;有的话先删除&#xff0c;然后再去添加。 2、代码实现 3、运行结果

【BZOJ】【4145】【AMPPZ2014】The Prices

状压DP/01背包 Orz Gromah 容易发现m的范围很小……只有16&#xff0c;那么就可以状压&#xff0c;用一个二进制数来表示买了的物品的集合。 一种简单直接的想法是&#xff1a;令$f[i][j]$表示前$i$个商店买了状态集合为$j$的商品的最小代价&#xff0c;那么我们转移的时候就需…

WPF 实现人脸检测

WPF开发者QQ群此群已满340500857 &#xff0c;请加新群458041663由于微信群人数太多入群请添加小编微信号yanjinhuawechat 或 W_Feng_aiQ 邀请入群需备注WPF开发者 PS&#xff1a;有更好的方式欢迎推荐。接着上一篇利用已经训练好的数据文件,检测人脸 地址如下&#xff1a;http…

C++之函数的默认值参数说明

1、思考 今天看到C代码的时候&#xff0c;发现文件里面的函数定义和实现都有3个参数&#xff0c;特码调用的时候只有2个参数了&#xff0c;日了狗&#xff0c;java里面好像没有这种方式&#xff0c;后来才发现是默认参数 2、代码实现 3、展示结果 4、总结 注意默认参数需要写…

插头DP

AC HDU1693 不能再简单了的插头DP 1 #include <cstdio>2 #include <fstream>3 #include <iostream>4 5 #include <cstdlib>6 #include <cstring>7 #include <algorithm>8 #include <cmath>9 10 #include <queue>11 #include…

自定义控件详解(四):Paint 画笔路径效果

Paint 画笔 &#xff0c;即用来绘制图形的"笔" 前面我们知道了Paint的一些基本用法&#xff1a; paint.setAntiAlias(true);//抗锯齿功能 paint.setColor(Color.RED); //设置画笔颜色 paint.setStyle(Style.FILL);//设置填充样式 paint.setStrokeWidth(10);//设…

2021 .NET Conf China 主题分享之-轻松玩转.NET大规模版本升级

去年.NET Conf China 技术大会上&#xff0c;我给大家分享了主题《轻松玩转.NET大规模版本升级》&#xff0c;今天把具体分享的内容整理成一篇博客&#xff0c;供大家研究参考学习。一、先说一下技术挑战和业务背景我们公司&#xff1a;特来电新能源股份有限公司&#xff1a;中…

ASP.NET Core基于滑动窗口算法实现限流控制

前言在实际项目中&#xff0c;为了保障服务器的稳定运行&#xff0c;需要对接口的可访问频次进行限流控制&#xff0c;避免因客户端频繁请求导致服务器压力过大。而AspNetCoreRateLimit[1]是目前ASP.NET Core下最常用的限流解决方案。查看它的实现代码&#xff0c;我发现它使用…

linux操作系统cp命令

转载于:https://www.cnblogs.com/skl374199080/p/3863918.html

sql必读的九本书

2019独角兽企业重金招聘Python工程师标准>>> 原文地址 直接上书(书籍以后会陆续加上去)书籍下载地址 《MySQL必知必会》《SQL学习指南&#xff08;第2版 修订版&#xff09;》《MySQL技术内幕——InnoDB存储引擎》《Redis设计与实现》《ZooKeeper&#xff1a;分布式…

C语言之加入头文件<stdbool.h>可以使用true和false

1、头文件<stdbool.h>介绍 &#xff08;1&#xff09;使用了<stdbool.h>后&#xff0c;可使用true和false来表示真假。 &#xff08;2&#xff09;在循环语句中进行变量声明是C99中才有的&#xff0c;因此编译时显式指明 gcc -stdc99 prime.c 2、最简单的例子 3、…

Nginx负载均衡+转发策略

负载均衡负载均衡(详解)https://cloud.tencent.com/developer/article/1526664--示例1upstream www_server_pool { server 10.0.0.5; server 10.0.0.6&#xff1a;80 weight1 max_fails1 fails_timeout10s; server 10.0.0.7&#xff1a;80 weight1 max_fails2 fails_timeo…

教育行业的互联网焦虑症

2019独角兽企业重金招聘Python工程师标准>>> 文/阑夕 2007年&#xff0c;前新东方名师刘一男在新东方在线&#xff08;网校&#xff09;上的全年课程收入是三千元&#xff0c;四年之后的2011年&#xff0c;这个数字飙升到了四十万&#xff0c;已经和刘一男当年实体…

零基础学人工智能:TensorFlow 入门例子

识别手写图片 因为这个例子是 TensorFlow 官方的例子&#xff0c;不会说的太详细&#xff0c;会加入了一点个人的理解&#xff0c;因为TensorFlow提供了各种工具和库&#xff0c;帮助开发人员构建和训练基于神经网络的模型。TensorFlow 中最重要的概念是张量&#xff08;Tenso…

CA周记 - 用 Visual Studio Code 做基于 .NET MAUI 跨平台移动应用开发

自2010年以来&#xff0c;移动应用开发是非常热门的一个方向&#xff0c;从技术上我们经历了原生应用开发、基于 H5 的 Web App、混合模式的移动应用开发&#xff0c;再到跨平台移动应用开发。.NET 不仅是一个跨平台的应用&#xff0c;也是一个跨应用场景的平台。.NET的移动应用…

P2P网络穿越 NAT穿越

http://blog.csdn.net/mazidao2008/article/details/4933730 —————————————————————————————————————————————————————————————— 穿越NAT的意义&#xff1a; NAT是为了节省IP地址而设计的&#xff0c;但它隐藏了…

C#导入导出.CSV文件

欢迎您成为我的读者&#xff0c;希望这篇文章能给你一些帮助。前言大家好&#xff0c;我是阿辉。今天和大家一起来看看&#xff0c;C#在处理流文件时,我们最常用的导出Excel文件是如何操作的。在日常的业务编码过程中&#xff0c;很多时候需求就要求导出Office能打开的表格文件…

奋斗逼,真牛逼!

▲点击上方第二个findyi关注&#xff0c;回复“1”领取职场资料职场&认知洞察 丨 作者 / 易洋 这是findyi公众号的第304篇原创文章今天下午一个读者咨询我一个问题&#xff1a;这名读者感觉卷不过身边的加班狂人&#xff0c;但又感觉这些人丝毫不给公司创造价值&#xff0…

Entity Framework 批量插入

为什么80%的码农都做不了架构师&#xff1f;>>> 奋斗的小鸟——dogxuefeng Entity Framework 批量插入很慢 Entity Framework 批量插入很慢吗&#xff1f;我自己测试下 前几天看到一篇文章里提到过&#xff0c;在批量插入时&#xff0c;需要加上Context.Configur…