WPF+Mvvm案例实战(五)- 自定义雷达图实现

文章目录
- 1、项目准备
- 1、创建文件
- 2、用户控件库
- 2、功能实现
- 1、用户控件库
- 1、控件样式实现
- 2、数据模型实现
- 2、应用程序代码实现
- 1.UI层代码实现
- 2、数据后台代码实现
- 3、主界面菜单添加
- 1、后台按钮方法改造:
- 2、按钮添加:
- 3、依赖注入
- 3、运行效果
- 4、源代码获取
1、项目准备
1、创建文件
打开项目 Wpf_Examples,新建 RadarWindow.xaml 界面、RadarViewModel.cs 和 用户控件库 UserControlLib 。如下所示:

2、用户控件库
创建用户控件库,创建 数据模型 RadarModel.cs 和 用户控件 RadarUC.xaml,文档目录结构如下:


2、功能实现
1、用户控件库
1、控件样式实现
RadarUC.xaml 代码如下:
<UserControl x:Class="UserControlLib.RadarUC"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:UserControlLib"mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"><Grid x:Name="LayGrid"><!--画布--><Canvas x:Name="mainCanvas"></Canvas><!--4规则多边形--><Polygon x:Name="P1" Stroke="#22ffffff" StrokeThickness="1"></Polygon><Polygon x:Name="P2" Stroke="#22ffffff" StrokeThickness="1"></Polygon><Polygon x:Name="P3" Stroke="#22ffffff" StrokeThickness="1"></Polygon><Polygon x:Name="P4" Stroke="#22ffffff" StrokeThickness="1"></Polygon><!--数据多边形--><Polygon x:Name="P5" Stroke="Orange" Fill="#550091F0" StrokeThickness="1" ></Polygon></Grid>
</UserControl>
RadarUC.cs 后端代码如下:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
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 UserControlLib.Models;namespace UserControlLib
{/// <summary>/// RadarUC.xaml 的交互逻辑/// </summary>public partial class RadarUC : UserControl{public RadarUC(){InitializeComponent();SizeChanged += OnSizeChanged;//Alt+Enter}/// <summary>/// 窗体大小发生变化 重新画图/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void OnSizeChanged(object sender, SizeChangedEventArgs e){Drag();}/// <summary>/// 数据源。支持数据绑定 依赖属性/// </summary>public ObservableCollection<RadarModel> ItemSource{get { return (ObservableCollection<RadarModel>)GetValue(ItemSourceProperty); }set { SetValue(ItemSourceProperty, value); }}// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...public static readonly DependencyProperty ItemSourceProperty =DependencyProperty.Register("ItemSource", typeof(ObservableCollection<RadarModel>), typeof(RadarUC));/// <summary>/// 画图方法/// </summary>public void Drag(){//判断是否有数据if (ItemSource == null || ItemSource.Count == 0){return;}//清楚之前画的mainCanvas.Children.Clear();P1.Points.Clear();P2.Points.Clear();P3.Points.Clear();P4.Points.Clear();P5.Points.Clear();//调整大小(正方形)double size = Math.Min(RenderSize.Width, RenderSize.Height);LayGrid.Height = size;LayGrid.Width = size;//半径double raduis = size / 2;//步子跨度double step = 360.0 / ItemSource.Count;for (int i = 0; i < ItemSource.Count; i++){double x = (raduis - 20) * Math.Cos((step * i - 90) * Math.PI / 180);//x偏移量double y = (raduis - 20) * Math.Sin((step * i - 90) * Math.PI / 180);//y偏移量//X Y坐标P1.Points.Add(new Point(raduis + x, raduis + y));P2.Points.Add(new Point(raduis + x * 0.75, raduis + y * 0.75));P3.Points.Add(new Point(raduis + x * 0.5, raduis + y * 0.5));P4.Points.Add(new Point(raduis + x * 0.25, raduis + y * 0.25));//数据多边形P5.Points.Add(new Point(raduis + x * ItemSource[i].Value * 0.01, raduis + y * ItemSource[i].Value * 0.01));//文字处理TextBlock txt = new TextBlock();txt.Width = 60;txt.FontSize = 10;txt.TextAlignment = TextAlignment.Center;txt.Text = ItemSource[i].ItemName;txt.Foreground = new SolidColorBrush(Color.FromArgb(100, 255, 255, 255));txt.SetValue(Canvas.LeftProperty, raduis + (raduis - 10) * Math.Cos((step * i - 90) * Math.PI / 180) - 30);//设置左边间距txt.SetValue(Canvas.TopProperty, raduis + (raduis - 10) * Math.Sin((step * i - 90) * Math.PI / 180) - 7);//设置上边间距mainCanvas.Children.Add(txt);}}}
}
2、数据模型实现
RadarModel.cs 代码实现:
public class RadarModel : INotifyPropertyChanged
{public event PropertyChangedEventHandler PropertyChanged;protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null){PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));}/// <summary>/// 项名称/// </summary>private string _ItemName;public string ItemName{get => _ItemName;set{if (_ItemName != value){_ItemName = value;OnPropertyChanged();}}}/// <summary>/// 项数值/// </summary>private double _Value;public double Value{get => _Value;set{if (_Value != value){_Value = value;OnPropertyChanged();}}}
}
2、应用程序代码实现
1.UI层代码实现
RadarWindow.xmal 代码如下(示例):
<Window x:Class="Wpf_Examples.Views.RadarWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:radar="clr-namespace:UserControlLib;assembly=UserControlLib"xmlns:local="clr-namespace:Wpf_Examples.Views"DataContext="{Binding Source={StaticResource Locator},Path=Radar}"mc:Ignorable="d"Title="RadarWindow" Height="450" Width="800" Background="#2B2B2B"><Grid><GroupBox Header="战斗属性" Margin="20" Foreground="White"><radar:RadarUC ItemSource="{Binding RadarList}"></radar:RadarUC></GroupBox></Grid>
</Window>
2、数据后台代码实现
项目控件库引用,如下所示:

项目页面控件引用

RadarViewModel.cs 代码如下:
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Threading;
using UserControlLib.Models;namespace Wpf_Examples.ViewModels
{public class RadarViewModel:ObservableObject{#region 雷达数据属性/// <summary>/// 雷达/// </summary>private ObservableCollection<RadarModel> radarList;public ObservableCollection<RadarModel> RadarList{get { return radarList; }set { SetProperty(ref radarList, value); }}#endregionpublic RadarViewModel() {#region 初始化雷达数据 RadarList = new ObservableCollection<RadarModel>();RadarList.Add(new RadarModel { ItemName = "闪避", Value = 90 });RadarList.Add(new RadarModel { ItemName = "防御", Value = 30.00 });RadarList.Add(new RadarModel { ItemName = "暴击", Value = 34.89 });RadarList.Add(new RadarModel { ItemName = "攻击", Value = 69.59 });RadarList.Add(new RadarModel { ItemName = "速度", Value = 20 });CreateTimer(); //创建定时器动态改变数据#endregion}private void CreateTimer(){#region 每秒定时器服务DispatcherTimer cpuTimer = new DispatcherTimer{Interval = new TimeSpan(0, 0, 0, 3, 0)};cpuTimer.Tick += DispatcherTimer_Tick;cpuTimer.Start();#endregion}private void DispatcherTimer_Tick(object sender, EventArgs e){Random random = new Random();foreach (var item in RadarList){item.Value = random.Next(10, 100);}}}
}
3、主界面菜单添加
1、后台按钮方法改造:
private void FunMenu(string obj){switch (obj){case "图片按钮":PopWindow(new ImageButtonWindow());break;case "LED效果灯":PopWindow(new LEDStatusWindow());break;case "动态数字卡":PopWindow(new DataCardWindow());break;case "自定义GroupBox边框":PopWindow(new GroubBoxWindow());break;case "自定义雷达图":PopWindow(new RadarWindow());break;}}
2、按钮添加:
<WrapPanel><Button Width="120" Height="30" FontSize="18" Content="图片按钮" Command="{Binding ButtonClickCmd}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=Self},Path=Content}" Margin="8"/><Button Width="120" Height="30" FontSize="18" Content="LED效果灯" Command="{Binding ButtonClickCmd}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=Self},Path=Content}" Margin="8"/><Button Width="120" Height="30" FontSize="18" Content="动态数字卡" Command="{Binding ButtonClickCmd}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=Self},Path=Content}" Margin="8"/><Button Width="190" Height="30" FontSize="18" Content="自定义GroupBox边框" Command="{Binding ButtonClickCmd}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=Self},Path=Content}" Margin="8"/><Button Width="140" Height="30" FontSize="18" Content="自定义雷达图" Command="{Binding ButtonClickCmd}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=Self},Path=Content}" Margin="8"/></WrapPanel>
3、依赖注入
public class ViewModelLocator{public IServiceProvider Services { get; }public ViewModelLocator(){Services = ConfigureServices();}private static IServiceProvider ConfigureServices(){var services = new ServiceCollection();//这里实现所有viewModel的容器注入services.AddSingleton<MainViewModel>();services.AddScoped<LEDStatusViewModel>();services.AddScoped<ImageButtonViewModel>();services.AddScoped<DataCardViewModel>();services.AddScoped<GroubBoxViewModel>();services.AddScoped<RadarViewModel>();//添加其他 viewModelreturn services.BuildServiceProvider();}public MainViewModel Main => Services.GetService<MainViewModel>();public LEDStatusViewModel LedStatus => Services.GetService<LEDStatusViewModel>();public ImageButtonViewModel ImageButton => Services.GetService<ImageButtonViewModel>();public DataCardViewModel DataCard => Services.GetService<DataCardViewModel>();public GroubBoxViewModel GroupBox => Services.GetService<GroubBoxViewModel>();public RadarViewModel Radar => Services.GetService<RadarViewModel>();}
3、运行效果

4、源代码获取
CSDN:下载链接WPF+Mvvm案例实战- 自定义雷达图实现
相关文章:
WPF+Mvvm案例实战(五)- 自定义雷达图实现
文章目录 1、项目准备1、创建文件2、用户控件库 2、功能实现1、用户控件库1、控件样式实现2、数据模型实现 2、应用程序代码实现1.UI层代码实现2、数据后台代码实现3、主界面菜单添加1、后台按钮方法改造:2、按钮添加:3、依赖注入 3、运行效果4、源代码获…...
网络爬虫-Python网络爬虫和C#网络爬虫
爬虫是一种从互联网抓取数据信息的自动化程序,通过 HTTP 协议向网站发送请求,获取网页内容,并通过分析网页内容来抓取和存储网页数据。爬虫可以在抓取过程中进行各种异常处理、错误重试等操作,确保爬取持续高效地运行 1、Python网…...
如何有效解除TikTok账号间的IP关联
在当今社交媒体环境中,TikTok凭借其独特的短视频形式吸引了数以亿计的用户。对许多内容创作者而言,运营多个账号是获取更大曝光和丰富内容的有效策略。然而,如何避免这些账号之间的IP关联,以防止被平台识别并封禁,成为…...
Python自省机制
Python 自省机制 Python 自省(Introspection)是一种动态检查对象的能力,使得开发者可以在运行时获取对象的相关信息,比如属性、方法、类型等。自省机制让 Python 具备了更强的动态性和灵活性,便于调试和开发。 自省&…...
wgan-gp 对连续变量 训练,6万条数据,训练结果不错,但是到局部的时候,拟合不好,是否可以对局部数据也进行计算呢
Wasserstein GAN with Gradient Penalty (WGAN-GP) 是一种改进的生成对抗网络(GAN),它通过引入梯度惩罚来改进训练过程,从而提高生成模型的稳定性和质量。如果你在使用WGAN-GP对连续变量进行训练时,发现整体训练结果不…...
python 制作 发货单 (生成 html, pdf)
起因, 目的: 某个小店,想做个发货单。 过程: 先写一个 html 模板。准备数据, 一般是从数据库读取,也可以是 json 格式,或是 python 字典。总之,是数据内容。使用 jinja2 来渲染模板。最终的结果可以是 h…...
GeoWebCache1.26调用ArcGIS切片
常用网址: GeoServer GeoWebCache (osgeo.org) GeoServer 用户手册 — GeoServer 2.20.x 用户手册 一、版本需要适配:Geoserver与GeoWebCache、jdk等的版本适配对照 查看来源 二、准备工作 1、数据:Arcgis标准的切片,通过…...
深度学习-卷积神经网络-基于VGG16模型, 实现猫狗二分类(文末附带数据集下载链接, 长期有效)
简介: 1.基于VGG16模型进行特征提取, 结合mlp实现猫狗二分类 2.训练数据--"dog_cat_class\training_set" 3.模型训练流程 1.对图像数据进行导入和预处理 2.搭建模型, 导入VGG16模型, 去除mlp层, 将经过VGG16训练后的数据作为输入, 输入到自建的mlp层中进行训练, 要…...
计算Java集合占用的空间【详解】
以ArrayList为例,假设集合元素类型是Person类型,假设集合容量为10,目前有两个person对象{name:“Jack”,age12} {name:“Tom”,age14} public class Person{private String name;private int age; }估算Person对象占用的大小: 对…...
仕考网:关于中级经济师考试的介绍
中级经济师考试是一种职称考试,每年举办一次,报名时间在7-8月,考试时间在10-11月 报名入口:中guo人事考试网 报名条件: 1.高中毕业并取得初级经济专业技术资格,从事相关专业工作满10年; 2.具备大学专科…...
SYN590RL 300MHz至450MHz ASK接收机芯片IC
一般描述 SYN590RL是赛诺克全新开发设计的一款宽电压范围,低功耗,高性能,无需外置AGC电容,灵敏度达到典型-110dBm,300MHz”450MHz 频率范围应用的单芯片ASK或OOK射频接收器。 SYN59ORL是一款典型的即插即用型单片高集成度无线接收器&…...
15分钟学 Go 第 20 天:Go的错误处理
第20天:Go的错误处理 目标 学习如何处理错误,以确保Go程序的健壮性和可维护性。 1. 错误处理的重要性 在开发中,错误处理至关重要。程序在运行时可能会出现各种问题,例如文件未找到、网络连接失败等。正确的错误处理能帮助我们…...
C++——string的模拟实现(上)
目录 引言 成员变量 1.基本框架 成员函数 1.构造函数和析构函数 2.拷贝构造函数 3.容量操作函数 3.1 有效长度和容量大小 3.2 容量操作 3.3 访问操作 (1)operator[]函数 (2)iterator迭代器 3.4 修改操作 (1)push_back()和append() (2)operator函数 引言 在 C—…...
JavaCV 之均值滤波:图像降噪与模糊的权衡之道
🧑 博主简介:CSDN博客专家,历代文学网(PC端可以访问:https://literature.sinhy.com/#/literature?__c1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,…...
桥接模式,外界与主机通,与虚拟机不通
一 二 在此选择Windows与外界连接的网卡,通过有线连就选有线网卡,通过无线连就选无线网卡。 三 如果需要设置固定IP,则选择"Manual"进行设置。我这边根据实际需要,走无线的时候用DHCP,走有线的时候设固定IP…...
用HTML构建酷炫的文件上传下载界面
1. 基础HTML结构 首先,我们构建一个基本的HTML结构,包括一个表单用于文件上传,以及一个列表用于展示已上传文件: HTML <!DOCTYPE html> <html> <head><title>酷炫文件上传下载</title><link …...
Gateway 统一网关
一、初识 Gateway 1. 为什么需要网关 我们所有的服务可以让任何请求访问,但有些业务不是对外公开的,这就需要用网关来统一替我们筛选请求,它就像是房间的一道门,想进入房间就必须经过门。而请求想要访问微服务,就必须…...
7 种常见的前端攻击
大家都知道,保证网站的安全是十分重要的,一旦网站被攻陷,就有可能造成用户的经济损失,隐私泄露,网站功能被破坏,或者是传播恶意病毒等重大危害。所以下面我们就来讲讲7 种常见的前端攻击。 1. 跨站脚本 (X…...
element plus实现点击上传于链接上传并且回显到upload组件中
摘要: 今天遇到一个问题:vue3使用elemnt plus的上传图片时,数据是从别人的系统导出来的商品,图片是http的形式的,并且商品很多的,一个一个下载下来再上传很麻烦的,所以本系统插件商品时图片使用…...
ELK日志分析系统部署
ELK日志分析系统 ELK指的是ElasticsearchLogstashKibana这种架构的缩写。 ELK是一种日志分析平台,在很早之前我们经常使用Shell三剑客(一般泛指grep、sed、awk)来进行日志分析,这种方式虽然也可以应对多种场景,但是当…...
5G载波聚合(CA)技术解析:提升网络速度与容量的关键手段
5G载波聚合(CA)技术解析:提升网络速度与容量的关键手段 在5G通信技术不断演进的进程中,载波聚合(Carrier Aggregation,简称CA)作为一项重要技术,正逐渐成为提升网络性能、满足用户多…...
FastAPI新手快速入门
一、认识FastAPI1.什么是apiapi接口其实就是应用程序器对外提供操作数据的入口,这个入口可以是函数、方法或者url接口当客户端调用入口,应用程序会执行对应代码操作,完成相对应的功能(应用服务器只负责对外提供统一API,…...
终极指南:用ncmdump彻底解决网易云音乐NCM格式限制
终极指南:用ncmdump彻底解决网易云音乐NCM格式限制 【免费下载链接】ncmdump ncmdump - 网易云音乐NCM转换 项目地址: https://gitcode.com/gh_mirrors/ncmdu/ncmdump 在数字音乐时代,格式兼容性已成为音乐爱好者面临的核心挑战。当你从网易云音乐…...
中国科学技术大学学位论文LaTeX模板ustcthesis:学术写作的终极解决方案
中国科学技术大学学位论文LaTeX模板ustcthesis:学术写作的终极解决方案 【免费下载链接】ustcthesis LaTeX template for USTC thesis 项目地址: https://gitcode.com/gh_mirrors/us/ustcthesis 中国科学技术大学学位论文LaTeX模板ustcthesis是专为中科大师生…...
《Java面试85题图解版(二)》进阶深化中篇:Spring核心 + 数据库进阶
📘 《Java面试85题图解版(二)》进阶深化中篇:Spring核心 数据库进阶 阅读提示:这是“图解比喻一句话总结”面试题库第二篇的进阶深化中篇,覆盖Spring核心与Spring Boot(9题)和数据库…...
前端工程化:开发环境配置最佳实践
前端工程化:开发环境配置最佳实践 前言 开发环境配置是前端工程化的基础。一个良好的开发环境能大大提高开发效率,减少团队协作中的环境问题。今天我就来给大家讲讲如何配置一套高效的前端开发环境。 为什么开发环境配置如此重要 开发环境是开发者日常工…...
KMS智能激活终极指南:5分钟永久激活Windows和Office全系列
KMS智能激活终极指南:5分钟永久激活Windows和Office全系列 【免费下载链接】KMS_VL_ALL_AIO Smart Activation Script 项目地址: https://gitcode.com/gh_mirrors/km/KMS_VL_ALL_AIO 还在为Windows系统频繁弹出激活提示而烦恼吗?Office文档突然变…...
【奇点智能大会独家解密】:大模型AB测试+影子流量+语义一致性校验三位一体灰度框架
更多请点击: https://intelliparadigm.com 第一章:大模型灰度发布策略:奇点智能大会 在2024年奇点智能大会上,多家头部AI平台首次系统性披露了面向千亿参数级大模型的灰度发布实践框架。该策略核心在于将模型更新从“全量切换”…...
Linux超级计算机Roadrunner的设计与优化实践
1. Linux超级计算机Roadrunner的设计背景与核心理念在1990年代末期,高性能计算领域正处于一个关键的转折点。传统超级计算机如Cray系列虽然性能强大,但价格昂贵且维护成本极高,使得大多数研究机构难以负担。与此同时,个人计算机性…...
数据流网络中的能耗与吞吐量优化策略
1. 数据流网络中的能量与吞吐量权衡:原理与挑战在现代信号处理系统中,数据流网络(Dataflow Graph, DFG)已成为建模并行计算任务的核心框架。这种计算模型将应用程序分解为多个计算节点(称为actors)和通信通…...
