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

news2025/5/10 2:10:33

在这里插入图片描述

文章目录

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

        #endregion
        public 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>();
         //添加其他 viewModel

         return 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.coloradmin.cn/o/2228717.html

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

相关文章

【CSS】——基础入门常见操作

阿华代码&#xff0c;不是逆风&#xff0c;就是我疯 你们的点赞收藏是我前进最大的动力&#xff01;&#xff01; 希望本文内容能够帮助到你&#xff01;&#xff01; 目录 一&#xff1a;CSS引入 二&#xff1a;CSS对元素进行美化 1&#xff1a;style修饰 2&#xff1a;选…

Jmeter基础篇(19)JSR223预处理器

前言 JSR223预处理器是Apache JMeter中的一个组件&#xff0c;它允许用户使用任何支持Java Scripting API (JSR 223) 的脚本语言来执行预处理任务。这个功能非常强大&#xff0c;因为它让测试人员能够利用如Groovy、JavaScript&#xff08;Nashorn引擎&#xff09;、BeanShell…

Github 2024-10-24 Go开源项目日报 Top10

根据Github Trendings的统计,今日(2024-10-24统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Go项目10Solidity项目1Ollama: 本地大型语言模型设置与运行 创建周期:248 天开发语言:Go协议类型:MIT LicenseStar数量:42421 个Fork数量:…

从0到1,用Rust轻松制作电子书

我之前简单提到过用 Rust 做电子书&#xff0c;今天分享下如何用Rust做电子书。制作电子书其实用途广泛&#xff0c;不仅可以用于技术文档&#xff08;对技术人来说非常方便&#xff09;&#xff0c;也可以制作用户手册、笔记、教程等&#xff0c;还可以应用于文学创作。 如果…

私有化视频平台EasyCVR视频汇聚平台接入RTMP协议推流为何无法播放?

私有化视频平台EasyCVR视频汇聚平台兼容性强、支持灵活拓展&#xff0c;平台可提供视频远程监控、录像、存储与回放、视频转码、视频快照、告警、云台控制、语音对讲、平台级联等视频能力。 有用户反馈&#xff0c;项目现场使用RTMP协议接入EasyCVR平台&#xff0c;但是视频却不…

51单片机应用开发(进阶)---外部中断(按键+数码管显示0-F)

实现目标 1、巩固数码管、外部中断知识 2、具体实现&#xff1a;按键K4&#xff08;INT1&#xff09;每按一次&#xff0c;数码管从0依次递增显示至F&#xff0c;再按则循环显示。 一、共阳数码管 1.1 共阳数码管结构 1.2 共阳数码管码表 共阳不带小数点0-F段码为&#xff…

【简道云 -注册/登录安全分析报告】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造成亏损无底洞…

CSS 样式 box-sizing: border-box; 用于控制元素的盒模型如何计算宽度和高度

文章目录 box-sizing: border-box; 的含义默认盒模型 (content-box)border-box 盒模型 在微信小程序中的应用示例 在微信小程序中&#xff0c;CSS 样式 box-sizing: border-box; 用于控制元素的盒模型如何计算宽度和高度。具体来说&#xff0c; box-sizing: border-box; 会改…

设计模式基础概念(行为模式):责任链模式(Chain Of Responsibility)

概述 责任链模式是一种行为设计模式&#xff0c; 允许你将请求沿着处理者链进行发送。 收到请求后&#xff0c; 每个处理者均可对请求进行处理&#xff0c; 或将其传递给链上的下个处理者。 该模式建议你将这些处理者连成一条链。 链上的每个处理者都有一个成员变量来保存对于…

从入门到了解C++系列-----类与对象(中)

首言 这是我对于在学习类与对象时的一些思考与总结。主要去讲解C自主实现的默认构造函数。 1. 6大默认成员函数 1.1 是什么 默认的成员函数&#xff0c;是由c 编译器自动生成的。我们即使不定义&#xff0c;也可以调用。有默认构造函数、默认拷贝构造函数、默认析构函数、赋值重…

快速生成高质量提示词,Image to Prompt 更高效

抖知书老师推荐&#xff1a; 随着 AI 技术的不断发展&#xff0c;视觉信息与语言信息之间的转换变得越来越便捷。在如今的数字化生活中&#xff0c;图像与文字的交互需求愈发旺盛&#xff0c;很多人都希望能轻松将图像内容直接转化为文本描述。今天我们来推荐一款实用的 AI 工…

SCSI驱动与 UFS 驱动交互概况

SCSI子系统概况 SCSI&#xff08;Small Computer System Interface&#xff09;子系统是 Linux 中的一个模块化框架&#xff0c;用于提供与存储设备的通用接口。通过 SCSI 子系统&#xff0c;可以支持不同类型的存储协议&#xff08;如 UFS、SATA、SAS&#xff09;&#xff0c…

5. 数据库连接池实现

WebServer 类中的 sql_pool() 方法&#xff0c;用于初始化数据库连接池并设置用户数据。 void WebServer::sql_pool() {/* 初始化数据库连接池 */m_connPool connection_pool::GetInstance();m_connPool->init("localhost", m_user, m_passWord, m_databaseName,…

Unity BesHttp插件修改Error log的格式

实现代码 找到插件的 UnityOutput.cs 然后按照需求替换为下面的代码即可。如果提示 void ILogOutput.Flush() { } 接口不存在&#xff0c;删除这行代码即可。 using Best.HTTP.JSON.LitJson; using System; using System.Collections.Generic; using UnityEngine; using Syst…

Kubernetes实战——DevOps集成SpringBoot项目

目录 一、安装Gitlab 1、安装并配置Gitlab 1.1 、下载安装包 1.2、安装 1.3、修改配置文件 1.4、更新配置并重启 2、配置 2.1、修改密码 2.2、禁用注册功能 2.3、取消头像 2.4、修改中文配置 2.5、配置 webhook 3、卸载 二、安装镜像私服Harbor 1、下载安装包 2、…

【移动应用开发】访问网络

目录 一、运行截图 二、源代码 1. WebView的简单使用 ① activity_main.xml ② MainActivity.kt ③ AndroidManifest.xml 2. 使用OkHttp访问以下接口&#xff0c;获取Aspirin化合物的JSON格式数据 ① activity_okhttp.xml ② OKhttpActivity ③ 导入依赖 3. 使用GSO…

软件工程--需求分析与用例模型

面向对象分析(ObjectOrientedAnalysis&#xff0c;简称OOA) 分析和理解问题域&#xff0c;找出描述问题域所需的类和对象&#xff0c;分析它们的内部构成和外部关系&#xff0c;建立独立于实现的OOA模型&#xff0c;暂时忽略与系统实现有关的问题。 主要使用UML中的以下几种图…

Android中同步屏障(Sync Barrier)介绍

在 Android 中&#xff0c;“同步屏障”&#xff08;Sync Barrier&#xff09;是 MessageQueue 中的一种机制&#xff0c;允许系统临时忽略同步消息&#xff0c;以便优先处理异步消息。这在需要快速响应的任务&#xff08;如触摸事件和动画更新&#xff09;中尤为重要。 在 An…

MyBatis-Plus:简化 CRUD 操作的艺术

一、关于MyBatis-Plus 1.1 简介 MyBatis-Plus 是一个基于 MyBatis 的增强工具&#xff0c;它旨在简化 MyBatis 的使用&#xff0c;提高开发效率。 ​ ‍ ‍ ‍ ​ ‍ 关于Mybatis 简介 MyBatis 是一款流行的 Java 持久层框架&#xff0c;旨在简化 Java 应用程序与数…

ECharts饼图-圆角环形图,附视频讲解与代码下载

引言&#xff1a; 在数据可视化的世界里&#xff0c;ECharts凭借其丰富的图表类型和强大的配置能力&#xff0c;成为了众多开发者的首选。今天&#xff0c;我将带大家一起实现一个饼图图表&#xff0c;通过该图表我们可以直观地展示和分析数据。此外&#xff0c;我还将提供详…