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案例实战- 自定义雷达图实现

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

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

相关文章

TikTok运营对IP有什么要求?

TikTok在进行直播带货时&#xff0c;网络环境的配置尤为关键&#xff0c;网络质量直接影响到直播效果&#xff0c;因此选择稳定的IP地址很重要。那么&#xff0c;TikTok直播时该选择什么样的IP地址呢&#xff1f;接下来&#xff0c;我们来深入分析一下。 TikTok对IP地址的要求 …

解读数字化转型的敏捷架构:从理论到实践的深度分析

在当今数字经济的推动下&#xff0c;企业要在瞬息万变的市场中保持竞争力&#xff0c;数字化转型已经不再是一种选择&#xff0c;而是不可避免的战略需求。然而&#xff0c;企业如何从理论到实践进行有效的转型&#xff0c;尤其是在复杂的技术环境中&#xff0c;如何通过正确的…

【FISCO BCOS】二十二、使用Key Manager加密区块链节点

#1024程序员节&#xff5c;征文# 落盘加密是对节点存储在硬盘上的内容进行加密&#xff0c;加密的内容包括&#xff1a;合约的数据、节点的私钥。具体的落盘加密介绍&#xff0c;可参考&#xff1a;落盘加密的介绍&#xff0c;今天我们来部署并对节点进行落盘加密。 环境&a…

信息学奥赛后的发展路径:科技创新、竞赛选拔还是学术研究?

参加信息学奥赛&#xff08;OI&#xff09;后&#xff0c;学生往往具备了较强的编程能力、逻辑思维和算法知识&#xff0c;而这些技能在多种发展路径上都有广泛应用。对于有志于深入发展的学生来说&#xff0c;选择合适的发展方向尤为重要。本文将详细讨论信息学奥赛后学生的三…

Linux中DHCP服务器配置和管理

文章目录 一、DHCP服务1.1、DHCP的工作流程1.2、DHCP的工作模式1.3、dhcp的主要配置文件 二、安装DHCP服务2.1、更新yum源2.2、安装DHCP服务软件包2.3、配置DHCP服务2.4、启用DHCP服务&#xff08;解决报错&#xff09;2.4.1、查看dhcpd服务的状态和最近的日志条目2.4.2、查看与…

<网络> 网络套接字编程(二)

文章目录 目录 文章目录 一、简单的TCP网络程序 1. 服务器创建套接字 2. 服务器绑定 3. 服务器监听 listen 4. 服务器获取连接 accept 5. 服务器处理请求 6. 客户端创建套接字 7. 客户端连接服务器 connect 8. 客户端发起请求 9. 服务器测试 10. 单执行流服务器弊端 二、多进程…

技术成神之路:设计模式(二十一)外观模式

相关文章&#xff1a;技术成神之路&#xff1a;二十三种设计模式(导航页) 介绍 外观模式&#xff08;Facade Pattern&#xff09;是一种结构型设计模式&#xff0c;它为子系统中的一组接口提供一个统一的接口。外观模式定义了一个高层接口&#xff0c;使得子系统更容易使用。 …

【Vulnhub靶场】DC-2

DC-2 靶场下载地址&#xff1a;https://download.vulnhub.com/dc/DC-2.zip 目标 本机IP&#xff1a;192.168.118.128 靶机IP&#xff1a;192.168.118.0/24 信息收集 常规我使用nmap三扫描&#xff0c;扫存活主机、扫端口、扫服务 第一步探测到存活主机IP为&#xff1a;192.1…

时序分解 | TTNRBO-VMD改进牛顿-拉夫逊算法优化变分模态分解

时序分解 | TTNRBO-VMD改进牛顿-拉夫逊算法优化变分模态分解 目录 时序分解 | TTNRBO-VMD改进牛顿-拉夫逊算法优化变分模态分解效果一览基本介绍程序设计参考资料 效果一览 基本介绍 (创新独家)TTNRBO-VMD改进牛顿-拉夫逊优化算优化变分模态分解TTNRBO–VMD 优化VMD分解层数K和…

MySQL任意版本安装卸载和数据库原理图绘制

MYSQL任意版本安装和卸载 安装&#xff1a; 1、解压文件 --- 不能出现中文路径 2、在解压目录&#xff08;安装目录&#xff09;下&#xff1a; 1>.创建data文件夹 2>.创建配置文件my.txt 然后修改成ini格式 3、修改配置文件 basedirD:\\mysql\\mysql-5.7.28-winx64…

后台进程注册的ContentObserver接收到的回调晚10秒钟

后台进程中的ContentObserver延迟回调晚10秒钟 在Android系统中&#xff0c;后台进程注册的ContentObserver会有一个延迟回调机制&#xff0c;通常延迟10秒左右。这种机制的设计是为了优化系统资源&#xff0c;减少后台进程频繁监听数据变化带来的资源消耗。 背景 当应用处于…

RabbitMQ集群搭建及使用

1. 概述 前提条件&#xff1a;linux服务器下已经安装好了docker服务。 本文档将搭建一个三台RabbitMQ的集群&#xff0c;包括三个RabbitMQ容器安装在同一服务器和三台不同的服务器。 2. 集群搭建 在一台服务器上创建三个RabbitMQ容器。 2.1.1. 创建容器 执行以下命令创建三…

Python实现PSO粒子群优化DBSCAN膨胀聚类模型(DBSCAN算法)项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后关注获取。 1.项目背景 随着大数据时代的到来&#xff0c;从海量数据中提取有用信息变得至关重要。聚类分析作为一种无监督…

Telephony中ITelephony的AIDL调用关系

以Android14.0源码讲解 ITelephony来自framework下的com.android.internal.telephony包下 frameworks/base/telephony/java/com/android/internal/telephony/ITelephony.aidl这个接口用于与Phone交互的界面&#xff0c;主要由TelephonyManager类使用&#xff0c;一些地方仍在…

【电机控制】相电流重构——单电阻采样方案

【电机控制】相电流重构——单电阻采样方案 文章目录 [TOC](文章目录) 前言一、基于单电阻采样电流重构技术原理分析1.1 单电阻采样原理图1.2 基本电压矢量与电流采样关系 二、非观测区2.1 扇区过渡区2.2 低压调制区 三、非观测区补偿——移相法四、参考文献总结 前言 使用工具…

C++11实践指北

C11&#xff1a;书、在线工具、库。 书 1. 《现代C语言核心特性解析》 覆盖 C11~C20 特性的讲解。 视频跟读&#xff1a;https://www.bilibili.com/video/BV1nN4y1j7fv 现代CPP随笔_0CCh - 每天5分钟了解现代C新特性 2. 《C Primer》第五版 基于 C11 的 C 入门书。 正在看…

java基础面试题一

目录 1、Java语言概述 1.1一个”.java”源文件中是否可以包括多个类&#xff1f;有什么限制 1.2Java 的优势 1.3常用的几个命令行操作都有哪些&#xff1f;(至少4个) 1.4Java 中是否存在内存溢出、内存泄漏&#xff1f;如何解决&#xff1f;举例说明 1. 内存溢出&#xf…

RocketMQ | 源码分析 | Broker控制器的启动

在分布式消息中间件的领域中&#xff0c;RocketMQ 以其高性能、高可靠性和强大的功能占据着重要的地位。而 Broker 作为 RocketMQ 的核心组件之一&#xff0c;其控制器的启动过程涉及到众多关键环节和复杂的逻辑。理解这个过程对于深入掌握 RocketMQ 的运行机制以及在实际应用中…

从0开始深度学习(22)——从全连接层到卷积

多层感知机在处理图像这种高维数据时&#xff0c;因为模型需要大量的数据来训练这么多参数&#xff0c;会导致巨大的计算成本&#xff0c;还会增加过拟合的风险&#xff0c;所以人们选择使用卷积神经网络 1 不变性 在计算机视觉和深度学习领域&#xff0c;特指模型对输入数据中…

MySQL8.0.40编译安装

近期MySQL发布了8.0.40版本&#xff0c;与之前的版本相比&#xff0c;部分依赖包发生了变化&#xff0c;因此重新编译一版&#xff0c;也便于大家参考。 1. 下载源码 选择对应的版本、选择源码、操作系统 如果没有登录或者没有MySQL官网账号&#xff0c;可以选择只下载 2. 进…