WPF——动态排名图表实现

news2024/11/30 18:28:40

开发环境

VS2022

.NET 8.0

MVVM Toolkit 8.2.2

需求

开发中需要实现按照成绩动态指名,以展示当前的竞赛成绩的一个实时情况及变化。

即如下效果:

需求分析

按照接收到的信息,就是要将获取到的集合排序,并且要将排序前后的变化,要能在UI上动态的表示出来,以直观的显示排名的变化效果。

UI上的排名上升与下降的实现,本质就是当前显示控件位置的变化,最方便的方式肯定是在Canvas上设置它的Top位置了,然后再有一个从原位置到新位置过度动画,那么就好了。

按上述思路,首先想到的就是自定义控件,完全自定义控件有点麻烦,最后决定使用常用的集合控件 ItemsControl(其子控件也行,但仅用ListView尝试过)来进行实现。

代码实现

VM及Model:

    internal partial class MainWindowViewModel : ObservableRecipient
    {
        [ObservableProperty]
        ObservableCollection<Person> persons =
        [
            new Person() { Id = 1, Name = "张三", Age = 18, Gender = "男", Address = "北京", Grade = "一年级" ,Y=50,OldY=50,Score=40},
            new Person() { Id = 2, Name = "李四", Age = 19, Gender = "女", Address = "上海", Grade ="二年级",Y=100,OldY=100,Score=60},
            new Person() { Id = 3, Name = "王五", Age = 20, Gender = "男", Address = "广州", Grade = "三年级" ,Y=150,OldY=150,Score=90},
            ];

        Timer timer;
        public MainWindowViewModel()
        {
            timer = new Timer(OnTimer, null, 0,1000);
        }

        private void OnTimer(object? state)
        {
            Dispatcher.CurrentDispatcher.Invoke(() =>
            {
                Random random = new();
                var index = random.Next(0, 3);
                Persons[index].Score = random.Next(0, 100);
                var sorts = Persons.OrderBy(p => p.Score);
                int i = 0;
                foreach (var item in sorts)
                {
                    item.Id = ++i;
                    item.Y = i * 50;
                }
            });


        }
    }
    public partial class Person : ObservableObject
    {
        [ObservableProperty]
        private int id;
        [ObservableProperty]
        private string name;
        [ObservableProperty]
        private int age;
        [ObservableProperty]
        private string gender;
        [ObservableProperty]
        private string address;
        [ObservableProperty]
        private string grade;

        private int y;
        public int Y
        {
            get => y;
            set
            {
                if (y != value)
                {
                    OldY = y; //记录旧值
                    SetProperty(ref y, value);
                }

            }
        }
        [ObservableProperty]
        private int oldY;
        [ObservableProperty]
        private int score;
    }

Xaml中绑定如下,注意下述代码中的ZContentPresenter为自定义控件:

 <ItemsControl
     x:Name="myItemsControl"
     Margin="10"
     ItemsSource="{Binding Persons}">
     <ItemsControl.ItemsPanel>
         <ItemsPanelTemplate>
             <Canvas />
         </ItemsPanelTemplate>
     </ItemsControl.ItemsPanel>
     <ItemsControl.ItemTemplate>
         <DataTemplate>

             <control:ZContentPresenter
                 x:Name="presenter"
                 Content="{Binding}"
                 Top="{Binding Y}">

                 <ContentPresenter.ContentTemplate>
                     <DataTemplate>
                         <StackPanel Orientation="Horizontal">
                             <TextBox Text="{Binding Id}" />
                             <TextBox Text="{Binding Name}" />
                             <TextBox Text="{Binding Age}" />
                             <TextBox Text="{Binding Y}" />

                         </StackPanel>
                     </DataTemplate>
                 </ContentPresenter.ContentTemplate>
             </control:ZContentPresenter>
         </DataTemplate>
     </ItemsControl.ItemTemplate>
 </ItemsControl>

对于 UI中的ZContentPresenter为自定义控件,其代码如下:

    public class ZContentPresenter : ContentPresenter
    {
        public ZContentPresenter()
        {

        }



        public int Top
        {
            get { return (int)GetValue(TopProperty); }
            set { SetValue(TopProperty, value); }
        }

        public static readonly DependencyProperty TopProperty =
            DependencyProperty.Register("Top", typeof(int), typeof(ZContentPresenter), new PropertyMetadata(0, TopChanged));

        private static void TopChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is ZContentPresenter control)
            {
                var oldValue = (int)e.OldValue;
                var newValue = (int)e.NewValue;

                var parent=(ContentPresenter)control.VisualParent;


                StartAnimation((double)(oldValue), (double)(newValue), parent);
            }

        }


        private static void StartAnimation(double from, double to, FrameworkElement element)
        {
            Storyboard storyboard = new();
            DoubleAnimation animation = new()
            {
                From = from,
                To = to,
                Duration = TimeSpan.FromSeconds(0.5),
                AutoReverse = false,
                RepeatBehavior = new RepeatBehavior(1)
            };
            Storyboard.SetTarget(animation, element);
            Storyboard.SetTargetProperty(animation, new PropertyPath(Canvas.TopProperty));
            //Storyboard.SetTargetProperty(animation, new PropertyPath("(Canvas.Top)"));
            storyboard.Children.Add(animation);
            storyboard.Begin();
/*            storyboard.Completed += (sender, e) =>
            {
                storyboard.Stop();
            };*/
        }

    }

那为什么不直接在ItemContainerStyle中直接使用样式与Trigger中设置动画来实现呢?

这涉及到Trigger不能侦听Y值的实时变化,另外还有一个问题就是在Animation中不能绑定From与To值,若From或To采用绑定,会导致出现报错:无法冻结此 Storyboard 时间线树供跨线程使用。

注意事项

1. 自定义控件中的

 Storyboard.SetTargetProperty(animation, new PropertyPath("(Canvas.Top)"));

这种写法与

Storyboard.SetTargetProperty(animation, new PropertyPath(Canvas.TopProperty));

是等价的,并且不能将括号去掉。

2. 另外就是一定要绑定Top属性为你指定的离Canvas顶部的距离,本例中以Y值进行绑定

3. 虽然已经将ItemsControl中的DataTemplate的ContentPresneter改用了ZContentPresneter(即使将ContentPresenter.ContentTemplate也改为了ZContentPresneter.ContentTemplate,也没有效果),但若要改写ItemsControl的ItemContainerStyle,它的TargetType仍还是只能为ContentPresenter,它的默认容器就是ContentPresenter,暂未发现如何将默认的容器改为ZContentPresneter。也就是说目前还只能如下设置:

            <ItemsControl.ItemContainerStyle>
                <Style TargetType="ContentPresenter">
                    <Setter Property="Canvas.Left" Value="0" />
                    <Setter Property="Canvas.Top" Value="{Binding OldY}" />
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding Y, UpdateSourceTrigger=PropertyChanged}" Value="50">
                            <DataTrigger.EnterActions>
                                <BeginStoryboard>
                                    <Storyboard>
                                        <DoubleAnimation
                                            AutoReverse="false"
                                            RepeatBehavior="1"
                                            Storyboard.TargetProperty="(Canvas.Top)"
                                            From="5"
                                            To="20"
                                            Duration="0:0:1" />
                                    </Storyboard>
                                </BeginStoryboard>
                            </DataTrigger.EnterActions>
                        </DataTrigger>
                    </Style.Triggers>

                </Style>
            </ItemsControl.ItemContainerStyle>

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

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

相关文章

什么是UDP?

UDP是工作在OSI&#xff08;开放系统互连&#xff0c;Open Systems Interconnection&#xff09;模型中传输层的协议。它使用IP作为底层协议&#xff0c;是为应用程序提供一种以最少的协议机制向其他程序发送消息的协议。其主要特点是无连接&#xff0c;不保证可靠传输和面向报…

汽车线束智能制造:MES系统与工艺深度融合的革新之路

万界星空科技汽车线束工厂MES系统解决方案是针对线束制造行业特定需求而设计的一套集成化管理系统&#xff0c;旨在提高生产效率、优化资源配置、确保产品质量并增强企业竞争力。 一、汽车线束制造工艺 汽车线束&#xff0c;作为连接汽车各个电子部件的桥梁&#xff0c;承载着…

论文:NeRF on the go:Exploiting Uncertainty for Distractor-free NeRFs in the Wild

随时随地使用NeRF。本文的目标是利用在野外随意捕捉的图像序列或视频作为输入&#xff0c;训练一个用于静态场景的NeRF&#xff0c;并有效地移除场景中的所有动态元素&#xff08;如汽车、电车、行人等&#xff09;&#xff0c;即干扰物。与现有的方法如NeRF-W [27]和RobustNeR…

python依赖包安装失败的解决办法(适用于conda安装)

版权声明&#xff1a;本文为博主原创文章&#xff0c;如需转载请贴上原博文链接&#xff1a;python依赖包安装失败的解决办法&#xff08;适用于conda安装&#xff09;-CSDN博客 前言&#xff1a;这个问题之前一直困扰着我&#xff0c;因为最近要升级Anaconda-Navigator&#x…

代码随想录算法训练营第十八天(二叉树 六)

力扣题部分: 530.二叉搜索树的最小绝对差 题目链接:. - 力扣&#xff08;LeetCode&#xff09; 题面: 给你一个二叉搜索树的根节点 root &#xff0c;返回 树中任意两不同节点值之间的最小差值 。 差值是一个正数&#xff0c;其数值等于两值之差的绝对值。 思路: 写关于二…

第T11周:优化器对比实验

>- **&#x1f368; 本文为[&#x1f517;365天深度学习训练营]中的学习记录博客** >- **&#x1f356; 原作者&#xff1a;[K同学啊]** 本次主要是探究不同优化器、以及不同参数配置对模型的影响 &#x1f680;我的环境&#xff1a; 语言环境&#xff1a;Python3.11.…

CSS 布局

CSS 页面布局技术允许我们拾取网页中的元素&#xff0c;并且控制它们相对正常布局流、周边元素、父容器或者主视口/窗口的位置。布局有一下几种 正常布局流display属性弹性盒子网格浮动定位CSS 表格布局多列布局 每种布局都有它们的用途&#xff0c;各有优缺点&#xff0c;相…

CSS伪类选择器和伪元素

伪类&#xff08;Pseudo-classes&#xff09; 伪类用于定义元素的特殊状态。它们被添加到选择器中以指定元素在其生命周期的特定状态下的样式。伪类不创建新的文档内容&#xff0c;也不创建新的文档树中的元素。相反&#xff0c;它们提供了一种方法来根据元素的状态来应用样式…

统信UOS系统连接打印机操作步骤

系统版本 操作步骤 首先点击开始菜单 搜索框输入打印&#xff0c;点击打印管理器 点击图下所示的号 按照图下所示&#xff0c;手动查找->输入打印机的ip地址->点击查找 等到如图下所示&#xff0c;出现打印机的时候&#xff0c;选择打印机&#xff0c;然后选择驱动&…

嵌入式AI快速入门课程-K510篇 (第三篇 环境搭建及开发板操作)

第三篇 环境搭建及开发板操作 文章目录 第三篇 环境搭建及开发板操作1.配置VMware使用桥接网卡1.1 vmware设置1.2 虚拟网络编辑器设置 2.安装软件2.2 安装 Windows 软件2.3 使用MobaXterm远程登录Ubuntu2.4 使用FileZilla在Windows和Ubuntu之间传文件2.5编程示例&#xff1a;Ub…

迎接“云+AI”智算时代!生态案例分论坛议程一览 | 2024 龙蜥大会

2024 龙蜥操作系统大会由中国计算机学会开源发展委员会、中关村科学城委员会、海淀区委网信办、中国开源软件推进联盟指导&#xff0c;龙蜥社区主办&#xff0c;阿里云、中兴通讯、Intel、浪潮信息、Arm、中科方德等 24 家理事单位共同承办&#xff0c;主题为“进化重构赴未来”…

海南云亿商务咨询有限公司助力抖音商家破浪前行

在当下这个短视频与直播电商风起云涌的时代&#xff0c;抖音作为头部平台&#xff0c;正以其庞大的用户基数和强大的算法推荐机制&#xff0c;成为众多品牌与商家竞相追逐的新蓝海。而在这片波澜壮阔的海洋中&#xff0c;海南云亿商务咨询有限公司如同一艘稳健的航船&#xff0…

软件测试 —— JMeter 参数化4种方式!

一、JMeter参数化简介 1.JMeter参数化的概念 当使用JMeter进行测试时&#xff0c;测试数据的准备是一项重要的工作。若要求每次迭代的数据不一样时&#xff0c;则需进行参数化&#xff0c;然后从参数化的文件中来读取测试数据。 参数化&#xff1a;是自动化测试脚本的一种常…

【Prettier】代码格式化工具Prettier的使用和配置介绍

前言 前段时间&#xff0c;因为项目的prettier的配置和eslint格式检查有些冲突&#xff0c;在其prettier官网和百度了一些配置相关的资料&#xff0c;在此做一些总结&#xff0c;以备不时之需。 Prettier官网 Prettier Prettier 是一种前端代码格式化工具&#xff0c;支持ja…

从ESG尽职调查、ESG立法与ESG诉讼谈ESG营销(01)

哈佛大学2024年中回顾全球ESG发展近况 作者&#xff1a;哈佛大学 编辑&#xff1a;数字化营销工兵 2024年上半年&#xff0c;环境、社会和治理&#xff08;ESG&#xff09;问题以及对方法的不同意见继续成为全球头条新闻。今年年初&#xff0c;公司及其利益相关者在ESG的支持…

AppenTalk | 不止于赛场,巴黎奥运会上的中国AI科技

当地时间8月11日&#xff0c;第33届夏季奥林匹克运动会在巴黎法兰西体育场落下帷幕。本届奥运会&#xff0c;中国体育代表团收获令人振奋的40金27银24铜总计91枚奖牌&#xff0c;其中金牌数更是创下了境外参加奥运会的最佳成绩。 在中国健儿闪耀奥运赛场时&#xff0c;中国AI科…

Transformer系列-4丨DETR模型和代码解析

1 前言 往期的文章中&#xff0c;笔者从网络结构和代码实现角度较为深入地和大家解析了Transformer模型、Vision Transformer模型&#xff08;ViT&#xff09;以及BERT模型&#xff0c;其具体的链接如下&#xff1a; 基础Transformer解析 ViT模型与代码解析 BERT模型与代码解…

嵌入式AI快速入门课程-K510篇 (第七篇 系统BSP开发)

第七篇 系统BSP开发 文章目录 第七篇 系统BSP开发1. 嵌入式Linux系统介绍嵌入式Linux系统组成产品形态嵌入式芯片启动流程Linux系统Linux系统框架嵌入式编译环境 2.嵌入式Linux开发准备手册文档开发工具配套硬件工程源码 3.嵌入式Linux开发组成概述编译工具链什么是工具链什么是…

[Linux#43][线程] 死锁 | 同步 | 基于 BlockingQueue 的生产者消费者模型

目录 1. 死锁 解决死锁问题 2. 同步 2.1 条件变量函数 cond 2.2 条件变量的使用&#xff1a; 3.CP 问题--理论 4. 基于 BlockingQueue 的生产者消费者模型 1. 基本概念 2.BlockQueue.hpp 基本设置&#xff1a; 生产关系控制&#xff1a; 消费关系的控制 ⭕思考点 …

公开整理-全国各省AI算力数据集(2000-2024年)

数据来源&#xff1a;本数据来源于&#xff0c;根据显卡HS编码筛选统计后获得时间跨度&#xff1a;2000-2024年数据范围&#xff1a;省级层面数据指标&#xff1a; 由于未发布2015至2016年的数据&#xff0c;因此该年份数据存在缺失。下表仅展示了部分指标及数据 年份 省份…